Failure Detector
Ein geclusterter Actor-System muss sich darauf einigen, wer lebt. Der Failure Detector ist die Peer-spezifische Zustandsmaschine, die entscheidet:
healthy— Heartbeats kommen normal an.unreachable— über dem Schwellwert; der Cluster vermeidet es, dorthin zu routen. Offiziell noch Mitglied; kann sich erholen.down— über dem längeren Schwellwert; der Cluster betrachtet den Peer als endgültig weg. Löst Downing aus.
Der Detector ist pro Peer — verschiedene Peers können gleichzeitig in verschiedenen Zuständen sein. Das Gesamtverhalten des Clusters (Routing, Sharding, Singleton-Wahl) liest diese Zustände, um zu entscheiden, was zu tun ist.
Die Defaults
Abschnitt betitelt „Die Defaults“{ heartbeatIntervalMs: 500, // alle 500ms einen Heartbeat senden unreachableAfterMs: 2_000, // nach 2s Stille als unreachable markieren downAfterMs: 5_000, // nach 5s Stille als down markieren}Für typische LAN-Cluster (1-10 Nodes, Sub-Millisekunden-Latenz) funktionieren diese Defaults gut. Sie ergeben:
- 2-Sekunden-Erkennungsfenster für “dieser Peer hat möglicherweise Probleme.”
- 5-Sekunden-Entscheidungsfenster für “dieser Peer ist endgültig weg.”
Überschreibe über das failureDetector-Feld in den
Cluster-Settings:
const clusterOptions = ClusterOptions.create() .withHost(host) .withPort(port) .withSeeds(seeds) .withFailureDetector({ heartbeatIntervalMs: 1_000, unreachableAfterMs: 5_000, downAfterMs: 15_000, });Cluster.join( system, clusterOptions,);Wann tunen:
| Workload | Richtung |
|---|---|
| Cluster über Regionen hinweg (hohe RTT) | Alle drei erhöhen. 100 ms RTT bedeuten, dass ein einzelner verpasster Heartbeat normales Rauschen ist. |
| Lokales Docker-Compose (Sub-ms RTT) | Verringern für schnellere Failover-Tests. Vor dem Deploy für Produktion wieder hochstellen. |
| Netzwerk mit periodischen Aussetzern | unreachableAfterMs erhöhen, um Fehlalarme zu vermeiden, aber downAfterMs größer halten, damit ein echter Ausfall trotzdem erkannt wird. |
| Kostensensitiv (chatty Heartbeats) | heartbeatIntervalMs erhöhen — bei 5s-Intervallen sind Gossip + Heartbeat < 1KB/s pro Peer. |
Das Verhältnis downAfterMs / unreachableAfterMs (Standard ~2.5x)
ist das Flap-Toleranzfenster: ein als unreachable markierter
Peer kann sich zurück auf healthy erholen, ohne downed zu werden,
solange ein Heartbeat innerhalb der Differenz eintrifft.
Heartbeats sind implizit
Abschnitt betitelt „Heartbeats sind implizit“Jeder Gossip-Austausch zählt als Heartbeat.Jede direkte Nachricht über den Cluster-Transport zählt ebenfalls.Das Framework schickt keine separaten “Ping”-Nachrichten — jeglicher Cluster-Verkehr von einem Peer setzt dessen Last-Seen-Timestamp zurück. Gossip ist die zuverlässigste Quelle (regelmäßiges Intervall), aber Anwendungsnachrichten tragen auch bei.
Das bedeutet: ein Cluster mit sehr gesprächigen Actors bekommt besser Failure Detection (mehr Heartbeats); ein leerlaufender Cluster ist ganz auf Gossip angewiesen.
Was der Detector entscheidet — und was nicht
Abschnitt betitelt „Was der Detector entscheidet — und was nicht“Der Detector gibt 'healthy' / 'unreachable' / 'down' zurück.
Was der Cluster damit macht:
| Entscheidung | Cluster-Verhalten |
|---|---|
healthy | Normales Routing. Kein Effekt. |
unreachable | Markiere das Mitglied in der Mitgliedertabelle als unreachable. Router überspringen es; Sharding allokiert keine neuen Shards mehr darauf. Singleton-Manager wählt keinen Leader von einer unerreichbaren Seite. |
down | Löse Downing aus. Wenn eine Downing-Strategie konfiguriert ist, entscheidet sie, welche Adressen zwangsweise entfernt werden; der Cluster verkündet diese Nodes als removed. |
Entscheidend: dass der Detector down entscheidet, entfernt den
Peer nicht automatisch. Entfernung läuft durch die
Downing-Strategie — der Detector ist das Signal, nicht die
Aktion. Ohne Downing-Strategie bleibt eine down-Entscheidung
nur beratend.
Die Sicht des lokalen Nodes
Abschnitt betitelt „Die Sicht des lokalen Nodes“Jeder Node fährt seinen eigenen Detector und beobachtet seine eigenen Peers. Das bedeutet, dass zwei Nodes uneins sein können, ob ein dritter erreichbar ist:
Der Cluster-Gossip propagiert diese pro-Node-Beobachtungen. Ein Mitglied gilt global als unreachable, wenn genug Peers es so melden — der Schwellwert ist in manchen Downing-Strategien konfigurierbar (siehe KeepMajority, KeepReferee).
Eigener Failure Detector
Abschnitt betitelt „Eigener Failure Detector“Der eingebaute Detector des Clusters ist bewusst einfach — einfache
Zeitschwellen, kein Tracking statistischer Varianz. Für LAN-Skala
reicht das aus. Die failureDetector-Cluster-Option stellt nur seine
Schwellwerte ein — sie nimmt Partial<FailureDetectorOptionsType>
(die drei *Ms-Stellschrauben oben), keine Detector-Instanz; es gibt
also keinen “eigenen Detector einhängen”-Hook an Cluster.join.
Falls dein Netzwerk Phi-Accrual braucht (varianzbewusst, adaptive
Schwellwerte), bringt das Framework bereits einen mit:
PhiAccrualFailureDetector (mit PhiAccrualOptions) ist eine
öffentliche, exportierte Klasse, die die Verteilung der jüngsten
Zwischenankunftszeiten verfolgt und einen kontinuierlichen
Verdachtswert erzeugt. Er ist noch nicht als auswählbarer Detector
in den Cluster eingebunden — der Cluster fährt immer den einfachen
FailureDetector — daher nutzt du ihn vorerst eigenständig.
Failure-Detector-Entscheidungen diagnostizieren
Abschnitt betitelt „Failure-Detector-Entscheidungen diagnostizieren“import { MemberUnreachable, MemberReachable } from 'actor-ts';
cluster.subscribe((evt) => { if (evt instanceof MemberUnreachable) { console.log(`${evt.member.address} als unreachable markiert`); } else if (evt instanceof MemberReachable) { console.log(`${evt.member.address} wieder als reachable markiert`); }});Abonniere die Cluster-Events für Sichtbarkeit. In Produktion verdrahte das in Metriken — ein Histogramm der “unreachable-Dauern” zeigt, ob der Schwellwert dem tatsächlichen Aussetzerprofil deines Netzwerks entspricht.
ReachabilityChanged — was diese Node sieht
Abschnitt betitelt „ReachabilityChanged — was diese Node sieht“MemberUnreachable ist ein Mitgliedschaftsübergang, und das macht es
zum falschen Signal für zwei Fragen, die es zu beantworten scheint:
- Es muss nicht die Beobachtung dieser Node sein. Der
Member-Status wandert per Gossip, also feuert
MemberUnreachableauch für einen Peer, von dem jemand anderes nichts mehr hört, während die eigenen Heartbeats dorthin normal ankommen. - Es feuert nur für ein Member, das
upwar. Ein Peer, der währendjoining,weakly-upoderleavingverstummt, erzeugt gar kein Reachability-Event — das Nächste, was du von ihm hörst, ist die Entfernung.
ReachabilityChanged ist das eigene Urteil des lokalen Detectors, ohne
beide Einschränkungen:
import { ReachabilityChanged } from 'actor-ts';
cluster.subscribe((evt) => { if (evt instanceof ReachabilityChanged) { peerHealth.gauge({ peer: evt.address.toString() }).set(evt.reachable ? 1 : 0); }});Es feuert nur bei Übergängen — einmal, wenn der Detector einen Peer
aus den Augen verliert, einmal, wenn er ihn wiedersieht — und nie für
einen Peer, der seit dem ersten Kontakt gesund war. Das Urteil wird
einmal pro heartbeatIntervalMs neu berechnet und kippt bei
unreachableAfterMs, ist also die früheste Warnung des Detectors,
lange vor jeder Downing-Entscheidung.
Dieses Urteil gegen den gegossipten status des Members zu halten, ist
der Weg, eine Partition von einem toten Peer zu unterscheiden: eine
Node, die alle anderen noch erreichen, ist ein Leitungs- und kein
Host-Problem. Das Event trägt keine Observer-Menge — “welche anderen
Nodes erreichen X ebenfalls nicht” bräuchte eine Observer-zu-Subjekt-
Tabelle auf dem Draht, und Gossip trägt eine flache Member-Liste.
Wohin als Nächstes
Abschnitt betitelt „Wohin als Nächstes“- Cluster-Überblick — die Mitgliedschafts-Zustandsmaschine, die der Detector speist.
- Downing-Strategien —
was nach der
down-Entscheidung des Detectors passiert. - Joining und Seeds — wie Peers erstmals in der Mitgliedertabelle auftauchen.
- Konfiguration — die
HOCON-Keys (
actor-ts.cluster.failure-detector.*). - Failure-Detector-Tuning — die operativ ausgerichtete Tuning-Seite.
Die FailureDetector
API-Referenz deckt die vollständige Oberfläche ab.
