Files
felhom-controller/controller/internal/backup/tier2_unreadable.go
T

57 lines
1.8 KiB
Go

package backup
import (
"sync"
"time"
)
// R-1252 (review round 2, HUNT-2 H1): a rejected backup block never deletes second-drive copies. RunTier2 reconciles
// only on a valid classification (tier2CaptureSetFull's `classified`); for a REJECTED block it says so on the app's
// status (note.tier2.settings_unreadable_kept) and tells the operator once a day through the existing operator-only
// digest (backup_run_failures, leg tier2-settings-unreadable — no new event type, so no hub change). Pinned by
// r1252_rejected_block_keeps_copies_test.go.
type tier2UnreadableWatch struct {
mu sync.Mutex
notify func(app, why string)
sent map[string]string // app → the UTC day it was last sent
now func() time.Time
}
// SetTier2SettingsUnreadableNotify wires the operator signal (main.go → notifier). INIT-ONLY.
func (m *Manager) SetTier2SettingsUnreadableNotify(fn func(app, why string)) {
m.tier2Unreadable.mu.Lock()
m.tier2Unreadable.notify = fn
m.tier2Unreadable.mu.Unlock()
}
// backupBlockRejected asks the stack provider why the app's backup block was rejected ("" = valid or absent).
func (m *Manager) backupBlockRejected(stack string) string {
if r, ok := m.stackProvider.(interface{ BackupBlockRejected(string) string }); ok {
return r.BackupBlockRejected(stack)
}
return ""
}
// notifyTier2SettingsUnreadable sends the operator signal at most once per app per UTC day.
func (m *Manager) notifyTier2SettingsUnreadable(app, why string) {
w := &m.tier2Unreadable
w.mu.Lock()
now := time.Now
if w.now != nil {
now = w.now
}
day := now().UTC().Format("2006-01-02")
if w.notify == nil || w.sent[app] == day {
w.mu.Unlock()
return
}
if w.sent == nil {
w.sent = map[string]string{}
}
w.sent[app] = day
fn := w.notify
w.mu.Unlock()
fn(app, why)
}