Merge pull request #7529 from iyear/refactor-metastore-tx
Refactor metastore transaction
This commit is contained in:
commit
cfe7ac9956
@ -102,13 +102,12 @@ func NewSnapshotter(root string) (snapshots.Snapshotter, error) {
|
||||
//
|
||||
// Should be used for parent resolution, existence checks and to discern
|
||||
// the kind of snapshot.
|
||||
func (b *snapshotter) Stat(ctx context.Context, key string) (snapshots.Info, error) {
|
||||
ctx, t, err := b.ms.TransactionContext(ctx, false)
|
||||
if err != nil {
|
||||
return snapshots.Info{}, err
|
||||
}
|
||||
defer t.Rollback()
|
||||
_, info, _, err := storage.GetInfo(ctx, key)
|
||||
func (b *snapshotter) Stat(ctx context.Context, key string) (info snapshots.Info, err error) {
|
||||
err = b.ms.WithTransaction(ctx, false, func(ctx context.Context) error {
|
||||
_, info, _, err = storage.GetInfo(ctx, key)
|
||||
return err
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return snapshots.Info{}, err
|
||||
}
|
||||
@ -116,19 +115,13 @@ func (b *snapshotter) Stat(ctx context.Context, key string) (snapshots.Info, err
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (b *snapshotter) Update(ctx context.Context, info snapshots.Info, fieldpaths ...string) (snapshots.Info, error) {
|
||||
ctx, t, err := b.ms.TransactionContext(ctx, true)
|
||||
if err != nil {
|
||||
return snapshots.Info{}, err
|
||||
}
|
||||
func (b *snapshotter) Update(ctx context.Context, info snapshots.Info, fieldpaths ...string) (_ snapshots.Info, err error) {
|
||||
err = b.ms.WithTransaction(ctx, true, func(ctx context.Context) error {
|
||||
info, err = storage.UpdateInfo(ctx, info, fieldpaths...)
|
||||
return err
|
||||
})
|
||||
|
||||
info, err = storage.UpdateInfo(ctx, info, fieldpaths...)
|
||||
if err != nil {
|
||||
t.Rollback()
|
||||
return snapshots.Info{}, err
|
||||
}
|
||||
|
||||
if err := t.Commit(); err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
func (b *snapshotter) usage(ctx context.Context, key string) (snapshots.Usage, error) {
|
||||
ctx, t, err := b.ms.TransactionContext(ctx, false)
|
||||
if err != nil {
|
||||
return snapshots.Usage{}, err
|
||||
}
|
||||
id, info, usage, err := storage.GetInfo(ctx, key)
|
||||
var parentID string
|
||||
if err == nil && info.Kind == snapshots.KindActive && info.Parent != "" {
|
||||
parentID, _, _, err = storage.GetInfo(ctx, info.Parent)
|
||||
func (b *snapshotter) usage(ctx context.Context, key string) (usage snapshots.Usage, err error) {
|
||||
var (
|
||||
id, parentID string
|
||||
info snapshots.Info
|
||||
)
|
||||
|
||||
}
|
||||
t.Rollback() // transaction no longer needed at this point.
|
||||
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 != "" {
|
||||
parentID, _, _, err = storage.GetInfo(ctx, info.Parent)
|
||||
}
|
||||
return err
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return snapshots.Usage{}, err
|
||||
@ -177,13 +172,10 @@ func (b *snapshotter) usage(ctx context.Context, key string) (snapshots.Usage, e
|
||||
}
|
||||
|
||||
// Walk the committed snapshots.
|
||||
func (b *snapshotter) Walk(ctx context.Context, fn snapshots.WalkFunc, fs ...string) error {
|
||||
ctx, t, err := b.ms.TransactionContext(ctx, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer t.Rollback()
|
||||
return storage.WalkInfo(ctx, fn, fs...)
|
||||
func (b *snapshotter) Walk(ctx context.Context, fn snapshots.WalkFunc, fs ...string) (err error) {
|
||||
return b.ms.WithTransaction(ctx, false, func(ctx context.Context) error {
|
||||
return storage.WalkInfo(ctx, fn, fs...)
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func (b *snapshotter) makeSnapshot(ctx context.Context, kind snapshots.Kind, key, parent string, opts []snapshots.Opt) ([]mount.Mount, error) {
|
||||
ctx, t, err := b.ms.TransactionContext(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil && t != nil {
|
||||
if rerr := t.Rollback(); rerr != nil {
|
||||
log.G(ctx).WithError(rerr).Warn("failed to rollback transaction")
|
||||
}
|
||||
func (b *snapshotter) makeSnapshot(ctx context.Context, kind snapshots.Kind, key, parent string, opts []snapshots.Opt) (_ []mount.Mount, err error) {
|
||||
var (
|
||||
target string
|
||||
s storage.Snapshot
|
||||
)
|
||||
|
||||
err = b.ms.WithTransaction(ctx, true, func(ctx context.Context) error {
|
||||
s, err = storage.CreateSnapshot(ctx, kind, key, parent, opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}()
|
||||
|
||||
s, err := storage.CreateSnapshot(ctx, kind, key, parent, opts...)
|
||||
if err != nil {
|
||||
return nil, 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 {
|
||||
// create new subvolume
|
||||
// btrfs subvolume create /dir
|
||||
if err = btrfs.SubvolCreate(target); err != nil {
|
||||
return nil, err
|
||||
if len(s.ParentIDs) == 0 {
|
||||
// create new subvolume
|
||||
// btrfs subvolume create /dir
|
||||
return btrfs.SubvolCreate(target)
|
||||
}
|
||||
} else {
|
||||
parentp := filepath.Join(b.root, "snapshots", s.ParentIDs[0])
|
||||
|
||||
var readonly bool
|
||||
if kind == snapshots.KindView {
|
||||
readonly = true
|
||||
// btrfs subvolume snapshot /parent /subvol
|
||||
readOnly := kind == snapshots.KindView
|
||||
return btrfs.SubvolSnapshot(target, parentp, readOnly)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if target != "" {
|
||||
if derr := btrfs.SubvolDelete(target); derr != nil {
|
||||
log.G(ctx).WithError(derr).WithField("subvolume", target).Error("failed to delete subvolume")
|
||||
}
|
||||
}
|
||||
|
||||
// btrfs subvolume snapshot /parent /subvol
|
||||
if err = btrfs.SubvolSnapshot(target, parentp, readonly); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
err = t.Commit()
|
||||
t = nil
|
||||
if err != nil {
|
||||
if derr := btrfs.SubvolDelete(target); derr != nil {
|
||||
log.G(ctx).WithError(derr).WithField("subvolume", target).Error("failed to delete subvolume")
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -272,47 +252,40 @@ 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) {
|
||||
usage, err := b.usage(ctx, key)
|
||||
var usage snapshots.Usage
|
||||
usage, err = b.usage(ctx, key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to compute usage: %w", err)
|
||||
}
|
||||
|
||||
ctx, t, err := b.ms.TransactionContext(ctx, true)
|
||||
var source, target string
|
||||
err = b.ms.WithTransaction(ctx, true, func(ctx context.Context) error {
|
||||
id, err := storage.CommitActive(ctx, key, name, usage, opts...) // TODO(stevvooe): Resolve a usage value for btrfs
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to commit: %w", err)
|
||||
}
|
||||
|
||||
source = filepath.Join(b.root, "active", id)
|
||||
target = filepath.Join(b.root, "snapshots", id)
|
||||
|
||||
return btrfs.SubvolSnapshot(target, source, true)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
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")
|
||||
if target != "" {
|
||||
if derr := btrfs.SubvolDelete(target); derr != nil {
|
||||
log.G(ctx).WithError(derr).WithField("subvolume", target).Error("failed to delete subvolume")
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
id, err := storage.CommitActive(ctx, key, name, usage, opts...) // TODO(stevvooe): Resolve a usage value for btrfs
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to commit: %w", err)
|
||||
}
|
||||
|
||||
source := filepath.Join(b.root, "active", id)
|
||||
target := filepath.Join(b.root, "snapshots", id)
|
||||
|
||||
if err := btrfs.SubvolSnapshot(target, source, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = t.Commit()
|
||||
t = nil
|
||||
if err != nil {
|
||||
if derr := btrfs.SubvolDelete(target); derr != nil {
|
||||
log.G(ctx).WithError(derr).WithField("subvolume", target).Error("failed to delete subvolume")
|
||||
if source != "" {
|
||||
if derr := btrfs.SubvolDelete(source); derr != nil {
|
||||
// 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")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if derr := btrfs.SubvolDelete(source); derr != nil {
|
||||
// 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")
|
||||
}
|
||||
|
||||
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.
|
||||
//
|
||||
// This can be used to recover mounts after calling View or Prepare.
|
||||
func (b *snapshotter) Mounts(ctx context.Context, key string) ([]mount.Mount, error) {
|
||||
ctx, t, err := b.ms.TransactionContext(ctx, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s, err := storage.GetSnapshot(ctx, key)
|
||||
t.Rollback()
|
||||
func (b *snapshotter) Mounts(ctx context.Context, key string) (_ []mount.Mount, err error) {
|
||||
var s storage.Snapshot
|
||||
|
||||
err = b.ms.WithTransaction(ctx, false, func(ctx context.Context) error {
|
||||
s, err = storage.GetSnapshot(ctx, key)
|
||||
return err
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get active snapshot: %w", err)
|
||||
}
|
||||
@ -341,21 +315,11 @@ func (b *snapshotter) Mounts(ctx context.Context, key string) ([]mount.Mount, er
|
||||
// associated with the key will be removed.
|
||||
func (b *snapshotter) Remove(ctx context.Context, key string) (err error) {
|
||||
var (
|
||||
source, removed string
|
||||
readonly bool
|
||||
source, removed string
|
||||
readonly, restore bool
|
||||
)
|
||||
|
||||
ctx, t, err := b.ms.TransactionContext(ctx, true)
|
||||
if err != nil {
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
if removed != "" {
|
||||
if derr := btrfs.SubvolDelete(removed); derr != nil {
|
||||
log.G(ctx).WithError(derr).WithField("subvolume", removed).Warn("failed to delete subvolume")
|
||||
@ -363,47 +327,53 @@ func (b *snapshotter) Remove(ctx context.Context, key string) (err error) {
|
||||
}
|
||||
}()
|
||||
|
||||
id, k, err := storage.Remove(ctx, key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to remove snapshot: %w", err)
|
||||
}
|
||||
|
||||
switch k {
|
||||
case snapshots.KindView:
|
||||
source = filepath.Join(b.root, "view", id)
|
||||
removed = filepath.Join(b.root, "view", "rm-"+id)
|
||||
readonly = true
|
||||
case snapshots.KindActive:
|
||||
source = filepath.Join(b.root, "active", id)
|
||||
removed = filepath.Join(b.root, "active", "rm-"+id)
|
||||
case snapshots.KindCommitted:
|
||||
source = filepath.Join(b.root, "snapshots", id)
|
||||
removed = filepath.Join(b.root, "snapshots", "rm-"+id)
|
||||
readonly = true
|
||||
}
|
||||
|
||||
if err := btrfs.SubvolSnapshot(removed, source, readonly); err != nil {
|
||||
removed = ""
|
||||
return err
|
||||
}
|
||||
|
||||
if err := btrfs.SubvolDelete(source); err != nil {
|
||||
return fmt.Errorf("failed to remove snapshot %v: %w", source, err)
|
||||
}
|
||||
|
||||
err = t.Commit()
|
||||
t = nil
|
||||
if err != nil {
|
||||
// Attempt to restore source
|
||||
if err1 := btrfs.SubvolSnapshot(source, removed, readonly); err1 != nil {
|
||||
log.G(ctx).WithFields(logrus.Fields{
|
||||
logrus.ErrorKey: err1,
|
||||
"subvolume": source,
|
||||
"renamed": removed,
|
||||
}).Error("failed to restore subvolume from renamed")
|
||||
// Keep removed to allow for manual restore
|
||||
removed = ""
|
||||
err = b.ms.WithTransaction(ctx, true, func(ctx context.Context) error {
|
||||
id, k, err := storage.Remove(ctx, key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to remove snapshot: %w", err)
|
||||
}
|
||||
|
||||
switch k {
|
||||
case snapshots.KindView:
|
||||
source = filepath.Join(b.root, "view", id)
|
||||
removed = filepath.Join(b.root, "view", "rm-"+id)
|
||||
readonly = true
|
||||
case snapshots.KindActive:
|
||||
source = filepath.Join(b.root, "active", id)
|
||||
removed = filepath.Join(b.root, "active", "rm-"+id)
|
||||
case snapshots.KindCommitted:
|
||||
source = filepath.Join(b.root, "snapshots", id)
|
||||
removed = filepath.Join(b.root, "snapshots", "rm-"+id)
|
||||
readonly = true
|
||||
}
|
||||
|
||||
if err = btrfs.SubvolSnapshot(removed, source, readonly); err != nil {
|
||||
removed = ""
|
||||
return err
|
||||
}
|
||||
|
||||
if err = btrfs.SubvolDelete(source); err != nil {
|
||||
return fmt.Errorf("failed to remove snapshot %v: %w", source, err)
|
||||
}
|
||||
|
||||
restore = true
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if restore { // means failed to commit transaction
|
||||
// Attempt to restore source
|
||||
if err1 := btrfs.SubvolSnapshot(source, removed, readonly); err1 != nil {
|
||||
log.G(ctx).WithFields(logrus.Fields{
|
||||
logrus.ErrorKey: err1,
|
||||
"subvolume": source,
|
||||
"renamed": removed,
|
||||
}).Error("failed to restore subvolume from renamed")
|
||||
// Keep removed to allow for manual restore
|
||||
removed = ""
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user