Troubleshooting
Wenn in Produktion etwas schiefläuft, ist diese Seite der Startpunkt. Jeder Abschnitt ist ein Symptom, die wahrscheinliche(n) Ursache(n) und was zu prüfen / loggen / messen ist, um die Diagnose zu bestätigen.
Für die Diagnose-Werkzeuge selbst:
- Logging — strukturierte Logs.
- Stock-Metriken — eingebaute Actor- / Cluster-Metriken.
- Tracing — Per-Request-Flow.
Cluster
Abschnitt betitelt „Cluster“Symptom: Cluster bildet sich nicht
Abschnitt betitelt „Symptom: Cluster bildet sich nicht“Pods starten, erreichen aber nie Up.
Ursachen zum Prüfen (in dieser Reihenfolge):
- Seeds unerreichbar — falsche Adressen / DNS / RBAC.
- Cluster-Port via Firewall blockiert — Pods können auf 2552 nicht reden.
- Unterschiedliche System-Namen —
ActorSystem.create('app-a')auf einem Node,'app-b'auf einem anderen. - TLS-Fehlkonfiguration — Handshake scheitert lautlos.
Diagnostik:
cluster.subscribe(MemberJoined, (e) => system.log.info(`saw join: ${e.member.address}`));cluster.subscribe(SelfUp, () => system.log.info('self up'));Prüfe, ob SelfUp feuert. Wenn nicht, hat der lokale Node nicht
einmal gebootstrappt. Schau dir die Ausgabe des Seed-Providers an:
kubectl logs pod-1 | grep -i seed# → "discovered N seeds: ..." ← darf nicht leer seinWenn leer: Selector / DNS / Config des Seed-Providers ist falsch.
Symptom: Cluster flappt
Abschnitt betitelt „Symptom: Cluster flappt“Mitglieder schwanken zwischen reachable und unreachable.
Ursachen:
- Zu enge Failure-Detector-Schwellen für den Jitter des Netzwerks. Siehe Failure-Detector-Tuning.
- Netzwerk-Partitionen (echte — diagnostiziere auf Infrastruktur-Ebene).
- GC-Pausen länger als die Unreachable-Schwelle.
Diagnostik:
rate(cluster_unreachable_duration_ms_count[5m]) > 0.1# Häufige ÜbergängeIn Logs:
[INFO ] cluster — node-X marked unreachable[INFO ] cluster — node-X marked reachable[INFO ] cluster — node-X marked unreachableAnhaltendes Flapping signalisiert Schwellen-Tuning.
Sharding
Abschnitt betitelt „Sharding“Symptom: Sharded Entities spawnen nicht
Abschnitt betitelt „Symptom: Sharded Entities spawnen nicht“Nachrichten an eine Sharding-Region erreichen keine Entities.
Ursachen:
- Coordinator noch nicht gestartet — Sharding ist asynchron; vor Coordinator-Bereitschaft gesendete Nachrichten werden gepuffert.
- Keine Nodes matchen die
role— leeres Up-Member-Set, also werden keine Shards allokiert. - extractEntityId liefert undefined — das Message-Routing hat nichts zum Hashen.
Diagnostik:
sharding_shards_hosted{type="entity-type"}# Sollte numShards (gesamt) clusterweit entsprechenLogs:
kubectl logs pod-1 | grep -i shard# → "coordinator: allocating shard X to node-Y"Siehst du “no candidates”, lehnt dein role-Filter jeden Node ab.
Prüfe Role-Tags in Cluster.join.
Symptom: Rebalance-Storm
Abschnitt betitelt „Symptom: Rebalance-Storm“Sharding-Metriken zeigen ständige Rebalances; Entities flackern.
Ursachen:
- Aggressive Allocation-Strategy —
LeastShardAllocationStrategymitrebalanceThreshold: 1und häufigen Mitgliedschaftsänderungen. - Flappender Cluster (siehe oben) — jede Änderung triggert Rebalance.
Fix:
new LeastShardAllocationStrategy(/* threshold */ 5, /* max */ 3);Höhere Schwelle = weniger Empfindlichkeit gegenüber kleinen Ungleichgewichten.
Persistence
Abschnitt betitelt „Persistence“Symptom: Actor braucht 30 Sekunden zum Starten
Abschnitt betitelt „Symptom: Actor braucht 30 Sekunden zum Starten“preStart braucht lange; das erste onReceive des Actors
verzögert sich.
Ursache: tiefes Journal ohne Snapshots. Recovery liest jedes jemals gespeicherte Event.
Diagnostik:
histogram_quantile(0.99, persistence_recovery_duration_ms_bucket)Wenn P99-Recovery > 1 Sekunde ist, setze eine Snapshot-Policy:
override snapshotPolicy() { return everyNEvents(100); }Siehe Snapshots.
Symptom: Events wiederhergestellt, aber State ist falsch
Abschnitt betitelt „Symptom: Events wiederhergestellt, aber State ist falsch“Nach Restart entspricht der State des Actors nicht dem, was er laut Journal sein sollte.
Ursachen:
onEventhat einen Seiteneffekt — läuft während des Replays und verändert irgendwie den State-Pfad.onEventnutztDate.now()oder Zufall — nicht-deterministisch; jeder Replay produziert anderen State.- Schema-Änderung ohne Adapter — alte Events haben eine andere
Form als
onEventerwartet.
Diagnostik: Vergleiche den State des Actors nach Recovery mit dem Inhalt des Journals. Replaye manuell in einem Test, um die Ursache zu isolieren.
Speicher + Performance
Abschnitt betitelt „Speicher + Performance“Symptom: Speicher wächst unbegrenzt
Abschnitt betitelt „Symptom: Speicher wächst unbegrenzt“Heap wächst linear mit Uptime; irgendwann OOM.
Ursachen (häufigste zuerst):
- Unbounded Mailboxes mit langsamem Consumer. Die Mailbox-Depth-Metrik wächst.
- Subscriber-Set-Leaks — Actors registrieren sich am Event-Stream oder DistributedPubSub, aber unsubscriben sich beim Stop nicht.
- DistributedData-Keys sammeln sich —
LWWMapmit Millionen Keys. - Persistente Puffer — Stash-Puffer, Ask-Reply-To-Refs.
Diagnostik:
actor_mailbox_size{class=...}# Finde Actors mit dauerhaft großen Queues# Heap-Dump über Runtime-Tools:node --inspect / Buns ProfilerPrüfe Mailbox-Sizing + die Leak-Muster in Event-Stream.
Symptom: HTTP-Latenz unter Last hoch
Abschnitt betitelt „Symptom: HTTP-Latenz unter Last hoch“Actor-Arbeit ist okay; HTTP-Antworten sind langsam.
Ursache: Ein Actor monopolisiert die Event Loop und hungert HTTP-Handler aus.
Fix: Per-Actor ThroughputDispatcher auf den schweren Actor.
Tests + Dev
Abschnitt betitelt „Tests + Dev“Symptom: Tests hängen am Ende
Abschnitt betitelt „Symptom: Tests hängen am Ende“Bun- / Vitest-Testprozess beendet sich nicht.
Ursache: await system.terminate() wurde im Fixture-Teardown nicht
aufgerufen. Geleakte Scheduler / Actor-Cells halten die Event Loop
am Leben.
Fix:
afterEach(async () => { await tk.shutdown();});Siehe TestKit.
Symptom: Flaky timing-sensitive Tests
Abschnitt betitelt „Symptom: Flaky timing-sensitive Tests“Tests sind lokal grün, fallen aber in CI um.
Ursache: Real-Clock-Timing in Tests. CI-Maschinen sind langsamer / anders.
Fix: Nutze ManualScheduler und steuere Zeit deterministisch.
Womit anfangen, wenn nichts offensichtlich ist
Abschnitt betitelt „Womit anfangen, wenn nichts offensichtlich ist“1. Logs auf ERROR-Level-Einträge prüfen. Nach Zeitfenster um das Problem filtern.2. Stock-Metriken prüfen — was hat sich verändert? (Restart-Rate, Mailbox-Tiefe, Member-Anzahl).3. Cluster-Events am Event-Stream prüfen.4. Wenn ein Request scheitert, der Trace-ID durch Logs / Trace-Backend folgen.5. Wenn möglich, in einem Multi-Node-Spec-Test reproduzieren.Wohin als nächstes
Abschnitt betitelt „Wohin als nächstes“- Operations-Überblick — die breitere Produktions-Checkliste.
- FAQ — häufige Fragen und Stolperfallen.
- Stock-Metriken — was zu lesen ist, wenn Symptome auftauchen.
- Logging — wie Logs tatsächlich nützlich werden.
- Tracing — Per-Request-Flow, wenn Logs nicht reichen.