Skip to content

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

SpalteVorherNachher
shared_host_idtenant_box_registry + server_ordersnur server_orders
synapse_porttenant_box_registry + server_ordersnur server_orders
minio_porttenant_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:

  1. server_orders.minio_port anlegen.
  2. Backfill aus tenant_box_registry.minio_port (nur wo NULL).
  3. Drift-Detection: falls noch Werte abweichen — NOTICE mit Liste; server_orders gewinnt (war immer Truth).
  4. Partial UNIQUE auf server_orders (shared_host_id, synapse_port) und (shared_host_id, minio_port).
  5. Alte Indizes auf tenant_box_registry droppen.
  6. Spalten shared_host_id, synapse_port, minio_port aus tenant_box_registry droppen.

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.ts
  • src/services/tenant-box-drift.service.ts (2 Stellen)
  • src/services/tenant-box-rollout-engine.service.ts (alle 6 SELECT-Stellen via SELECT_COLS-Konstante)

Port-Allocation → direkt aus server_orders

  • tenant-box.service.ts::getNextSynapsePort — nur noch prisma.serverOrder.findMany
  • tenant-box.service.ts::getNextMinioPort — analog

Schreib-Stellen vereinfacht

  • createTenantBox + importTenantBoxFromShared: INSERT in tenant_box_registry ohne die 3 droppingten Spalten; server_orders.minioPort separat per Prisma gesetzt.
  • destroyTenantBox: DELETE WHERE slug (kein hostId-Match mehr nötig).
  • tenant-migration.service.ts: Drift-Fix-Block 6b' entfernt — überflüssig, weil Drift physisch ausgeschlossen.

Was passiert uns nicht mehr

SzenarioVorherMit 0132
Tenant-Migration zwischen HostsDrift möglich (L-014)unmöglich
Manueller psql-Hotfix auf server_ordersApp-Code wird umgangensync-frei
Künftige neue Migration-PfadeEntwickler muss ans Sync denkenkein Sync nötig
Rollback einer Migrationzweiter Schritt nötigatomar
Race-Condition zwischen den zwei UPDATEstheoretisch möglichein Statement

Was 0132 nicht löst

  • Drift zwischen server_orders und 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.