Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat.] AsyncRemove support #314

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cmd/overlaybd-snapshotter/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ func parseConfig(fpath string) error {
if err := json.Unmarshal(data, pconfig); err != nil {
return errors.Wrapf(err, "failed to parse plugin config from %s", string(data))
}
logrus.Infof("snapshotter commitID: %s, rwMode: %s, autoRemove: %v, writableLayerType: %s",
commitID, pconfig.RwMode, pconfig.AutoRemoveDev, pconfig.WritableLayerType)
logrus.Infof("snapshotter commitID: %s, rwMode: %s, autoRemove: %v, writableLayerType: %s, asyncRemoveSnapshot: %v",
commitID, pconfig.RwMode, pconfig.AutoRemoveDev, pconfig.WritableLayerType, pconfig.AsyncRemove)
return nil
}

Expand Down
1 change: 1 addition & 0 deletions docs/PROMETHEUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ To configure overlaybd as a Prometheus target, you need to specify `uriPrefix` a
```json
{
"root": "/var/lib/containerd/io.containerd.snapshotter.v1.overlaybd",
"asyncRemove": false,
"address": "/run/overlaybd-snapshotter/overlaybd.sock",
"verbose": "info",
"rwMode": "overlayfs",
Expand Down
93 changes: 90 additions & 3 deletions pkg/snapshot/overlay.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ type Registry struct {
}

type BootConfig struct {
AsyncRemove bool `json:"asyncRemove"`
Address string `json:"address"`
Root string `json:"root"`
LogLevel string `json:"verbose"`
Expand All @@ -102,6 +103,7 @@ type BootConfig struct {

func DefaultBootConfig() *BootConfig {
return &BootConfig{
AsyncRemove: false,
LogLevel: "info",
RwMode: "overlayfs",
LogReportCaller: false,
Expand Down Expand Up @@ -142,6 +144,15 @@ var defaultConfig = SnapshotterConfig{
// Opt is an option to configure the 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 *BootConfig) error {
config.AsyncRemove = true
return nil
}

// snapshotter is implementation of github.com/containerd/containerd/snapshots.Snapshotter.
//
// It is a snapshotter plugin. The layout of root dir is organized:
Expand Down Expand Up @@ -189,6 +200,7 @@ type snapshotter struct {
tenant int
locker *locker.Locker
turboFsType []string
asyncRemove bool

quotaDriver *diskquota.PrjQuotaDriver
quotaSize string
Expand Down Expand Up @@ -256,6 +268,7 @@ func NewSnapshotter(bootConfig *BootConfig, opts ...Opt) (snapshots.Snapshotter,
quotaDriver: &diskquota.PrjQuotaDriver{
QuotaIDs: make(map[uint32]struct{}),
},
asyncRemove: bootConfig.AsyncRemove,
}, nil
}

Expand Down Expand Up @@ -910,6 +923,63 @@ func (o *snapshotter) commit(ctx context.Context, name, key string, opts ...snap
return id, info, nil
}

func (o *snapshotter) cleanupDirectories(ctx context.Context) ([]string, error) {
// Get a write transaction to ensure no other write transaction can be entered
// while the cleanup is scanning.
ctx, t, err := o.ms.TransactionContext(ctx, true)
if err != nil {
return nil, err
}

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)
if err != nil {
return nil, err
}

snapshotDir := filepath.Join(o.root, "snapshots")
fd, err := os.Open(snapshotDir)
if err != nil {
return nil, err
}
defer fd.Close()

dirs, err := fd.Readdirnames(0)
if err != nil {
return nil, err
}

cleanup := []string{}
for _, d := range dirs {
if _, ok := ids[d]; ok {
continue
}

cleanup = append(cleanup, filepath.Join(snapshotDir, d))
}

return cleanup, nil
}

// 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 {
cleanup, err := o.cleanupDirectories(ctx)
if err != nil {
return err
}

for _, dir := range cleanup {
if err := os.RemoveAll(dir); err != nil {
log.G(ctx).WithError(err).WithField("path", dir).Warn("failed to remove directory")
}
}

return nil
}

// Remove abandons the snapshot identified by key. The snapshot will
// immediately become unavailable and unrecoverable.
func (o *snapshotter) Remove(ctx context.Context, key string) (err error) {
Expand Down Expand Up @@ -988,9 +1058,26 @@ func (o *snapshotter) Remove(ctx context.Context, key string) (err error) {
if err != nil {
return errors.Wrap(err, "failed to remove")
}

if err := os.RemoveAll(o.snPath(id)); err != nil && !os.IsNotExist(err) {
return err
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")
}
}
}
}()
} else {
log.G(ctx).Info("asyncRemove enabled, remove snapshots in Cleanup() method.")
}

rollback = false
Expand Down
Loading