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
|
// 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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user