Merge pull request #7529 from iyear/refactor-metastore-tx

Refactor metastore transaction
This commit is contained in:
Derek McGowan 2022-12-28 20:08:38 -08:00 committed by GitHub
commit cfe7ac9956
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -102,13 +102,12 @@ func NewSnapshotter(root string) (snapshots.Snapshotter, error) {
// //
// Should be used for parent resolution, existence checks and to discern // Should be used for parent resolution, existence checks and to discern
// the kind of snapshot. // the kind of snapshot.
func (b *snapshotter) Stat(ctx context.Context, key string) (snapshots.Info, error) { func (b *snapshotter) Stat(ctx context.Context, key string) (info snapshots.Info, err error) {
ctx, t, err := b.ms.TransactionContext(ctx, false) err = b.ms.WithTransaction(ctx, false, func(ctx context.Context) error {
if err != nil { _, info, _, err = storage.GetInfo(ctx, key)
return snapshots.Info{}, err return err
} })
defer t.Rollback()
_, info, _, err := storage.GetInfo(ctx, key)
if err != nil { if err != nil {
return snapshots.Info{}, err return snapshots.Info{}, err
} }
@ -116,19 +115,13 @@ func (b *snapshotter) Stat(ctx context.Context, key string) (snapshots.Info, err
return info, nil return info, nil
} }
func (b *snapshotter) Update(ctx context.Context, info snapshots.Info, fieldpaths ...string) (snapshots.Info, error) { func (b *snapshotter) Update(ctx context.Context, info snapshots.Info, fieldpaths ...string) (_ snapshots.Info, err error) {
ctx, t, err := b.ms.TransactionContext(ctx, true) err = b.ms.WithTransaction(ctx, true, func(ctx context.Context) error {
if err != nil {
return snapshots.Info{}, err
}
info, err = storage.UpdateInfo(ctx, info, fieldpaths...) info, err = storage.UpdateInfo(ctx, info, fieldpaths...)
if err != nil { return err
t.Rollback() })
return snapshots.Info{}, err
}
if err := t.Commit(); err != nil { if err != nil {
return snapshots.Info{}, err return snapshots.Info{}, err
} }
@ -140,18 +133,20 @@ func (b *snapshotter) Usage(ctx context.Context, key string) (snapshots.Usage, e
return b.usage(ctx, key) return b.usage(ctx, key)
} }
func (b *snapshotter) usage(ctx context.Context, key string) (snapshots.Usage, error) { func (b *snapshotter) usage(ctx context.Context, key string) (usage snapshots.Usage, err error) {
ctx, t, err := b.ms.TransactionContext(ctx, false) var (
if err != nil { id, parentID string
return snapshots.Usage{}, err info snapshots.Info
} )
id, info, usage, err := storage.GetInfo(ctx, key)
var parentID string err = b.ms.WithTransaction(ctx, false, func(ctx context.Context) error {
id, info, usage, err = storage.GetInfo(ctx, key)
if err == nil && info.Kind == snapshots.KindActive && info.Parent != "" { if err == nil && info.Kind == snapshots.KindActive && info.Parent != "" {
parentID, _, _, err = storage.GetInfo(ctx, info.Parent) parentID, _, _, err = storage.GetInfo(ctx, info.Parent)
} }
t.Rollback() // transaction no longer needed at this point. return err
})
if err != nil { if err != nil {
return snapshots.Usage{}, err return snapshots.Usage{}, err
@ -177,13 +172,10 @@ func (b *snapshotter) usage(ctx context.Context, key string) (snapshots.Usage, e
} }
// Walk the committed snapshots. // Walk the committed snapshots.
func (b *snapshotter) Walk(ctx context.Context, fn snapshots.WalkFunc, fs ...string) error { func (b *snapshotter) Walk(ctx context.Context, fn snapshots.WalkFunc, fs ...string) (err error) {
ctx, t, err := b.ms.TransactionContext(ctx, false) return b.ms.WithTransaction(ctx, false, func(ctx context.Context) error {
if err != nil {
return err
}
defer t.Rollback()
return storage.WalkInfo(ctx, fn, fs...) return storage.WalkInfo(ctx, fn, fs...)
})
} }
func (b *snapshotter) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) { func (b *snapshotter) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) {
@ -194,51 +186,39 @@ func (b *snapshotter) View(ctx context.Context, key, parent string, opts ...snap
return b.makeSnapshot(ctx, snapshots.KindView, key, parent, opts) return b.makeSnapshot(ctx, snapshots.KindView, key, parent, opts)
} }
func (b *snapshotter) makeSnapshot(ctx context.Context, kind snapshots.Kind, key, parent string, opts []snapshots.Opt) ([]mount.Mount, error) { func (b *snapshotter) makeSnapshot(ctx context.Context, kind snapshots.Kind, key, parent string, opts []snapshots.Opt) (_ []mount.Mount, err error) {
ctx, t, err := b.ms.TransactionContext(ctx, true) var (
if err != nil { target string
return nil, err s storage.Snapshot
} )
defer func() {
if err != nil && t != nil {
if rerr := t.Rollback(); rerr != nil {
log.G(ctx).WithError(rerr).Warn("failed to rollback transaction")
}
}
}()
s, err := storage.CreateSnapshot(ctx, kind, key, parent, opts...) err = b.ms.WithTransaction(ctx, true, func(ctx context.Context) error {
s, err = storage.CreateSnapshot(ctx, kind, key, parent, opts...)
if err != nil { if err != nil {
return nil, err return err
} }
target := filepath.Join(b.root, strings.ToLower(s.Kind.String()), s.ID) target = filepath.Join(b.root, strings.ToLower(s.Kind.String()), s.ID)
if len(s.ParentIDs) == 0 { if len(s.ParentIDs) == 0 {
// create new subvolume // create new subvolume
// btrfs subvolume create /dir // btrfs subvolume create /dir
if err = btrfs.SubvolCreate(target); err != nil { return btrfs.SubvolCreate(target)
return nil, err
} }
} else {
parentp := filepath.Join(b.root, "snapshots", s.ParentIDs[0]) parentp := filepath.Join(b.root, "snapshots", s.ParentIDs[0])
var readonly bool
if kind == snapshots.KindView {
readonly = true
}
// btrfs subvolume snapshot /parent /subvol // btrfs subvolume snapshot /parent /subvol
if err = btrfs.SubvolSnapshot(target, parentp, readonly); err != nil { readOnly := kind == snapshots.KindView
return nil, err return btrfs.SubvolSnapshot(target, parentp, readOnly)
} })
}
err = t.Commit()
t = nil
if err != nil { if err != nil {
if target != "" {
if derr := btrfs.SubvolDelete(target); derr != nil { if derr := btrfs.SubvolDelete(target); derr != nil {
log.G(ctx).WithError(derr).WithField("subvolume", target).Error("failed to delete subvolume") log.G(ctx).WithError(derr).WithField("subvolume", target).Error("failed to delete subvolume")
} }
}
return nil, err return nil, err
} }
@ -272,48 +252,41 @@ func (b *snapshotter) mounts(dir string, s storage.Snapshot) ([]mount.Mount, err
} }
func (b *snapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) (err error) { func (b *snapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) (err error) {
usage, err := b.usage(ctx, key) var usage snapshots.Usage
usage, err = b.usage(ctx, key)
if err != nil { if err != nil {
return fmt.Errorf("failed to compute usage: %w", err) return fmt.Errorf("failed to compute usage: %w", err)
} }
ctx, t, err := b.ms.TransactionContext(ctx, true) var source, target string
if err != nil { err = b.ms.WithTransaction(ctx, true, func(ctx context.Context) error {
return err
}
defer func() {
if err != nil && t != nil {
if rerr := t.Rollback(); rerr != nil {
log.G(ctx).WithError(rerr).Warn("failed to rollback transaction")
}
}
}()
id, err := storage.CommitActive(ctx, key, name, usage, opts...) // TODO(stevvooe): Resolve a usage value for btrfs id, err := storage.CommitActive(ctx, key, name, usage, opts...) // TODO(stevvooe): Resolve a usage value for btrfs
if err != nil { if err != nil {
return fmt.Errorf("failed to commit: %w", err) return fmt.Errorf("failed to commit: %w", err)
} }
source := filepath.Join(b.root, "active", id) source = filepath.Join(b.root, "active", id)
target := filepath.Join(b.root, "snapshots", id) target = filepath.Join(b.root, "snapshots", id)
if err := btrfs.SubvolSnapshot(target, source, true); err != nil { return btrfs.SubvolSnapshot(target, source, true)
return err })
}
err = t.Commit()
t = nil
if err != nil { if err != nil {
if target != "" {
if derr := btrfs.SubvolDelete(target); derr != nil { if derr := btrfs.SubvolDelete(target); derr != nil {
log.G(ctx).WithError(derr).WithField("subvolume", target).Error("failed to delete subvolume") log.G(ctx).WithError(derr).WithField("subvolume", target).Error("failed to delete subvolume")
} }
}
return err return err
} }
if source != "" {
if derr := btrfs.SubvolDelete(source); derr != nil { if derr := btrfs.SubvolDelete(source); derr != nil {
// Log as warning, only needed for cleanup, will not cause name collision // Log as warning, only needed for cleanup, will not cause name collision
log.G(ctx).WithError(derr).WithField("subvolume", source).Warn("failed to delete subvolume") log.G(ctx).WithError(derr).WithField("subvolume", source).Warn("failed to delete subvolume")
} }
}
return nil return nil
} }
@ -322,13 +295,14 @@ func (b *snapshotter) Commit(ctx context.Context, name, key string, opts ...snap
// called on an read-write or readonly transaction. // called on an read-write or readonly transaction.
// //
// This can be used to recover mounts after calling View or Prepare. // This can be used to recover mounts after calling View or Prepare.
func (b *snapshotter) Mounts(ctx context.Context, key string) ([]mount.Mount, error) { func (b *snapshotter) Mounts(ctx context.Context, key string) (_ []mount.Mount, err error) {
ctx, t, err := b.ms.TransactionContext(ctx, false) var s storage.Snapshot
if err != nil {
return nil, err err = b.ms.WithTransaction(ctx, false, func(ctx context.Context) error {
} s, err = storage.GetSnapshot(ctx, key)
s, err := storage.GetSnapshot(ctx, key) return err
t.Rollback() })
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get active snapshot: %w", err) return nil, fmt.Errorf("failed to get active snapshot: %w", err)
} }
@ -342,20 +316,10 @@ func (b *snapshotter) Mounts(ctx context.Context, key string) ([]mount.Mount, er
func (b *snapshotter) Remove(ctx context.Context, key string) (err error) { func (b *snapshotter) Remove(ctx context.Context, key string) (err error) {
var ( var (
source, removed string source, removed string
readonly bool readonly, restore bool
) )
ctx, t, err := b.ms.TransactionContext(ctx, true)
if err != nil {
return err
}
defer func() { defer func() {
if err != nil && t != nil {
if rerr := t.Rollback(); rerr != nil {
log.G(ctx).WithError(rerr).Warn("failed to rollback transaction")
}
}
if removed != "" { if removed != "" {
if derr := btrfs.SubvolDelete(removed); derr != nil { if derr := btrfs.SubvolDelete(removed); derr != nil {
log.G(ctx).WithError(derr).WithField("subvolume", removed).Warn("failed to delete subvolume") log.G(ctx).WithError(derr).WithField("subvolume", removed).Warn("failed to delete subvolume")
@ -363,6 +327,7 @@ func (b *snapshotter) Remove(ctx context.Context, key string) (err error) {
} }
}() }()
err = b.ms.WithTransaction(ctx, true, func(ctx context.Context) error {
id, k, err := storage.Remove(ctx, key) id, k, err := storage.Remove(ctx, key)
if err != nil { if err != nil {
return fmt.Errorf("failed to remove snapshot: %w", err) return fmt.Errorf("failed to remove snapshot: %w", err)
@ -382,18 +347,21 @@ func (b *snapshotter) Remove(ctx context.Context, key string) (err error) {
readonly = true readonly = true
} }
if err := btrfs.SubvolSnapshot(removed, source, readonly); err != nil { if err = btrfs.SubvolSnapshot(removed, source, readonly); err != nil {
removed = "" removed = ""
return err return err
} }
if err := btrfs.SubvolDelete(source); err != nil { if err = btrfs.SubvolDelete(source); err != nil {
return fmt.Errorf("failed to remove snapshot %v: %w", source, err) return fmt.Errorf("failed to remove snapshot %v: %w", source, err)
} }
err = t.Commit() restore = true
t = nil return nil
})
if err != nil { if err != nil {
if restore { // means failed to commit transaction
// Attempt to restore source // Attempt to restore source
if err1 := btrfs.SubvolSnapshot(source, removed, readonly); err1 != nil { if err1 := btrfs.SubvolSnapshot(source, removed, readonly); err1 != nil {
log.G(ctx).WithFields(logrus.Fields{ log.G(ctx).WithFields(logrus.Fields{
@ -404,6 +372,8 @@ func (b *snapshotter) Remove(ctx context.Context, key string) (err error) {
// Keep removed to allow for manual restore // Keep removed to allow for manual restore
removed = "" removed = ""
} }
}
return err return err
} }