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.
124 lines
7.0 KiB
Markdown
124 lines
7.0 KiB
Markdown
# 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 <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-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.
|