Files
nexarch/archive/docs/BAK-04-PRUEFPROTOKOLL.md
T
sysops 80b0ca9176 feat(archive): BAK-04 Tenant-Backup & -Restore einzelner Mandant
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.
2026-08-30 00:41:48 +02:00

7.0 KiB
Raw Blame History

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.Backuppg_dump -Fc für genau eine Datenbank.
  • internal/tenantbackup.Verifypg_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-03s internal/restore).
  • internal/tenantbackup.Restorepg_restore --no-owner.
  • internal/tenantbackup.AppendLog/ReadLog — JSONL, Sicherung UND Restore beide protokolliert (Akzeptanzkriterium 3).
  • cmd/tenantbackup-clibackup <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) bestandenTestBackupRestore_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 bestandenTestRestore_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 bestandenTestLog_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-cli gebaut nach /opt/nexarch-archive/bin/
  • Eigene Postgres-Rolle nexarch_tenantbackup (CREATEDB, kein Superuser) angelegt — getrennt von nexarch_backup (Prinzip geringster Rechte)
  • /etc/nexarch/archive-tenantbackup.env (0600)
  • Realer End-zu-Ende-Lauf über tenantbackup-cli (nicht nur Testcode): Sicherung von dms_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}.tmpl als 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-DB dms_tenant_test existiert); 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.