Skip to content

Commit 7804c52

Browse files
committed
Integrate the temporary directory into layer deletion
This enables deferred deletion of physical files outside of global locks, improving performance and reducing lock contention. Signed-off-by: Jan Rodák <[email protected]>
1 parent 5879e4e commit 7804c52

File tree

10 files changed

+205
-19
lines changed

10 files changed

+205
-19
lines changed

drivers/aufs/aufs.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
"time"
3737

3838
graphdriver "github.com/containers/storage/drivers"
39+
"github.com/containers/storage/internal/tempdir"
3940
"github.com/containers/storage/pkg/archive"
4041
"github.com/containers/storage/pkg/chrootarchive"
4142
"github.com/containers/storage/pkg/directory"
@@ -781,3 +782,9 @@ func (a *Driver) SupportsShifting(uidmap, gidmap []idtools.IDMap) bool {
781782
func (a *Driver) Dedup(req graphdriver.DedupArgs) (graphdriver.DedupResult, error) {
782783
return graphdriver.DedupResult{}, nil
783784
}
785+
786+
// DeferredRemove is not implemented.
787+
// It calls Remove directly.
788+
func (a *Driver) DeferredRemove(id string) (tempdir.CleanupTempDirFunc, error) {
789+
return nil, a.Remove(id)
790+
}

drivers/btrfs/btrfs.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"unsafe"
3131

3232
graphdriver "github.com/containers/storage/drivers"
33+
"github.com/containers/storage/internal/tempdir"
3334
"github.com/containers/storage/pkg/directory"
3435
"github.com/containers/storage/pkg/fileutils"
3536
"github.com/containers/storage/pkg/idtools"
@@ -678,3 +679,9 @@ func (d *Driver) AdditionalImageStores() []string {
678679
func (d *Driver) Dedup(req graphdriver.DedupArgs) (graphdriver.DedupResult, error) {
679680
return graphdriver.DedupResult{}, nil
680681
}
682+
683+
// DeferredRemove is not implemented.
684+
// It calls Remove directly.
685+
func (d *Driver) DeferredRemove(id string) (tempdir.CleanupTempDirFunc, error) {
686+
return nil, d.Remove(id)
687+
}

drivers/driver.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"strings"
1010

1111
"github.com/containers/storage/internal/dedup"
12+
"github.com/containers/storage/internal/tempdir"
1213
"github.com/containers/storage/pkg/archive"
1314
"github.com/containers/storage/pkg/directory"
1415
"github.com/containers/storage/pkg/fileutils"
@@ -124,6 +125,10 @@ type ProtoDriver interface {
124125
CreateFromTemplate(id, template string, templateIDMappings *idtools.IDMappings, parent string, parentIDMappings *idtools.IDMappings, opts *CreateOpts, readWrite bool) error
125126
// Remove attempts to remove the filesystem layer with this id.
126127
Remove(id string) error
128+
// DeferredRemove is used to remove the filesystem layer with this id
129+
// at a later time, e.g., when the last reference to the layer is removed.
130+
// It returns a function that can be used to remove the layer later.
131+
DeferredRemove(id string) (tempdir.CleanupTempDirFunc, error)
127132
// Get returns the mountpoint for the layered filesystem referred
128133
// to by this id. You can optionally specify a mountLabel or "".
129134
// Optionally it gets the mappings used to create the layer.

drivers/overlay/overlay.go

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/containers/storage/drivers/quota"
2525
"github.com/containers/storage/internal/dedup"
2626
"github.com/containers/storage/internal/staging_lockfile"
27+
"github.com/containers/storage/internal/tempdir"
2728
"github.com/containers/storage/pkg/archive"
2829
"github.com/containers/storage/pkg/chrootarchive"
2930
"github.com/containers/storage/pkg/directory"
@@ -1327,6 +1328,35 @@ func (d *Driver) Remove(id string) error {
13271328
return nil
13281329
}
13291330

1331+
func (d *Driver) DeferredRemove(id string) (tempdir.CleanupTempDirFunc, error) {
1332+
t, err := tempdir.NewTempDir(filepath.Join(d.home, stagingDir))
1333+
if err != nil {
1334+
return nil, err
1335+
}
1336+
1337+
dir := d.dir(id)
1338+
lid, err := os.ReadFile(path.Join(dir, "link"))
1339+
if err == nil {
1340+
if err := t.Add(path.Join(d.home, linkDir, string(lid))); err != nil {
1341+
logrus.Debugf("Failed to Add to stage Directory link: %v", err)
1342+
}
1343+
}
1344+
1345+
d.releaseAdditionalLayerByID(id)
1346+
1347+
if err := t.Add(dir); err != nil {
1348+
return t.Cleanup, fmt.Errorf("failed to add to stage directory: %w", err)
1349+
}
1350+
1351+
if d.quotaCtl != nil {
1352+
d.quotaCtl.ClearQuota(dir)
1353+
if d.imageStore != "" {
1354+
d.quotaCtl.ClearQuota(d.imageStore)
1355+
}
1356+
}
1357+
return t.Cleanup, nil
1358+
}
1359+
13301360
// recreateSymlinks goes through the driver's home directory and checks if the diff directory
13311361
// under each layer has a symlink created for it under the linkDir. If the symlink does not
13321362
// exist, it creates them
@@ -1353,8 +1383,8 @@ func (d *Driver) recreateSymlinks() error {
13531383
// Check that for each layer, there's a link in "l" with the name in
13541384
// the layer's "link" file that points to the layer's "diff" directory.
13551385
for _, dir := range dirs {
1356-
// Skip over the linkDir and anything that is not a directory
1357-
if dir.Name() == linkDir || !dir.IsDir() {
1386+
// Skip over the linkDir and anything that is not a directory or tempDir
1387+
if dir.Name() == linkDir || !dir.IsDir() || dir.Name() == stagingDir {
13581388
continue
13591389
}
13601390
// Read the "link" file under each layer to get the name of the symlink

drivers/vfs/driver.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
graphdriver "github.com/containers/storage/drivers"
1313
"github.com/containers/storage/internal/dedup"
14+
"github.com/containers/storage/internal/tempdir"
1415
"github.com/containers/storage/pkg/archive"
1516
"github.com/containers/storage/pkg/directory"
1617
"github.com/containers/storage/pkg/fileutils"
@@ -244,6 +245,18 @@ func (d *Driver) Remove(id string) error {
244245
return system.EnsureRemoveAll(d.dir(id))
245246
}
246247

248+
func (d *Driver) DeferredRemove(id string) (tempdir.CleanupTempDirFunc, error) {
249+
pathToRemove := d.dir(id)
250+
t, err := tempdir.NewTempDir(filepath.Dir(pathToRemove))
251+
if err != nil {
252+
return nil, err
253+
}
254+
if err := t.Add(pathToRemove); err != nil {
255+
return nil, err
256+
}
257+
return t.Cleanup, nil
258+
}
259+
247260
// Get returns the directory for the given id.
248261
func (d *Driver) Get(id string, options graphdriver.MountOpts) (_ string, retErr error) {
249262
dir := d.dir(id)

drivers/windows/windows.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/Microsoft/go-winio/backuptar"
2525
"github.com/Microsoft/hcsshim"
2626
graphdriver "github.com/containers/storage/drivers"
27+
"github.com/containers/storage/internal/tempdir"
2728
"github.com/containers/storage/pkg/archive"
2829
"github.com/containers/storage/pkg/directory"
2930
"github.com/containers/storage/pkg/fileutils"
@@ -1014,3 +1015,9 @@ func parseStorageOpt(storageOpt map[string]string) (*storageOptions, error) {
10141015
}
10151016
return &options, nil
10161017
}
1018+
1019+
// DeferredRemove is not implemented.
1020+
// It calls Remove directly.
1021+
func (d *Driver) DeferredRemove(id string) (tempdir.CleanupTempDirFunc, error) {
1022+
return nil, d.Remove(id)
1023+
}

drivers/zfs/zfs.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"time"
1414

1515
graphdriver "github.com/containers/storage/drivers"
16+
"github.com/containers/storage/internal/tempdir"
1617
"github.com/containers/storage/pkg/directory"
1718
"github.com/containers/storage/pkg/idtools"
1819
"github.com/containers/storage/pkg/mount"
@@ -406,6 +407,12 @@ func (d *Driver) Remove(id string) error {
406407
return nil
407408
}
408409

410+
// DeferredRemove is not implemented.
411+
// It calls Remove directly.
412+
func (d *Driver) DeferredRemove(id string) (tempdir.CleanupTempDirFunc, error) {
413+
return nil, d.Remove(id)
414+
}
415+
409416
// Get returns the mountpoint for the given id after creating the target directories if necessary.
410417
func (d *Driver) Get(id string, options graphdriver.MountOpts) (_ string, retErr error) {
411418
mountpoint := d.mountPath(id)

layers.go

Lines changed: 56 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"time"
1919

2020
drivers "github.com/containers/storage/drivers"
21+
"github.com/containers/storage/internal/tempdir"
2122
"github.com/containers/storage/pkg/archive"
2223
"github.com/containers/storage/pkg/idtools"
2324
"github.com/containers/storage/pkg/ioutils"
@@ -43,6 +44,7 @@ const (
4344
// in readers (which, for implementation reasons, gives other writers the opportunity to create more inconsistent state)
4445
// until we just give up.
4546
maxLayerStoreCleanupIterations = 3
47+
tempDirPath = "tmp"
4648
)
4749

4850
type layerLocations uint8
@@ -293,6 +295,9 @@ type rwLayerStore interface {
293295
// Delete deletes a layer with the specified name or ID.
294296
Delete(id string) error
295297

298+
// deferredDelete deletes a layer with the specified name or ID.
299+
deferredDelete(id string) ([]tempdir.CleanupTempDirFunc, error)
300+
296301
// Wipe deletes all layers.
297302
Wipe() error
298303

@@ -794,6 +799,12 @@ func (r *layerStore) load(lockedForWriting bool) (bool, error) {
794799
layers := []*Layer{}
795800
ids := make(map[string]*Layer)
796801

802+
if r.lockfile.IsReadWrite() {
803+
if err := tempdir.RecoverStaleDirs(filepath.Join(r.layerdir, tempDirPath)); err != nil {
804+
return false, err
805+
}
806+
}
807+
797808
for locationIndex := range numLayerLocationIndex {
798809
location := layerLocationFromIndex(locationIndex)
799810
rpath := r.jsonPath[locationIndex]
@@ -935,7 +946,12 @@ func (r *layerStore) load(lockedForWriting bool) (bool, error) {
935946
// Now actually delete the layers
936947
for _, layer := range layersToDelete {
937948
logrus.Warnf("Found incomplete layer %q, deleting it", layer.ID)
938-
err := r.deleteInternal(layer.ID)
949+
cleanFunctions, err := r.deleteWhileHoldingLock(layer.ID)
950+
defer func() {
951+
if err := tempdir.CleanupTemporaryDirectories(cleanFunctions...); err != nil {
952+
logrus.Errorf("Error cleaning up temporary directories: %v", err)
953+
}
954+
}()
939955
if err != nil {
940956
// Don't return the error immediately, because deleteInternal does not saveLayers();
941957
// Even if deleting one incomplete layer fails, call saveLayers() so that other possible successfully
@@ -1920,13 +1936,15 @@ func layerHasIncompleteFlag(layer *Layer) bool {
19201936
}
19211937

19221938
// Requires startWriting.
1923-
func (r *layerStore) deleteInternal(id string) error {
1939+
// Caller MUST run all returned cleanup functions after this. Ideally outside of
1940+
// the startWriting.
1941+
func (r *layerStore) deleteWhileHoldingLock(id string) ([]tempdir.CleanupTempDirFunc, error) {
19241942
if !r.lockfile.IsReadWrite() {
1925-
return fmt.Errorf("not allowed to delete layers at %q: %w", r.layerdir, ErrStoreIsReadOnly)
1943+
return nil, fmt.Errorf("not allowed to delete layers at %q: %w", r.layerdir, ErrStoreIsReadOnly)
19261944
}
19271945
layer, ok := r.lookup(id)
19281946
if !ok {
1929-
return ErrLayerUnknown
1947+
return nil, ErrLayerUnknown
19301948
}
19311949
// Ensure that if we are interrupted, the layer will be cleaned up.
19321950
if !layerHasIncompleteFlag(layer) {
@@ -1935,16 +1953,29 @@ func (r *layerStore) deleteInternal(id string) error {
19351953
}
19361954
layer.Flags[incompleteFlag] = true
19371955
if err := r.saveFor(layer); err != nil {
1938-
return err
1956+
return nil, err
19391957
}
19401958
}
1959+
tempDirectory, err := tempdir.NewTempDir(filepath.Join(r.layerdir, tempDirPath))
1960+
if err != nil {
1961+
return nil, err
1962+
}
19411963
// We never unset incompleteFlag; below, we remove the entire object from r.layers.
19421964
id = layer.ID
1943-
if err := r.driver.Remove(id); err != nil && !errors.Is(err, os.ErrNotExist) {
1944-
return err
1965+
cleanFunctions := []tempdir.CleanupTempDirFunc{}
1966+
cleanFunc, err := r.driver.DeferredRemove(id)
1967+
cleanFunctions = append(cleanFunctions, cleanFunc)
1968+
if err != nil && !errors.Is(err, os.ErrNotExist) {
1969+
return cleanFunctions, err
1970+
}
1971+
1972+
cleanFunctions = append(cleanFunctions, tempDirectory.Cleanup)
1973+
if err := tempDirectory.Add(r.tspath(id)); err != nil && !errors.Is(err, os.ErrNotExist) {
1974+
return cleanFunctions, err
1975+
}
1976+
if err := tempDirectory.Add(r.datadir(id)); err != nil && !errors.Is(err, os.ErrNotExist) {
1977+
return cleanFunctions, err
19451978
}
1946-
os.Remove(r.tspath(id))
1947-
os.RemoveAll(r.datadir(id))
19481979
delete(r.byid, id)
19491980
for _, name := range layer.Names {
19501981
delete(r.byname, name)
@@ -1968,7 +1999,7 @@ func (r *layerStore) deleteInternal(id string) error {
19681999
}) {
19692000
selinux.ReleaseLabel(mountLabel)
19702001
}
1971-
return nil
2002+
return cleanFunctions, nil
19722003
}
19732004

19742005
// Requires startWriting.
@@ -1989,9 +2020,18 @@ func (r *layerStore) deleteInDigestMap(id string) {
19892020

19902021
// Requires startWriting.
19912022
func (r *layerStore) Delete(id string) error {
2023+
cleanupFunctions, deferErr := r.deferredDelete(id)
2024+
cleanupErr := tempdir.CleanupTemporaryDirectories(cleanupFunctions...)
2025+
return errors.Join(deferErr, cleanupErr)
2026+
}
2027+
2028+
// Requires startWriting.
2029+
// Caller MUST run all returned cleanup functions after this. Ideally outside of
2030+
// the startWriting.
2031+
func (r *layerStore) deferredDelete(id string) ([]tempdir.CleanupTempDirFunc, error) {
19922032
layer, ok := r.lookup(id)
19932033
if !ok {
1994-
return ErrLayerUnknown
2034+
return nil, ErrLayerUnknown
19952035
}
19962036
id = layer.ID
19972037
// The layer may already have been explicitly unmounted, but if not, we
@@ -2003,13 +2043,14 @@ func (r *layerStore) Delete(id string) error {
20032043
break
20042044
}
20052045
if err != nil {
2006-
return err
2046+
return nil, err
20072047
}
20082048
}
2009-
if err := r.deleteInternal(id); err != nil {
2010-
return err
2049+
cleanFunctions, err := r.deleteWhileHoldingLock(id)
2050+
if err != nil {
2051+
return cleanFunctions, err
20112052
}
2012-
return r.saveFor(layer)
2053+
return cleanFunctions, r.saveFor(layer)
20132054
}
20142055

20152056
// Requires startReading or startWriting.

store.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222

2323
drivers "github.com/containers/storage/drivers"
2424
"github.com/containers/storage/internal/dedup"
25+
"github.com/containers/storage/internal/tempdir"
2526
"github.com/containers/storage/pkg/archive"
2627
"github.com/containers/storage/pkg/directory"
2728
"github.com/containers/storage/pkg/idtools"
@@ -2540,7 +2541,13 @@ func (s *store) Lookup(name string) (string, error) {
25402541
return "", ErrLayerUnknown
25412542
}
25422543

2543-
func (s *store) DeleteLayer(id string) error {
2544+
func (s *store) DeleteLayer(id string) (err error) {
2545+
cleanupFunctions := []tempdir.CleanupTempDirFunc{}
2546+
defer func() {
2547+
if cleanupErr := tempdir.CleanupTemporaryDirectories(cleanupFunctions...); cleanupErr != nil {
2548+
err = errors.Join(cleanupErr, err)
2549+
}
2550+
}()
25442551
return s.writeToAllStores(func(rlstore rwLayerStore) error {
25452552
if rlstore.Exists(id) {
25462553
if l, err := rlstore.Get(id); err != nil {
@@ -2574,7 +2581,9 @@ func (s *store) DeleteLayer(id string) error {
25742581
return fmt.Errorf("layer %v used by container %v: %w", id, container.ID, ErrLayerUsedByContainer)
25752582
}
25762583
}
2577-
if err := rlstore.Delete(id); err != nil {
2584+
cf, err := rlstore.deferredDelete(id)
2585+
cleanupFunctions = append(cleanupFunctions, cf...)
2586+
if err != nil {
25782587
return fmt.Errorf("delete layer %v: %w", id, err)
25792588
}
25802589

0 commit comments

Comments
 (0)