Fence installation transaction durability (T-613)

This commit is contained in:
ila
2026-07-18 16:28:49 +08:00
parent 84befee70f
commit 20596a7de4
18 changed files with 697 additions and 64 deletions
+36 -18
View File
@@ -3,7 +3,6 @@ package installer
import (
"errors"
"fmt"
"os"
)
var ErrRecoveryInconsistent = errors.New("install recovery state is inconsistent")
@@ -24,6 +23,11 @@ type RecoveryResult struct {
// Recover resolves an interrupted transaction from journal and directory state.
func Recover(root string) (RecoveryResult, error) {
return recoverWithFence(root, defaultDurability())
}
func recoverWithFence(root string, fence durabilityFence) (RecoveryResult, error) {
fence = effectiveDurability(fence)
layout, err := inspectAppLayout(root)
if err != nil {
return RecoveryResult{}, err
@@ -57,18 +61,18 @@ func Recover(root string) (RecoveryResult, error) {
state.staging,
)
}
if err := removeManagedDirectory(layout, layout.backup); err != nil {
if err := removeManagedDirectoryWithFence(layout, layout.backup, fence); err != nil {
return RecoveryResult{}, err
}
if err := removeTransaction(layout); err != nil {
if err := removeTransactionWithFence(layout, fence); err != nil {
return RecoveryResult{}, err
}
result.Action = RecoveryCommitted
return result, nil
case phaseRollbackRequired:
return recoverRollbackRequired(layout, record, state, result)
return recoverRollbackRequired(layout, record, state, result, fence)
case phasePrepared, phaseCurrentBackedUp, phaseStagingActivated:
return recoverUncommitted(layout, record, state, result)
return recoverUncommitted(layout, record, state, result, fence)
default:
return RecoveryResult{}, fmt.Errorf("%w: phase=%q", ErrTransactionCorrupt, record.Phase)
}
@@ -79,6 +83,7 @@ func recoverUncommitted(
record transactionRecord,
state directoryState,
result RecoveryResult,
fence durabilityFence,
) (RecoveryResult, error) {
if record.HadCurrent {
if state.backup {
@@ -89,27 +94,39 @@ func recoverUncommitted(
)
}
if state.current {
if err := os.Rename(layout.current, layout.staging); err != nil {
if err := renameManagedDirectory(
layout,
layout.current,
layout.staging,
fence,
"app root after recovery staging rename",
); err != nil {
return RecoveryResult{}, fmt.Errorf("move unverified current aside: %w", err)
}
}
if err := os.Rename(layout.backup, layout.current); err != nil {
if err := renameManagedDirectory(
layout,
layout.backup,
layout.current,
fence,
"app root after recovery current restore",
); err != nil {
return RecoveryResult{}, fmt.Errorf("restore backup during recovery: %w", err)
}
if err := removeManagedDirectory(layout, layout.staging); err != nil {
if err := removeManagedDirectoryWithFence(layout, layout.staging, fence); err != nil {
return RecoveryResult{}, err
}
if err := removeTransaction(layout); err != nil {
if err := removeTransactionWithFence(layout, fence); err != nil {
return RecoveryResult{}, err
}
result.Action = RecoveryRolledBack
return result, nil
}
if record.Phase == phasePrepared && state.current && state.staging {
if err := removeManagedDirectory(layout, layout.staging); err != nil {
if err := removeManagedDirectoryWithFence(layout, layout.staging, fence); err != nil {
return RecoveryResult{}, err
}
if err := removeTransaction(layout); err != nil {
if err := removeTransactionWithFence(layout, fence); err != nil {
return RecoveryResult{}, err
}
result.Action = RecoveryAborted
@@ -127,13 +144,13 @@ func recoverUncommitted(
ErrRecoveryInconsistent,
)
}
if err := removeManagedDirectory(layout, layout.current); err != nil {
if err := removeManagedDirectoryWithFence(layout, layout.current, fence); err != nil {
return RecoveryResult{}, err
}
if err := removeManagedDirectory(layout, layout.staging); err != nil {
if err := removeManagedDirectoryWithFence(layout, layout.staging, fence); err != nil {
return RecoveryResult{}, err
}
if err := removeTransaction(layout); err != nil {
if err := removeTransactionWithFence(layout, fence); err != nil {
return RecoveryResult{}, err
}
result.Action = RecoveryAborted
@@ -145,6 +162,7 @@ func recoverRollbackRequired(
record transactionRecord,
state directoryState,
result RecoveryResult,
fence durabilityFence,
) (RecoveryResult, error) {
if record.HadCurrent && !state.backup {
if !state.current {
@@ -153,19 +171,19 @@ func recoverRollbackRequired(
ErrRecoveryInconsistent,
)
}
if err := removeManagedDirectory(layout, layout.staging); err != nil {
if err := removeManagedDirectoryWithFence(layout, layout.staging, fence); err != nil {
return RecoveryResult{}, err
}
if err := removeTransaction(layout); err != nil {
if err := removeTransactionWithFence(layout, fence); err != nil {
return RecoveryResult{}, err
}
result.Action = RecoveryRolledBack
return result, nil
}
if err := rollbackActivated(layout, record.HadCurrent); err != nil {
if err := rollbackActivated(layout, record.HadCurrent, fence); err != nil {
return RecoveryResult{}, err
}
if err := removeTransaction(layout); err != nil {
if err := removeTransactionWithFence(layout, fence); err != nil {
return RecoveryResult{}, err
}
result.Action = RecoveryRolledBack