v0.243.0: FileBrowser generated admin password (R-513); per-tier whole-guest backup truth (R-517); skip absent-storage tiers (R-518); OOM-killed worker visible (R-514)
gates / gates (push) Successful in 14s
gates / gates (push) Successful in 14s
MinAgent: 0.131.0 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -35,6 +35,43 @@ var ErrTiersUnsupported = errors.New("quiesce: agent does not serve /backup/tier
|
||||
type BackupTier struct {
|
||||
Target string
|
||||
Primary bool
|
||||
// StorageAbsent (R-518, agent >= v0.131.0): the agent POSITIVELY determined the tier's storage
|
||||
// does not exist. Only then is the tier skipped; unknown/legacy stay false (fail toward backing up).
|
||||
StorageAbsent bool
|
||||
}
|
||||
|
||||
// TierSkipNotifier is the OPTIONAL notifier extension for a tier skipped because its storage does not
|
||||
// exist (R-518). Optional so every existing TierNotifier keeps compiling.
|
||||
type TierSkipNotifier interface {
|
||||
BackupTierSkipped(tier, message string)
|
||||
}
|
||||
|
||||
// skipAbsentTiers drops the tiers whose storage the agent reports absent (R-518's cheap half,
|
||||
// BIGNIGHT: the apps stayed stopped through a PBS tier whose storage did not exist, then it failed).
|
||||
// It logs every skip and notifies ONCE per tier per absence (a scheduled loop re-resolves every poll;
|
||||
// the note re-arms when the storage reappears).
|
||||
func (l *Loop) skipAbsentTiers(tiers []BackupTier) []BackupTier {
|
||||
out := make([]BackupTier, 0, len(tiers))
|
||||
l.skipMu.Lock()
|
||||
defer l.skipMu.Unlock()
|
||||
if l.skipNoted == nil {
|
||||
l.skipNoted = map[string]bool{}
|
||||
}
|
||||
for _, t := range tiers {
|
||||
if !t.StorageAbsent {
|
||||
delete(l.skipNoted, t.Target)
|
||||
out = append(out, t)
|
||||
continue
|
||||
}
|
||||
l.logger.Printf("[WARN] [quiesce] tier %s skipped: its storage does not exist on the host — no app is stopped for it", tierLabel(t.Target))
|
||||
if !l.skipNoted[t.Target] {
|
||||
l.skipNoted[t.Target] = true
|
||||
if n, ok := l.tierNotify.(TierSkipNotifier); ok && n != nil {
|
||||
n.BackupTierSkipped(t.Target, "Whole-guest backup tier "+t.Target+" skipped: its storage does not exist on the host (never provisioned or removed). No app was stopped for it.")
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// TieredBackend is the OPTIONAL R-82 extension to Backend. A backend that does not implement it
|
||||
@@ -120,6 +157,14 @@ func (l *Loop) resolveDueTiers(ctx context.Context) (due []dueTier, degraded boo
|
||||
if terr != nil {
|
||||
return nil, false, terr
|
||||
}
|
||||
if len(tiers) > 0 {
|
||||
if kept := l.skipAbsentTiers(tiers); len(kept) == 0 {
|
||||
l.logger.Printf("[WARN] [quiesce] every advertised tier's storage is absent — nothing to back up this cycle")
|
||||
return nil, false, nil
|
||||
} else {
|
||||
tiers = kept
|
||||
}
|
||||
}
|
||||
if len(tiers) == 0 {
|
||||
// An agent that advertises no tiers cannot be backed up per-tier, but it can still be
|
||||
// backed up untargeted. Fail toward DOING the backup, never toward skipping it.
|
||||
|
||||
Reference in New Issue
Block a user