Files
nexarch/archive/docs/BAK-07-PRUEFPROTOKOLL.md
T
sysops 823a14ae12 feat(archive): BAK-07 gestaffelte Aufbewahrungsfrist fuer Backup-Snapshots
internal/backup.PruneTiered: reine GFS-Funktion (Tag/Woche/Monat) fuer
Datenbank-Generationen, behaelt strukturell immer die neueste Generation
(Sicherheitsnetz gegen Legal-Hold-Kollision). internal/objectbackup.
PruneTiered: duenner Wrapper um restics native --keep-daily/-weekly/
-monthly-Staffelung. Beide CLIs nutzen die Staffelung, wenn konfiguriert,
bleiben sonst abwaertskompatibel zur flachen "letzte N"-Regel. Real
gegen zeitversetzt erzeugte restic-Snapshots getestet (Fund: restics
--time-Flag erwartet eigenes Format, nicht RFC3339), Pruning-Sicherheit
nach dem Loeschen alter Snapshots ueber vollstaendigen Restore +
restic check --read-data bewiesen. Beide Rotationswege real ueber
systemd auf 131 ausgeloest.
2026-08-30 00:49:23 +02:00

5.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

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.