Files
nexarch/archive/docs/BAK-07-PRUEFPROTOKOLL.md
T
sysops 004c6fab62 docs(archive): BAK-07 dokumentiert fehlende Rotations-Kohaerenz
Nutzerfrage: DB- und Objekt-Rotation laufen unabhaengig, kein Test/
Invariant stellt sicher, dass aeltester erreichbarer Snapshot und
aelteste erreichbare DB-Generation zeitlich zusammenpassen. Aktuell
identische keep-Werte sind Zufall, kein erzwungenes Verhalten - als
Folgepunkt dokumentiert, nicht Teil des Tickets.
2026-08-30 00:54:02 +02:00

6.3 KiB
Raw Blame History

BAK-07 Prüfprotokoll: Aufbewahrungsfrist für Backup-Snapshots

Voraussetzung BAK-01, BAK-02 erledigt, siehe eigene Protokolle.

Grundsatzentscheidung: restic-native Staffelung für Objekte, eigene GFS-Funktion für DB-Generationen

Objekt-Storage (BAK-02): restic beherrscht gestaffelte Aufbewahrung bereits nativ (forget --keep-daily/--keep-weekly/--keep-monthly) — kein Eigenbau, internal/objectbackup.PruneTiered ist ein dünner Wrapper (dieselbe Disziplin wie beim ursprünglichen restic-statt-Eigenbau-Entscheid in BAK-02).

Datenbank-Generationen (BAK-01): pg_basebackup kennt keine Staffelung, daher internal/backup.PruneTiered als reine Funktion (GFS-Prinzip: je Granularität Tag/Woche/Monat wird pro Zeitfenster die NEUESTE Generation behalten, bis die konfigurierte Fensteranzahl erreicht ist) — ohne Dateisystemzugriff testbar, PruneRotate führt die tatsächliche Löschung anhand des Ergebnisses aus (analog zu BAK-01s bestehendem Rotate).

Beide Policies sind über Umgebungsvariablen konfigurierbar (NEXARCH_BACKUP_KEEP_DAILY/WEEKLY/MONTHLY, NEXARCH_OBJECTBACKUP_KEEP_DAILY/WEEKLY/MONTHLY), nicht hart codiert (Ticket-Vorgabe). Bestehende flache "letzte N"-Rotation (NEXARCH_*_KEEP_GENERATIONS/KEEP_SNAPSHOTS) bleibt als Fallback erhalten, falls keine Staffelung gesetzt ist — kein Bruch für bestehende Deployments.

internal/backup.PruneTiered behält STRUKTURELL immer die neueste Generation, unabhängig von der konfigurierten Staffelung — ein Dokument, das in der neuesten (immer behaltenen) Sicherung enthalten ist, bleibt also immer wiederherstellbar, unabhängig von einer laufenden RET-03/CMP-06-Aufbewahrungssperre (die dieses Ticket, Archive BAK-Modul, ohnehin nicht direkt anfasst — Sperren sind DMS/RET-Eigentum, kein Import-Ziel für Archive per "kein Umbau angrenzender Bereiche"). Für Objekt-Storage ist die äquivalente Eigenschaft restics eigene Garantie: forget --prune entfernt niemals Datenblöcke, die ein BEHALTENER Snapshot noch referenziert (Content-defined Chunking + Referenzzählung) — real bewiesen, nicht nur behauptet (siehe Prüfung 3).

Umsetzung

  • internal/backup.RetentionPolicy/PruneTiered (retention.go) — reine GFS-Funktion.
  • internal/backup.PruneRotate (rotate.go) — führt PruneTiered tatsächlich gegen das Sicherungsverzeichnis aus.
  • internal/objectbackup.RetentionPolicy/PruneTiered (restic.go) — Wrapper um restics native Staffelung.
  • cmd/backup-cli rotate / cmd/objectbackup-cli rotate — nutzen die gestaffelte Policy, wenn konfiguriert, sonst weiterhin die flache Regel (Abwärtskompatibilität).

Prüfungen

# Prüfung Ergebnis
1 Prune-Test mit simulierten Snapshots über mehrere Altersstufen ergibt korrekte Staffelung bestandenTestPruneTiered_KeepsCorrectStaggering (Datenbank, reine Funktion, simulierte Generationen über Tage/Wochen/Monate) UND TestPruneTiered_KeepsCorrectStaggering_RealSnapshots (Objekt-Storage, ECHTE restic-Snapshots mit erzwungenem Zeitstempel restic backup --time, KeepDaily=3 reduziert 10 reale Snapshots korrekt auf 3)
2 Automatisierte Löschung abgelaufener Snapshots im Test nachgewiesen bestandenTestPruneRotate_DeletesOnlyExpiredGenerations: reales Verzeichnis, abgelaufene Generation tatsächlich vom Dateisystem entfernt, aktuelle unangetastet; real auf 131: backup-cli rotate und objectbackup-cli rotate über systemd ausgelöst, Journal zeigt „gestaffelte rotation abgeschlossen“
3 Legal-Hold-Test bestätigt, dass Pruning nicht mit Dokumenten-Retention kollidiert bestandenTestPruneTiered_AlwaysKeepsNewest (Datenbank-Sicherheitsnetz: neueste Generation immer behalten, selbst bei Policy 0/0/0) UND TestPruneTiered_KeptSnapshotStillFullyRestorable (Objekt-Storage, real: 5 ältere Snapshots eines Dokuments weggeprunt, verbleibender Snapshot danach vollständig und inhaltlich korrekt wiederhergestellt, zusätzlich restic check --read-data bestätigt Datenintegrität nach dem Pruning)

Zusätzlich: TestPruneTiered_DeterministicForIdenticalInput.

Echte Verdrahtung auf 192.168.1.131

  • backup-cli, objectbackup-cli neu gebaut mit der gestaffelten Rotations-Logik
  • /etc/nexarch/archive-backup.env und /etc/nexarch/archive-objectbackup.env um KEEP_DAILY=14/KEEP_WEEKLY=12/KEEP_MONTHLY=12 ergänzt
  • Beide *-rotate.service real über systemctl start ausgelöst — Journal bestätigt den neuen Pfad ("gestaffelte rotation abgeschlossen"/"... entfernt: [...]"), nicht mehr die alte flache Regel

Build/Test-Ergebnis (192.168.1.131, make check)

go build ./...  -> clean
go vet ./...    -> clean
golangci-lint run ./...  -> 0 issues
go test ./... -p 1 -count=1  -> 7/7 Pakete mit Tests ok, 0 Fehlschläge

Bekannte Lücke: keine Kohärenz zwischen DB- und Objekt-Rotation

Beide Rotationswege laufen als UNABHÄNGIGE systemd-Timer mit eigener Kadenz und eigener Policy-Auswertung — nichts stellt sicher, dass der älteste noch erreichbare restic-Snapshot und die älteste noch erreichbare DB-Generation denselben Zeitpunkt abdecken. Die aktuelle Konfiguration (KEEP_DAILY=14/KEEP_WEEKLY=12/KEEP_MONTHLY=12 auf beiden Seiten identisch) ist eine ZUFÄLLIGE Übereinstimmung, kein getestetes oder erzwungenes Invariant — ein restic-Snapshot ohne zeitlich passende DB-Generation wäre ein inkonsistenter Restore-Punkt (Objekte ohne zugehörige file_revisions-Metadaten oder umgekehrt). Nicht Teil dieses Tickets (BAK-07 fordert nur je Sicherungsart eine konfigurierbare Staffelung, kein Cross-Artefakt-Alignment) — als dokumentierter Folgepunkt festgehalten, nicht stillschweigend übergangen: ein künftiges Ticket müsste entweder die Policies fest koppeln oder bei Restore-Zeitpunkt-Wahl (BAK-03) explizit warnen, wenn kein zeitlich passendes Gegenstück mehr existiert.

Gesamtergebnis

Bestanden. Alle drei Akzeptanzkriterien und alle drei Pflichtprüfungen real erfüllt — sowohl mit simulierten (Datenbank) als auch mit tatsächlich zeitversetzt erzeugten, realen restic-Snapshots (Objekt-Storage). Ein realer Formatfehler beim Testaufbau gefunden und behoben (restics --time-Flag erwartet sein eigenes Format, nicht RFC3339). Beide Rotations-Wege real über systemd auf 192.168.1.131 ausgelöst, nicht nur isolierter Testcode.