hub v0.136.0: nightly VACUUM INTO snapshot, keep 2 (R-173 decision A); hub PVC 2Gi + Longhorn default group; 09 rulings 125-127; 05 §16.3
gates / gates (push) Failing after 14m0s
gates / gates (push) Failing after 14m0s
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:
@@ -17,6 +17,7 @@ import (
|
||||
"gitea.dooplex.hu/admin/felhom-hub/internal/api"
|
||||
"gitea.dooplex.hu/admin/felhom-hub/internal/assets"
|
||||
"gitea.dooplex.hu/admin/felhom-hub/internal/claim"
|
||||
"gitea.dooplex.hu/admin/felhom-hub/internal/dbsnap"
|
||||
"gitea.dooplex.hu/admin/felhom-hub/internal/gitea"
|
||||
"gitea.dooplex.hu/admin/felhom-hub/internal/hetznerapi"
|
||||
"gitea.dooplex.hu/admin/felhom-hub/internal/intent"
|
||||
@@ -848,6 +849,22 @@ func main() {
|
||||
}
|
||||
}()
|
||||
|
||||
// Nightly DB snapshot (R-173, v0.136.0, `05` §16.3): VACUUM INTO <data>/snapshots/, keep 2. DooPlex's
|
||||
// felhom-hub-db-backup.service picks up the newest at 02:30, checks, encrypts and pushes it to ep0. A start-up
|
||||
// catch-up runs one when the newest is older than 24 h (a pod that was down at 02:00).
|
||||
snapMaker := &dbsnap.Maker{Store: dataStore, Dir: filepath.Join(cfg.Server.DataDir, "snapshots"), Logger: logger}
|
||||
runSnapshot := func() {
|
||||
sctx, cancel := context.WithTimeout(ctx, 10*time.Minute)
|
||||
defer cancel()
|
||||
if _, err := snapMaker.Make(sctx); err != nil {
|
||||
logger.Printf("[ERROR] db snapshot failed: %v", err)
|
||||
}
|
||||
}
|
||||
if dbsnap.NeedsCatchUp(snapMaker.Dir, time.Now(), 24*time.Hour) {
|
||||
go runSnapshot()
|
||||
}
|
||||
go scheduleDaily(ctx, "db-snapshot", "02:00", runSnapshot, logger)
|
||||
|
||||
// Backup deadline checker — runs daily at 05:00 Budapest
|
||||
go scheduleDaily(ctx, "deadline-check", "05:00", func() {
|
||||
monitor.CheckBackupDeadlines(dataStore, stalenessChecker, dispatcher.ProcessEvent, logger)
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// R-173 seam wiring: main() must schedule the nightly db snapshot — a scheduleDaily call named "db-snapshot" at
|
||||
// "02:00", before DooPlex's 02:30 push — and must run the start-up catch-up (dbsnap.NeedsCatchUp). Without them the
|
||||
// dbsnap package exists and DooPlex pushes nothing new. RED-PROOF: delete either call → this test fails.
|
||||
func TestR173_MainSchedulesTheDBSnapshot(t *testing.T) {
|
||||
f, err := parser.ParseFile(token.NewFileSet(), "main.go", nil, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
scheduled, catchUp := false, false
|
||||
ast.Inspect(f, func(n ast.Node) bool {
|
||||
c, ok := n.(*ast.CallExpr)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
if id, ok := c.Fun.(*ast.Ident); ok && id.Name == "scheduleDaily" && len(c.Args) >= 3 {
|
||||
name, _ := c.Args[1].(*ast.BasicLit)
|
||||
at, _ := c.Args[2].(*ast.BasicLit)
|
||||
if name != nil && at != nil {
|
||||
n, _ := strconv.Unquote(name.Value)
|
||||
a, _ := strconv.Unquote(at.Value)
|
||||
if n == "db-snapshot" && a == "02:00" {
|
||||
scheduled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if sel, ok := c.Fun.(*ast.SelectorExpr); ok && sel.Sel.Name == "NeedsCatchUp" {
|
||||
catchUp = true
|
||||
}
|
||||
return true
|
||||
})
|
||||
if !scheduled {
|
||||
t.Error(`cmd/hub/main.go never calls scheduleDaily(ctx, "db-snapshot", "02:00", …) — no nightly snapshot`)
|
||||
}
|
||||
if !catchUp {
|
||||
t.Error("cmd/hub/main.go never calls dbsnap.NeedsCatchUp — a pod down at 02:00 leaves a stale snapshot")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user