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.
95 lines
5.3 KiB
Markdown
95 lines
5.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
|
||
```
|
||
|
||
## 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.
|