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.
5.4 KiB
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— rufenpg_basebackupüberos/execauf, Ergebnis landet in einer Generationsstruktur (<BackupDir>/<Generation>/full/bzw..../incremental/<ID>/).internal/backup.Verify— öffnetbase.tar.gzvollstä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,Rotateentfernt die ältesten bis aufkeepkomplett (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-cligebaut nach/opt/nexarch-archive/bin//etc/nexarch/archive-backup.envmit 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-timersbestätigt alle drei scharf) - Jeder der drei Dienste (
full/incremental/rotate) einmal manuell übersystemctl startausgelöst (nicht nurgo testdirekt) — alle drei mitstatus=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.