v0.133.0: a restore-test can never fill a box's disk; leftovers retried on a timer (R-672, R-673)
gates / gates (push) Successful in 13s
gates / gates (push) Successful in 13s
Space preflight before anything is created (uncompressed size from the vzdump log / PBS snapshot, x1.2 + 5 GiB, thin metadata, off the tested guest's pool when another storage is eligible, unknown refuses, reported as a non-pass result). Failed scratch teardown and the stale-lock sweep retried every 10 min (the sweep under the heavy-op gate). A thin pool crossing 90% requests an immediate host report. Six red-proofs. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -44,6 +44,17 @@ type Engine struct {
|
||||
|
||||
opSeq uint64 // atomic; makes each op id unique per attempt
|
||||
|
||||
// restoreSpace + spacePolicy are the restore-test's space preflight (R-672). nil space REFUSES
|
||||
// every restore-test (fail-closed) — see restoretest_space.go.
|
||||
restoreSpace RestoreSpace
|
||||
spacePolicy SpacePolicy
|
||||
|
||||
// scratchMu guards activeScratch (the vmids a running restore-test owns — the retry timer never
|
||||
// touches those) and teardownTries (failed timer retries per journal op, R-672 rule 3).
|
||||
scratchMu sync.Mutex
|
||||
activeScratch map[int]bool
|
||||
teardownTries map[string]int
|
||||
|
||||
// lastRes records the most recent successful Reconcile Result (v0.90.0, R-28 fast-tick source).
|
||||
// The fast-tick reads it to decide convergence: actionable drift is Planned − Pending > 0 (a
|
||||
// destructive pending_signature refusal is EXPECTED state, not drift to hammer on). lastOK is
|
||||
@@ -86,6 +97,10 @@ type EngineOptions struct {
|
||||
HostRunner proxmox.Runner
|
||||
// StateDir is the agent state dir ("" → /var/lib/felhom-agent); only the 4d swap reads it.
|
||||
StateDir string
|
||||
// RestoreSpace is the restore-test's space preflight (R-672). nil → every restore-test is REFUSED
|
||||
// with its reason (fail-closed). SpacePolicy zero → DefaultSpacePolicy.
|
||||
RestoreSpace RestoreSpace
|
||||
SpacePolicy SpacePolicy
|
||||
}
|
||||
|
||||
// NewEngine builds an Engine. The Queue is shared (the single §10 choke point); the
|
||||
@@ -124,9 +139,24 @@ func NewEngine(opts EngineOptions) *Engine {
|
||||
logger: logger,
|
||||
hostRun: opts.HostRunner,
|
||||
stateDir: stateDir,
|
||||
|
||||
restoreSpace: opts.RestoreSpace,
|
||||
spacePolicy: policyOrDefault(opts.SpacePolicy),
|
||||
activeScratch: map[int]bool{},
|
||||
teardownTries: map[string]int{},
|
||||
}
|
||||
}
|
||||
|
||||
func policyOrDefault(p SpacePolicy) SpacePolicy {
|
||||
if p.Factor < 1 {
|
||||
p.Factor = DefaultSpacePolicy.Factor
|
||||
}
|
||||
if p.ReserveBytes <= 0 {
|
||||
p.ReserveBytes = DefaultSpacePolicy.ReserveBytes
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// Result summarizes one Reconcile pass.
|
||||
type Result struct {
|
||||
Planned int
|
||||
|
||||
Reference in New Issue
Block a user