# 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 -Fc` fü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-03s `internal/restore`). - `internal/tenantbackup.Restore` — `pg_restore --no-owner`. - `internal/tenantbackup.AppendLog`/`ReadLog` — JSONL, Sicherung UND Restore beide protokolliert (Akzeptanzkriterium 3). - `cmd/tenantbackup-cli` — `backup ` / `restore [ ]`. ## 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-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.