@@ -85,6 +85,7 @@ type Registry struct {
8585}
8686
8787type BootConfig struct {
88+ AsyncRemove bool `json:"asyncRemove"`
8889 Address string `json:"address"`
8990 Root string `json:"root"`
9091 LogLevel string `json:"verbose"`
@@ -102,6 +103,7 @@ type BootConfig struct {
102103
103104func DefaultBootConfig () * BootConfig {
104105 return & BootConfig {
106+ AsyncRemove : false ,
105107 LogLevel : "info" ,
106108 RwMode : "overlayfs" ,
107109 LogReportCaller : false ,
@@ -142,6 +144,15 @@ var defaultConfig = SnapshotterConfig{
142144// Opt is an option to configure the snapshotter
143145type Opt func (config * SnapshotterConfig ) error
144146
147+ // AsynchronousRemove defers removal of filesystem content until
148+ // the Cleanup method is called. Removals will make the snapshot
149+ // referred to by the key unavailable and make the key immediately
150+ // available for re-use.
151+ func AsynchronousRemove (config * BootConfig ) error {
152+ config .AsyncRemove = true
153+ return nil
154+ }
155+
145156// snapshotter is implementation of github.com/containerd/containerd/snapshots.Snapshotter.
146157//
147158// It is a snapshotter plugin. The layout of root dir is organized:
@@ -189,6 +200,7 @@ type snapshotter struct {
189200 tenant int
190201 locker * locker.Locker
191202 turboFsType []string
203+ asyncRemove bool
192204
193205 quotaDriver * diskquota.PrjQuotaDriver
194206 quotaSize string
@@ -256,6 +268,7 @@ func NewSnapshotter(bootConfig *BootConfig, opts ...Opt) (snapshots.Snapshotter,
256268 quotaDriver : & diskquota.PrjQuotaDriver {
257269 QuotaIDs : make (map [uint32 ]struct {}),
258270 },
271+ asyncRemove : bootConfig .AsyncRemove ,
259272 }, nil
260273}
261274
@@ -910,6 +923,63 @@ func (o *snapshotter) commit(ctx context.Context, name, key string, opts ...snap
910923 return id , info , nil
911924}
912925
926+ func (o * snapshotter ) cleanupDirectories (ctx context.Context ) ([]string , error ) {
927+ // Get a write transaction to ensure no other write transaction can be entered
928+ // while the cleanup is scanning.
929+ ctx , t , err := o .ms .TransactionContext (ctx , true )
930+ if err != nil {
931+ return nil , err
932+ }
933+
934+ defer t .Rollback ()
935+ return o .getCleanupDirectories (ctx , t )
936+ }
937+ func (o * snapshotter ) getCleanupDirectories (ctx context.Context , t storage.Transactor ) ([]string , error ) {
938+ ids , err := storage .IDMap (ctx )
939+ if err != nil {
940+ return nil , err
941+ }
942+
943+ snapshotDir := filepath .Join (o .root , "snapshots" )
944+ fd , err := os .Open (snapshotDir )
945+ if err != nil {
946+ return nil , err
947+ }
948+ defer fd .Close ()
949+
950+ dirs , err := fd .Readdirnames (0 )
951+ if err != nil {
952+ return nil , err
953+ }
954+
955+ cleanup := []string {}
956+ for _ , d := range dirs {
957+ if _ , ok := ids [d ]; ok {
958+ continue
959+ }
960+
961+ cleanup = append (cleanup , filepath .Join (snapshotDir , d ))
962+ }
963+
964+ return cleanup , nil
965+ }
966+
967+ // Cleanup cleans up disk resources from removed or abandoned snapshots// Cleanup cleans up disk resources from removed or abandoned snapshots
968+ func (o * snapshotter ) Cleanup (ctx context.Context ) error {
969+ cleanup , err := o .cleanupDirectories (ctx )
970+ if err != nil {
971+ return err
972+ }
973+
974+ for _ , dir := range cleanup {
975+ if err := os .RemoveAll (dir ); err != nil {
976+ log .G (ctx ).WithError (err ).WithField ("path" , dir ).Warn ("failed to remove directory" )
977+ }
978+ }
979+
980+ return nil
981+ }
982+
913983// Remove abandons the snapshot identified by key. The snapshot will
914984// immediately become unavailable and unrecoverable.
915985func (o * snapshotter ) Remove (ctx context.Context , key string ) (err error ) {
@@ -988,9 +1058,23 @@ func (o *snapshotter) Remove(ctx context.Context, key string) (err error) {
9881058 if err != nil {
9891059 return errors .Wrap (err , "failed to remove" )
9901060 }
991-
992- if err := os .RemoveAll (o .snPath (id )); err != nil && ! os .IsNotExist (err ) {
993- return err
1061+ if ! o .asyncRemove {
1062+ var removals []string
1063+ removals , err = o .getCleanupDirectories (ctx , t )
1064+ if err != nil {
1065+ return errors .Wrap (err , "unable to get directories for removal" )
1066+ }
1067+ defer func () {
1068+ if err == nil {
1069+ for _ , dir := range removals {
1070+ if err := os .RemoveAll (dir ); err != nil {
1071+ log .G (ctx ).WithError (err ).WithField ("path" , dir ).Warn ("failed to remove directory" )
1072+ }
1073+ }
1074+ }
1075+ }()
1076+ } else {
1077+ log .G (ctx ).Info ("asyncRemove enabled, remove snapshots in Cleanup() method." )
9941078 }
9951079
9961080 rollback = false
0 commit comments