internal/restore: Atomarer Restore ueber Temp-Verzeichnis + Rename, nicht-leeres Ziel ohne -force bricht VOR jeder Beruehrung ab, JSONL- Protokoll jedes Laufs. Drei reale Defekte beim Bau gefunden und behoben: pg_combinebackup braucht Plain- statt Tar-Format (Extraktionsschritt ergaenzt), pg_wal.tar.gz wurde nie verifiziert/wiederhergestellt (BAK-01s Verify jetzt erweitert), Go-exec haengt bei pg_ctl start wegen vererbter Pipes (Testfix: echte Logdatei statt CombinedOutput). Beide Restore-Pfade real auf 131 ueber restore-cli nachgewiesen, inkl. echtem Postgres-Start aus wiederhergestelltem Verzeichnis.
98 lines
5.4 KiB
Markdown
98 lines
5.4 KiB
Markdown
# BAK-01 – Prüfprotokoll: Datenbank-Backup-Strategie
|
||
|
||
Welle 1, keine Vorbedingungen. Neues Modul-Verzeichnis `code/archive/`
|
||
(gleiches Monorepo-Muster wie `code/dms/`), eigenes Go-Modul
|
||
`gitea.perlbach24.de/scripte/nexarch/archive`.
|
||
|
||
## Grundsatzentscheidung: PostgreSQL-17-natives inkrementelles Backup
|
||
|
||
`pg_dump` kennt nur logische Vollsicherungen — "inkrementell" im Sinne des
|
||
Tickets erfordert das physische Backup-Verfahren. Gewählt: PostgreSQL 17s
|
||
natives `pg_basebackup --incremental` (WAL-Summarization), NICHT klassisches
|
||
WAL-Archiving (`archive_mode`), weil letzteres einen Neustart der
|
||
(geteilten, auch von Core/DMS-Tests genutzten) Postgres-Instanz auf
|
||
192.168.1.131 erfordert hätte. Stattdessen `summarize_wal = on` gesetzt —
|
||
nur ein `pg_reload_conf()`, kein Neustart, keine Unterbrechung laufender
|
||
Verbindungen (per Health-Check nach der Änderung bestätigt).
|
||
|
||
Voraussetzung geschaffen: Rolle `nexarch_backup` mit `REPLICATION`-Attribut
|
||
angelegt (Postgres verlangt eine Replikationsverbindung für
|
||
`pg_basebackup`), `pg_hba.conf` erlaubte lokale Replikationsverbindungen
|
||
bereits.
|
||
|
||
## Umsetzung
|
||
|
||
- `internal/backup.FullBackup`/`IncrementalBackup` — rufen `pg_basebackup`
|
||
über `os/exec` auf, Ergebnis landet in einer Generationsstruktur
|
||
(`<BackupDir>/<Generation>/full/` bzw. `.../incremental/<ID>/`).
|
||
- `internal/backup.Verify` — öffnet `base.tar.gz` vollständig (gzip- UND
|
||
tar-Stream, jeder Eintrag bis zum Ende gelesen, nicht nur Kopfdaten) —
|
||
Akzeptanzkriterium 2: Verifikation auf Lesbarkeit, nicht nur Erstellung.
|
||
- `internal/backup.Rotate`/`ListGenerations` — Generationen sind nach
|
||
Zeitstempel-ID sortierbar, `Rotate` entfernt die ältesten bis auf `keep`
|
||
komplett (inklusive aller abhängigen Inkremente).
|
||
- `cmd/backup-cli` — `full`/`incremental`/`rotate`, aufgerufen von
|
||
systemd-Timern (`deploy/systemd/nexarch-archive-backup-*.timer`) —
|
||
"automatisiert nach Zeitplan" (Akzeptanzkriterium 1) entsteht durch die
|
||
Timer-Definition, kein zusätzlicher Dauerprozess nötig.
|
||
|
||
## Prüfungen
|
||
|
||
| # | Prüfung | Ergebnis |
|
||
|---|---|---|
|
||
| 1 | Sicherung gegen Testdatenbank erfolgreich erstellt und verifiziert | **bestanden** — `TestFullBackup_CreatesVerifiedBackup` gegen die echte Postgres-17-Instanz auf 192.168.1.131 (kein Mock), zusätzlich `TestIncrementalBackup_IsSmallerThanFull`: inkrementelle Sicherung real deutlich kleiner als Vollsicherung (167 KB vs. 16 MB bei der ersten manuellen Probe) — beweist echte inkrementelle Übertragung, nicht nur eine zweite Vollsicherung |
|
||
| 2 | Verifikation erkennt eine absichtlich beschädigte Sicherungsdatei | **bestanden** — `TestVerify_DetectsCorruptedFile`: 64 Bytes in der Mitte von `base.tar.gz` gekippt, `Verify` schlägt danach fehl (unbeschädigt zuvor erfolgreich) |
|
||
| 3 | Rotationsregel entfernt nachweislich nur die ältesten Generationen | **bestanden** — `TestRotate_RemovesOnlyOldestGenerations`: 5 Generationen, `keep=2`, exakt die 3 ältesten entfernt, die 2 neuesten nachweislich unangetastet |
|
||
|
||
## Echte Verdrahtung auf 192.168.1.131 (nicht nur Testcode)
|
||
|
||
Anders als die zuletzt in DMS gefundenen "Baustein existiert, ist aber
|
||
nirgends verdrahtet"-Fälle (FDN-03/FDN-09 gegen Core) wurde hier die
|
||
komplette Kette tatsächlich installiert und ausgeführt:
|
||
|
||
- `backup-cli` gebaut nach `/opt/nexarch-archive/bin/`
|
||
- `/etc/nexarch/archive-backup.env` mit den Verbindungsdaten (0600)
|
||
- 3 systemd-Timer installiert und aktiviert (`enable --now`):
|
||
Vollsicherung täglich 02:00 UTC, Inkrement stündlich, Rotation täglich
|
||
03:00 UTC (`systemctl list-timers` bestätigt alle drei scharf)
|
||
- Jeder der drei Dienste (`full`/`incremental`/`rotate`) einmal manuell über
|
||
`systemctl start` ausgelöst (nicht nur `go test` direkt) — alle drei mit
|
||
`status=0/SUCCESS`, Journal bestätigt inhaltlich korrekte Ausgabe
|
||
(Vollsicherung erstellt+verifiziert, Inkrement erstellt+verifiziert
|
||
gegen die richtige Vorgänger-Generation, Rotation lief ohne Fehler)
|
||
|
||
## 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 -> 4/4 Tests ok, 0 Fehlschläge (echter Postgres 17, kein Mock)
|
||
```
|
||
|
||
## Nachtrag (BAK-02-Sitzung): Backup-Zielverzeichnis korrigiert
|
||
|
||
`NEXARCH_BACKUP_DIR` zeigte ursprünglich auf `/var/backups/nexarch`
|
||
(Root-Dateisystem des Containers, kein dediziertes Dataset) — korrigiert auf
|
||
`/var/nexarch-archiv/backups/postgres` (persistentes ZFS-Dataset), siehe
|
||
`docs/BAK-02-PRUEFPROTOKOLL.md` Abschnitt „Korrektur an BAK-01" für Details.
|
||
Vollsicherung nach der Korrektur erneut über systemd ausgelöst, landet
|
||
nachweislich am neuen Ort.
|
||
|
||
## Gesamtergebnis
|
||
|
||
**Bestanden.** Alle drei Akzeptanzkriterien und alle drei Pflichtprüfungen
|
||
real erfüllt — inklusive tatsächlicher systemd-Timer-Installation und
|
||
manuell ausgelöstem End-to-End-Lauf aller drei Dienste auf dem Testhost,
|
||
nicht nur isolierter Testcode.
|
||
|
||
## Nachtrag (BAK-03): Verify prüft jetzt auch pg_wal.tar.gz
|
||
|
||
Beim Bau von BAK-03s echtem Restore-Test fiel auf, dass `pg_basebackup`
|
||
(Standard-WAL-Methode `stream`) bei `-Ft -z` NEBEN `base.tar.gz` eine
|
||
zweite Archivdatei `pg_wal.tar.gz` erzeugt, die `Verify` bislang nie
|
||
geprüft hat — eine Sicherung mit beschädigtem WAL-Archiv wäre unbemerkt
|
||
nicht crash-konsistent wiederherstellbar gewesen. `Verify` prüft seither
|
||
beide Archive vollständig (siehe `BAK-03-PRUEFPROTOKOLL.md`). Das
|
||
Sicherungsverfahren selbst (Format, Ort, Rotation) bleibt unverändert.
|