Drift-Management — Wie Prilog DB und Matrix synchron haelt
Was ist Drift?
Prilog speichert den Soll-Zustand in einer PostgreSQL-Datenbank (Spaces, Mitgliedschaften, Rollen). Matrix/Synapse speichert den Ist-Zustand (Raeume, Mitglieder, Power-Levels). Drift entsteht, wenn beide nicht mehr uebereinstimmen.
Beispiel: Die Prilog-DB sagt "User X ist Mitglied von Space Y", aber in Synapse ist der User nicht im entsprechenden Matrix-Raum.
Wie Drift entsteht
1. Synapse war kurzzeitig nicht erreichbar
Die haeufigste Ursache. Wenn ein User einem Space hinzugefuegt wird, schreibt Prilog in die DB und schickt einen API-Call an Synapse. Wenn Synapse gerade restartet, ueberlastet oder nicht erreichbar ist, schlaegt der Matrix-Call fehl — die DB ist aktualisiert, Matrix nicht.
Typische Ausloeser:
- Synapse-Container-Restart (Docker-Update, OOM)
- Netzwerk-Unterbrechung zwischen Backend und Synapse
- Synapse Rate-Limit bei Massen-Operationen (Bulk-Import von 25 Schuelern)
2. Backend-API wurde restartet waehrend einer Operation
Wenn das Backend mitten in einem Space-Sync restartet wird (z.B. wegen Deployment), kann der DB-Write durchgehen aber der Synapse-Call nicht mehr.
3. Timeout bei grossen Operationen
Bulk-Operationen (viele User auf einmal einem Space zuweisen) koennen laenger dauern als der HTTP-Timeout. Die ersten 15 Memberships werden synchronisiert, dann bricht der Request ab — die restlichen bleiben unsynchronisiert.
4. Matrix-Raum existiert nicht (mehr)
Selten, aber moeglich: Ein Synapse-Admin hat einen Raum manuell geloescht, oder die Raum-Erstellung beim Space-Anlegen ist fehlgeschlagen (z.B. weil das Synapse-Volumen voll war).
5. Power-Level-Konflikt
Wenn ein Space zwischen CHAT und INFOTAFEL umgeschaltet wird, muessen die Matrix Power-Levels angepasst werden. Wenn dabei ein Fehler passiert, stimmen die Berechtigungen nicht mehr.
3-Stufen-Abwehr
Prilog setzt auf drei aufeinander aufbauende Schutzstufen:
Aenderung in Prilog-DB
│
▼
┌─────────────────────────────────────────┐
│ Stufe 1: Auto-Sync (sofort) │
│ Bei jeder Mitgliedschafts-Aenderung │
│ wird synchron ein Synapse-Call gemacht. │
│ Funktioniert in 99% der Faelle. │
│ │
│ Bei Fehler: Space wird als 'pending' │
│ markiert → Stufe 2 greift. │
└────────────────┬────────────────────────┘
│ Wenn Auto-Sync fehlschlaegt
▼
┌─────────────────────────────────────────┐
│ Stufe 2: Naechtlicher Drift-Scan │
│ Cron-Job um 02:00 — prueft ALLE Spaces │
│ und Memberships gegen Synapse. │
│ │
│ Bei Drift: automatische Reparatur. │
│ Ergebnis im Cron-Log + Portal. │
│ │
│ Wenn Repair fehlschlaegt: bleibt │
│ 'drifted' → Stufe 3 greift. │
└────────────────┬────────────────────────┘
│ Wenn Auto-Repair fehlschlaegt
▼
┌─────────────────────────────────────────┐
│ Stufe 3: Health-Monitor + Alert │
│ Alle 15 Minuten: prueft ob Drift │
│ nach Repair noch besteht. │
│ │
│ Bei verbleibendem Drift: │
│ → Alert-Mail an Admin │
│ → Sichtbar im Portal unter "Wartung" │
│ → Manueller "Reparieren"-Button │
└─────────────────────────────────────────┘Stufe 1: Auto-Sync (sofort, bei jeder Aenderung)
Jede Mitgliedschafts-Aenderung (Hinzufuegen, Entfernen, Rolle aendern) loest einen synchronen Synapse-Call aus. Das Backend wartet auf die Antwort und markiert den Sync-Status:
synced— Matrix-State stimmt mit DB uebereinpending— Matrix-Call fehlgeschlagen, wird beim naechsten Scan repariertfailed— Wiederholter Fehler
Code: customer-sync.service.ts → runSpaceSync(), runMembershipSync()
Stufe 2: Naechtlicher Drift-Scan mit Auto-Repair
Cron-Job space-drift-scan (taeglich 02:00). Geht ueber alle aktiven Spaces aller Tenants und vergleicht den DB-Zustand mit dem Matrix-Zustand.
Drei Repair-Szenarien:
| Drift-Typ | Erkennung | Auto-Repair |
|---|---|---|
| Space ohne Matrix-Raum | matrixRoomId = null | initializeSpaceInMatrix() erstellt den Raum |
| Space-Zustand weicht ab | runSpaceSync() meldet drifted | Erneuter runSpaceSync() |
| Membership fehlt in Matrix | runMembershipSync() meldet drifted | Erneuter runMembershipSync() |
Code: space-drift-scan.ts → runDriftScan()
Cron-Log Beispiel:
Drift-Scan: 24 Spaces (22 ok, 1 drifted, 0 failed, 1 auto-repariert),
156 Memberships (2 drifted, 2 auto-repariert)Stufe 3: Health-Monitor (alle 15 Minuten)
Der Health-Monitor-Cron prueft:
- Agent-Verbindung — ist der Server-Agent online?
- Synapse-Erreichbarkeit — antwortet
/_matrix/client/v3/login? - Verbleibender Drift — gibt es Spaces mit
syncStatus = drifted/failed?
Bei Problemen: Alert-Mail an den Admin mit Problemliste.
Code: core/cron/jobs.ts → system-health-monitor
Was der Admin sieht
Portal: Wartungs-Seite
Unter Benutzerkonten → Wartung sieht der Tenant-Admin alle Spaces mit Drift. Pro Space gibt es einen "Reparieren"-Button der die Synchronisation manuell anstosst.
Im Normalfall ist diese Liste leer — Stufe 1 und 2 raeumen den Drift automatisch weg.
Portal: Systemstatus
Unter Benutzerkonten → Systemstatus zeigt der Health-Check den Gesamtzustand inkl. Sync-Status aller Spaces.
Admin-Panel: admin.prilog.chat
GET /admin/health zeigt pro Server:
- Agent-Status (verbunden/getrennt)
- Synapse-Status (erreichbar/nicht erreichbar)
- Drift-Count (Anzahl Spaces mit Problemen)
Warum nicht einfach eine Queue?
Man koennte argumentieren: "Statt Drift zu reparieren, benutze eine Message-Queue (Redis/Bull) die fehlgeschlagene Syncs automatisch wiederholt."
Gruende dagegen (fuer jetzt):
- Komplexitaet: Eine Queue braucht Worker-Prozesse, Dead-Letter-Handling, Retry-Logik, Monitoring. Der Drift-Scan ist ~130 Zeilen und lauft als einfacher Cron.
- Seltenheit: Drift passiert vielleicht 1-2x pro Woche. Eine Queue die 99.9% der Zeit leer ist, ist Overhead.
- Idempotenz:
runSpaceSyncundrunMembershipSyncsind idempotent — man kann sie beliebig oft aufrufen. Das macht den Scan-und-Repair-Ansatz robust.
Wenn Drift haeufiger wird (>10 Faelle pro Tag), lohnt sich eine Queue. Bis dahin reicht der 3-Stufen-Ansatz.
Drift in Zahlen (Testinstanz weser, April 2026)
| Metrik | Wert |
|---|---|
| Spaces gesamt | 24 |
| Drift-Faelle pro Woche | ~2 |
| Davon auto-repariert | ~2 |
| Manuell repariert (Stufe 3) | 0 |
| Durchschnittliche Scan-Dauer | ~5s |