Add overlay options for making cleanup asynchronous

Allow configuring the overlay snapshotter to synchronously
or asynchronously do cleanup. When the driver is integrated
into a garbage collection system, the asynchronous cleanup
can reduce the time of removal and allow the longer disk
cleanup to be handled without locking the snapshotter.

Signed-off-by: Derek McGowan <derek@mcgstyle.net>
This commit is contained in:
Derek McGowan 2018-01-24 13:38:11 -08:00
parent 14d402e289
commit 1fd2b5783a
No known key found for this signature in database
GPG Key ID: F58C5D0A4405ACDB

View File

@ -28,20 +28,45 @@ func init() {
InitFn: func(ic *plugin.InitContext) (interface{}, error) { InitFn: func(ic *plugin.InitContext) (interface{}, error) {
ic.Meta.Platforms = append(ic.Meta.Platforms, platforms.DefaultSpec()) ic.Meta.Platforms = append(ic.Meta.Platforms, platforms.DefaultSpec())
ic.Meta.Exports["root"] = ic.Root ic.Meta.Exports["root"] = ic.Root
return NewSnapshotter(ic.Root) return NewSnapshotter(ic.Root, AsynchronousRemove)
}, },
}) })
} }
// SnapshotterConfig is used to configure the overlay snapshotter instance
type SnapshotterConfig struct {
asyncRemove bool
}
// Opt is an option to configure the overlay snapshotter
type Opt func(config *SnapshotterConfig) error
// AsynchronousRemove defers removal of filesystem content until
// the Cleanup method is called. Removals will make the snapshot
// referred to by the key unavailable and make the key immediately
// available for re-use.
func AsynchronousRemove(config *SnapshotterConfig) error {
config.asyncRemove = true
return nil
}
type snapshotter struct { type snapshotter struct {
root string root string
ms *storage.MetaStore ms *storage.MetaStore
asyncRemove bool
} }
// NewSnapshotter returns a Snapshotter which uses overlayfs. The overlayfs // NewSnapshotter returns a Snapshotter which uses overlayfs. The overlayfs
// diffs are stored under the provided root. A metadata file is stored under // diffs are stored under the provided root. A metadata file is stored under
// the root. // the root.
func NewSnapshotter(root string) (snapshots.Snapshotter, error) { func NewSnapshotter(root string, opts ...Opt) (snapshots.Snapshotter, error) {
var config SnapshotterConfig
for _, opt := range opts {
if err := opt(&config); err != nil {
return nil, err
}
}
if err := os.MkdirAll(root, 0700); err != nil { if err := os.MkdirAll(root, 0700); err != nil {
return nil, err return nil, err
} }
@ -64,6 +89,7 @@ func NewSnapshotter(root string) (snapshots.Snapshotter, error) {
return &snapshotter{ return &snapshotter{
root: root, root: root,
ms: ms, ms: ms,
asyncRemove: config.asyncRemove,
}, nil }, nil
} }
@ -215,6 +241,28 @@ func (o *snapshotter) Remove(ctx context.Context, key string) (err error) {
return errors.Wrap(err, "failed to remove") return errors.Wrap(err, "failed to remove")
} }
if !o.asyncRemove {
var removals []string
removals, err = o.getCleanupDirectories(ctx, t)
if err != nil {
return errors.Wrap(err, "unable to get directories for removal")
}
// Remove directories after the transaction is closed, failures must not
// return error since the transaction is committed with the removal
// key no longer available.
defer func() {
if err == nil {
for _, dir := range removals {
if err := os.RemoveAll(dir); err != nil {
log.G(ctx).WithError(err).WithField("path", dir).Warn("failed to remove directory")
}
}
}
}()
}
return t.Commit() return t.Commit()
} }
@ -230,7 +278,7 @@ func (o *snapshotter) Walk(ctx context.Context, fn func(context.Context, snapsho
// Cleanup cleans up disk resources from removed or abandoned snapshots // Cleanup cleans up disk resources from removed or abandoned snapshots
func (o *snapshotter) Cleanup(ctx context.Context) error { func (o *snapshotter) Cleanup(ctx context.Context) error {
cleanup, err := o.getCleanupDirectories(ctx) cleanup, err := o.cleanupDirectories(ctx)
if err != nil { if err != nil {
return err return err
} }
@ -244,13 +292,17 @@ func (o *snapshotter) Cleanup(ctx context.Context) error {
return nil return nil
} }
func (o *snapshotter) getCleanupDirectories(ctx context.Context) ([]string, error) { func (o *snapshotter) cleanupDirectories(ctx context.Context) ([]string, error) {
ctx, t, err := o.ms.TransactionContext(ctx, false) ctx, t, err := o.ms.TransactionContext(ctx, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer t.Rollback() defer t.Rollback()
return o.getCleanupDirectories(ctx, t)
}
func (o *snapshotter) getCleanupDirectories(ctx context.Context, t storage.Transactor) ([]string, error) {
ids, err := storage.IDMap(ctx) ids, err := storage.IDMap(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
@ -307,7 +359,7 @@ func (o *snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, k
td, err = o.prepareDirectory(ctx, snapshotDir, kind) td, err = o.prepareDirectory(ctx, snapshotDir, kind)
if err != nil { if err != nil {
if rerr := t.Rollback(); rerr != nil { if rerr := t.Rollback(); rerr != nil {
log.G(ctx).WithError(rerr).Warn("Failure rolling back transaction") log.G(ctx).WithError(rerr).Warn("failed to rollback transaction")
} }
return nil, errors.Wrap(err, "failed to create prepare snapshot dir") return nil, errors.Wrap(err, "failed to create prepare snapshot dir")
} }
@ -335,7 +387,7 @@ func (o *snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, k
if err := os.Lchown(filepath.Join(td, "fs"), int(stat.Uid), int(stat.Gid)); err != nil { if err := os.Lchown(filepath.Join(td, "fs"), int(stat.Uid), int(stat.Gid)); err != nil {
if rerr := t.Rollback(); rerr != nil { if rerr := t.Rollback(); rerr != nil {
log.G(ctx).WithError(rerr).Warn("Failure rolling back transaction") log.G(ctx).WithError(rerr).Warn("failed to rollback transaction")
} }
return nil, errors.Wrap(err, "failed to chown") return nil, errors.Wrap(err, "failed to chown")
} }