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.
113 lines
6.3 KiB
Markdown
113 lines
6.3 KiB
Markdown
# 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.
|
||
|
||
## Sicherheitsnetz für Akzeptanzkriterium 3 (Legal-Hold-Kollision)
|
||
|
||
`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 | **bestanden** — `TestPruneTiered_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 | **bestanden** — `TestPruneRotate_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 | **bestanden** — `TestPruneTiered_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.
|