tenant_box_registry: Drift physisch eliminiert (Option A)
Stand: 2026-06-01 — Migration 0132_tenant_box_registry_drift_eliminated LIVE
Vorbedingung: Manifest-Lesson L-014 (Backup-Stille shared-2, Mai 2026, 14 Tage)
Motivation
tenant_box_registry trug Kopien von shared_host_id + synapse_port, die schon in server_orders existierten. minio_port lebte ausschließlich in der Registry. Bei Tenant-Migrationen wurde die App-Logik nur an einer Stelle gepflegt — die Kopie in der Registry driftete. Der Backup-Cron las aus der Registry → falscher Host → 14 Tage stille Pannen (rssw + test-2 auf shared-2).
Lösung
| Spalte | Vorher | Nachher |
|---|---|---|
shared_host_id | tenant_box_registry + server_orders | nur server_orders |
synapse_port | tenant_box_registry + server_orders | nur server_orders |
minio_port | tenant_box_registry (einzig) | nur server_orders |
tenant_box_registry trägt seitdem nur noch box-spezifische Felder, die in server_orders keinen sinnvollen Platz haben:
tier(free/pro/enterprise)- Credentials:
pg_password_encrypted,minio_root_user,minio_root_password_encrypted,registration_secret_encrypted - Versionen:
synapse_version,postgres_version,minio_version - Backup-Status:
last_pre_update_snapshot,last_daily_backup_at,last_daily_backup_sha256 - Drift-Report:
drift_report,last_drift_check_at - Identität:
server_name,domain,public_baseurl
Migration (idempotent)
prisma/migrations/0132_tenant_box_registry_drift_eliminated/migration.sql:
server_orders.minio_portanlegen.- Backfill aus
tenant_box_registry.minio_port(nur wo NULL). - Drift-Detection: falls noch Werte abweichen —
NOTICEmit Liste;server_ordersgewinnt (war immer Truth). - Partial UNIQUE auf
server_orders (shared_host_id, synapse_port)und(shared_host_id, minio_port). - Alte Indizes auf
tenant_box_registrydroppen. - Spalten
shared_host_id,synapse_port,minio_portaustenant_box_registrydroppen.
Code-Änderungen
Lese-Stellen → JOIN auf server_orders
src/core/cron/jobs.ts(backup-cron, backup-stale-watch)src/services/tenant-box-version-sync.service.tssrc/services/tenant-box-drift.service.ts(2 Stellen)src/services/tenant-box-rollout-engine.service.ts(alle 6 SELECT-Stellen viaSELECT_COLS-Konstante)
Port-Allocation → direkt aus server_orders
tenant-box.service.ts::getNextSynapsePort— nur nochprisma.serverOrder.findManytenant-box.service.ts::getNextMinioPort— analog
Schreib-Stellen vereinfacht
createTenantBox+importTenantBoxFromShared: INSERT intenant_box_registryohne die 3 droppingten Spalten;server_orders.minioPortseparat per Prisma gesetzt.destroyTenantBox: DELETE WHERE slug (keinhostId-Match mehr nötig).tenant-migration.service.ts: Drift-Fix-Block 6b' entfernt — überflüssig, weil Drift physisch ausgeschlossen.
Was passiert uns nicht mehr
| Szenario | Vorher | Mit 0132 |
|---|---|---|
| Tenant-Migration zwischen Hosts | Drift möglich (L-014) | unmöglich |
Manueller psql-Hotfix auf server_orders | App-Code wird umgangen | sync-frei |
| Künftige neue Migration-Pfade | Entwickler muss ans Sync denken | kein Sync nötig |
| Rollback einer Migration | zweiter Schritt nötig | atomar |
| Race-Condition zwischen den zwei UPDATEs | theoretisch möglich | ein Statement |
Was 0132 nicht löst
- Drift zwischen
server_ordersund der Realität auf der Box (Reconcile-Aufgabe). - Fehler im Backup-Code selbst (deshalb der
backup-stale-watch-Cron, unabhängig). - Stille Background-Operationen ohne Alerts — siehe Manifest-Konsequenz.
Manifest-Konsequenz
L-014 wird ergänzt: „Drift physisch eliminiert" — wo eine Tabellen-Drift vermeidbar ist, sollte die redundante Spalte ganz weg, nicht durch Sync-Logik abgesichert. App-Sync ist Plan B, DB-Constraints sind Plan A.