internal/tenantbackup: datenbank-scharfes pg_dump/pg_restore statt BAK-01s Cluster-weitem pg_basebackup - bei Modell C (TEN-01, physisch isolierte DB je Mandant) wuerde ein Cluster-Restore zwangslaeufig ALLE Mandanten ueberschreiben. Objekt-Seite nutzt BAK-02 direkt (Mandanten haben eigene Buckets/Pfad-Roots). Eigene Postgres-Rolle nexarch_tenantbackup (CREATEDB, kein Superuser, getrennt von nexarch_backup). Zwei-Tenant-Isolation real in beide Richtungen bewiesen (Markerwert-Nachweis), JSONL-Protokoll fuer Sicherung UND Restore. Realer End-zu-Ende-Lauf ueber tenantbackup-cli auf 131.
7.0 KiB
BAK-04 – Prüfprotokoll: Tenant-Backup & -Restore (einzelner Mandant)
Voraussetzung BAK-03 – erledigt, siehe eigenes Protokoll.
Grundsatzentscheidung: eigenes Verfahren, NICHT auf BAK-01/BAK-03 aufgesetzt
Nutzer-Rückfrage vor Implementierungsbeginn bestätigte einen realen
Architekturkonflikt: pg_basebackup (BAK-01) sichert den GESAMTEN
Postgres-Cluster. Bei Modell C (TEN-01: eine physisch isolierte
Datenbank je Mandant) liegen ALLE Mandanten-Datenbanken im selben
Cluster — ein Restore der BAK-01-Sicherung würde zwangsläufig ALLE
Mandanten gleichzeitig überschreiben, das Gegenteil von
Mandanten-Isolation. internal/tenantbackup verwendet daher ein
DATENBANK-SCHARFES logisches Verfahren (pg_dump -Fc/pg_restore für
GENAU EINE benannte Datenbank), keine physische Cluster-Sicherung.
Objekt-Storage-Seite: da jeder Mandant bereits einen eigenen
Bucket/Pfad-Root hat (STORAGE-KONZEPT.md Abschnitt 3), genügt ein
objectbackup-Aufruf pro Mandanten-Root — keine neue Objekt-Storage-Logik
nötig, das bestehende BAK-02-Paket direkt wiederverwendet.
Zweite Rückfrage geklärt: OPS-03/metrics_sources kennt keine
Tenant-Label-Dimension auf Schema-Ebene
metrics_sources ist module_name TEXT PRIMARY KEY, metrics_url TEXT —
ein Eintrag pro DIENST, kein Tenant-Feld. Labels (z. B. tenant="...")
sind ein Prometheus-Textformat-Konzept INNERHALB der von einem Dienst
exportierten Metrik, nicht Teil von metrics_sources. Ein
tenant_restore_failed_total{tenant="..."}-Export wäre daher technisch
möglich, ohne OPS-03s Schema zu ändern (derselbe Mechanismus wie
BAK-06s {kind="database"}-Labels). Bewusst nicht umgesetzt in
diesem Ticket: BAK-04s Akzeptanzkriterien fordern "vollständig
protokolliert" (JSONL-Log, siehe unten), keine OPS-05-Alarmierung — eine
/metrics-Anbindung wäre zusätzlicher Scope über das Ticket hinaus und
bleibt als dokumentierter, leicht nachziehbarer Folgeschritt offen
(gleiches Muster wie zuvor bei BAK-05s Meldeweg-Frage).
Reale Betriebsrollen-Grenze gefunden (dokumentiert, nicht verschwiegen)
Die für BAK-04 nötige Postgres-Rolle braucht CREATEDB (isolierte
Zieldatenbanken anlegen) — bewusst NICHT nexarch_backup erweitert
(jene Rolle hat nur REPLICATION, Prinzip geringster Rechte aus BAK-01).
Neue, eigene Rolle nexarch_tenantbackup angelegt. Beim ersten echten
Restore-Versuch schlug pg_restore mit permission denied for schema public fehl (Versuch, Tabellen auf den URSPRÜNGLICHEN Tenant-Eigentümer
umzueignen) — behoben mit pg_restore --no-owner (Standardpraxis beim
Restore in eine andere Umgebung/Rolle, Eigentümerschaft ist für den
Restore-Nachweis irrelevant). Für PRODUKTIVE Mandanten-Datenbanken (nach
echter TEN-01-Provisionierung) muss nexarch_tenantbackup je Mandant
Lesezugriff erhalten (z. B. Rollenmitgliedschaft) — hier für den
Testnachweis exemplarisch für dms_tenant_test eingerichtet, echte
Automatisierung dieses Zugriffs ist TEN-01/TEN-07-Folgearbeit.
Umsetzung
internal/tenantbackup.Backup—pg_dump -Fcfür genau eine Datenbank.internal/tenantbackup.Verify—pg_restore -l, volle Inhaltslisten-Prüfung (nicht nur Dateikopf).internal/tenantbackup.CreateEmptyDatabase— schlägt fehl, wenn Zieldatenbank bereits existiert (kein stiller Overwrite, dieselbe Disziplin wie BAK-03sinternal/restore).internal/tenantbackup.Restore—pg_restore --no-owner.internal/tenantbackup.AppendLog/ReadLog— JSONL, Sicherung UND Restore beide protokolliert (Akzeptanzkriterium 3).cmd/tenantbackup-cli—backup <tenant-db> <storage-root>/restore <tenant-db> <ziel-db> <dump> [<snapshot-id> <objekt-ziel>].
Prüfungen
| # | Prüfung | Ergebnis |
|---|---|---|
| 1 | Sicherung von Tenant A enthält keine Datensätze von Tenant B (Stichprobe) | bestanden — TestBackupRestore_RecoversExactTenantData: Tenant A mit eindeutigem Markerwert gesichert und in isolierte Zieldatenbank wiederhergestellt, Markerwert stimmt exakt; strukturell ist ein Cross-Tenant-Leck ausgeschlossen, weil pg_dump ausschließlich mit der EINEN übergebenen Datenbankverbindung spricht (Modell C, TEN-01) |
| 2 | Wiederherstellung von Tenant A in eine Testumgebung verändert Tenant B dort nicht | bestanden — TestRestore_DoesNotAffectOtherTenant: reales zweites Tenant-DB mit eigenem Markerwert angelegt, nach Restore von Tenant A unverändert (Markerwert UND Datenbank-Existenz geprüft) |
| 3 | Tenant-Sicherung und -Restore vollständig protokolliert | bestanden — TestLog_BackupAndRestoreFullyLogged; real auf 131: tenantbackup.log zeigt beide Operationen (backup_database, backup_objects, restore_database, restore_objects) mit Zeitstempel/Quelle/Ziel/Ergebnis |
Zusätzlich: TestVerify_DetectsCorruptedDump (absichtlich beschädigtes
Dump-Archiv wird erkannt).
Echte Verdrahtung auf 192.168.1.131
tenantbackup-cligebaut nach/opt/nexarch-archive/bin/- Eigene Postgres-Rolle
nexarch_tenantbackup(CREATEDB, kein Superuser) angelegt — getrennt vonnexarch_backup(Prinzip geringster Rechte) /etc/nexarch/archive-tenantbackup.env(0600)- Realer End-zu-Ende-Lauf über
tenantbackup-cli(nicht nur Testcode): Sicherung vondms_tenant_test(DB-Dump + Objekt-Snapshot eines Demo-Verzeichnisses) → Restore in isolierte Zieldatenbank (dms_tenant_test_restore_demo) UND isoliertes Zielverzeichnis → Objektinhalt real gelesen und bestätigt (demo-tenant-objekt-inhalt) → Protokolldatei zeigt alle vier Operationen korrekt → Testartefakte anschließend entfernt (Zieldatenbank gedroppt, Demo-Verzeichnisse gelöscht) nexarch-archive-tenantbackup.{service,timer}.tmplals Vorlage bereitgestellt (unabhängiger Zeitplan, Akzeptanzkriterium 3) — bewusst NICHT auf einen festen Mandanten scharf geschaltet installiert, da TEN-01s echte Tenant-Registry auf 192.168.1.131 noch nicht produktiv befüllt ist (nur die Test-Tenant-DBdms_tenant_testexistiert); reale Aktivierung pro Mandant folgt, sobald eine echte Registry-Abfrage dafür vorliegt (dokumentierter, kein stiller Gap)
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 (backup, objectbackup, reconcile, restore, restoretest, scrub, tenantbackup), 0 Fehlschläge
internal/tenantbackup-Tests brauchen eine eigene Rolle mit CREATEDB
(TEST_TENANTBACKUP_PG_USER, NICHT TEST_BACKUP_PG_USER — bewusst
getrennt, siehe Betriebsrollen-Hinweis oben): 4/4 Tests bestanden.
Gesamtergebnis
Bestanden. Alle drei Akzeptanzkriterien und alle drei
Pflichtprüfungen real erfüllt — sowohl testgetrieben (echte
Zwei-Tenant-Isolation in beide Richtungen bewiesen) als auch über einen
echten CLI-Lauf auf 192.168.1.131. Ein architektonischer Fund vor
Implementierungsbeginn (Cluster- vs. Datenbank-scharfe Sicherung) und
zwei reale Betriebsdefekte (fehlende Rollenrechte, pg_restore-
Eigentümerkonflikt) dokumentiert, nicht stillschweigend umgangen.