Singleton mit Lease
Der Singleton-Manager wählt standardmäßig einen Singleton allein basierend auf Cluster-Gossip. Bei Partitionen + unzureichendem Downing können beide Hälften ihren eigenen Leader wählen → zwei Singletons existieren.
Die Single-Writer-Lease verhindert das:
import { StartSingletonOptions } from 'actor-ts/cluster';import { KubernetesLease, KubernetesLeaseOptions } from 'actor-ts/coordination';
const kubernetesLeaseOptions = KubernetesLeaseOptions.create() .withName('job-scheduler-singleton') .withOwner(process.env.POD_NAME!) .withTtlMs(30_000) .withNamespace(process.env.K8S_NAMESPACE!);const startSingletonOptions = StartSingletonOptions.create() .withTypeName('job-scheduler') .withActor(JobScheduler) .withLease(new KubernetesLease( kubernetesLeaseOptions, ));cluster.singleton.start(startSingletonOptions);Jetzt spawnt der Manager den Singleton erst nach dem Erwerb der Lease. Zwei Manager, die gleichzeitig Leadership beanspruchen, liefern sich ein Rennen um die Lease; nur einer gewinnt.
Wie es funktioniert
Abschnitt betitelt „Wie es funktioniert“Das Lease-Backend (K8s-API-Server) liefert die atomare Exactly-One-Holder-Garantie — jenseits der eventuellen Konsistenz von Gossip.
Konfiguration
Abschnitt betitelt „Konfiguration“const startSingletonOptions = StartSingletonOptions.create() .withTypeName(typeName) .withActor(singletonActor) .withLease(lease) // ← optionale Lease .withAcquireRetryIntervalMs(5_000); // Default — Retry nach fehlgeschlagenem Acquirecluster.singleton.start(startSingletonOptions);Lease ist dieselbe Abstraktion wie bei
Coordination — InMemoryLease für Tests,
KubernetesLease für Produktion.
Das Schutzniveau lesen
Abschnitt betitelt „Das Schutzniveau lesen“| Setup | Singleton-Einzigartigkeitsgarantie |
|---|---|
| Kein Downing, keine Lease | Hält, solange der abtretende Host den Hand-over beantwortet. Eine Partition läuft in den Timeout, danach hosten beide Hälften. |
| Nur Downing-Strategie | Dasselbe, aber die Partition wird innerhalb von downAfterMs aufgelöst statt anzuhalten. |
| Downing + Lease | Das Stärkste, was hier verfügbar ist: Arbitrierung durch eine dritte Instanz, die beide Seiten erreichen können, und die Lease wird erst freigegeben, wenn die abtretende Instanz terminiert ist. |
Für Singletons, bei denen Doppelausführung echten Schaden anrichten würde (Kunden doppelt belasten, Events doppelt veröffentlichen), nutze beides.
Keine dieser Zeilen sagt „unmöglich“, und das ist Absicht. Ohne Lease macht
der Hand-over
„höchstens eine“ wahr, solange der Amtsinhaber erreichbar ist und
antwortet — und einen Peer, der nicht antworten kann, kann man auch nicht
bitten zurückzutreten. Der eintretende Host nimmt dann
handOverTimeoutMs in Kauf und hostet trotzdem, mit einer Warnung, dass
die Invariante nicht bewiesen wurde. Mit einer Lease liegt der
Schiedsrichter außerhalb beider Hälften, und genau das ändert die Antwort;
was bleibt, ist die Garantie des Lease-Providers selbst — der nächste
Abschnitt beschreibt, wie ein Ausfall davon sich verhält.
Behandlung von Lease-Verlust
Abschnitt betitelt „Behandlung von Lease-Verlust“// Innerhalb des Managers (vom Framework verwaltet):lease.onLost((reason) => { // Stoppe den Singleton; retry acquire nach acquireRetryIntervalMs});Wenn die Lease widerrufen wird (TTL abgelaufen, anderer Halter hat sie übernommen):
- Der Manager stoppt den Singleton mit einem
PoisonPill— die Instanz arbeitet also zuerst ab, was schon in ihrer Mailbox liegt. Das ist ein geordnetes Drainen, kein sofortiger Abbruch: wie lange es dauert, ist die Größe dieses Rückstaus pluspostStop. - Der
postStopdes Singletons läuft. - Erst dann versucht der Manager Acquire erneut. Neu zu erwerben, während die alte Instanz noch drainiert, hat den Manager früher dauerhaft eine Lease über gar keinen Singleton halten lassen.
Bedeutet: wenn das Lease-Backend kurz hickst und sie kurzfristig widerruft, startet der Singleton neu während der Erholung — gleicher Effekt wie ein kurzer Actor-Neustart.
Wichtig, in welche Richtung die Ehrlichkeit hier schneidet: auf diesem Pfad
ist Promptheit die Sicherheitseigenschaft, und ein Drain ist nicht prompt.
Ein Singleton mit langer Mailbox und langsamem postStop läuft nach dem
Widerruf seiner Lease genau so lange weiter — ein Fenster, in dem ein
anderer Halter schon gestartet sein kann. Halte postStop in einem
Singleton mit Lease kurz, und behandle den Widerruf als den Moment, ab dem
die Instanz nicht mehr autoritativ ist, nicht als den Moment, in dem sie
stoppt.
Verfügbarkeit des Lease-Backends
Abschnitt betitelt „Verfügbarkeit des Lease-Backends“K8s-API-Ausfall → keine Lease-Renewals → Singleton verliert irgendwann die Lease → Singleton stoppt überall → kein Singleton verfügbar, bis K8s-API sich erholtDas Lease-Backend wird zum SPOF. Für typische Cluster ist die K8s-API-Uptime viel höher als die des restlichen Systems, aber das ist eine echte Überlegung. Plane den seltenen Fall ein.
Failover-Fenster
Abschnitt betitelt „Failover-Fenster“A's Lease hat TTL 30s.A stürzt ab — kein Renew passiert.Nach 30s läuft die Lease ab.B erwirbt + spawnt Singleton.Failover-Fenster = Lease-TTL. Konfigurierbar über das
ttlMs-Feld der Lease.
Kürzere TTL = schnelleres Failover, aber mehr Renew-Verkehr. Typisch: 15-30 Sekunden.
Was während des Fensters passiert
Abschnitt betitelt „Was während des Fensters passiert“Sekunden 0-30: A's Lease noch gültig (er ist abgestürzt, aber TTL läuft noch) B kann nicht acquiren; nirgendwo ein Singleton Nachrichten an den Singleton landen in Dead Letters
Sekunden 30+: B erwirbt; Singleton spawnt Wenn Singleton ein PersistentActor ist, läuft Recovery Nachrichten werden wieder verarbeitetWährend der Lücke scheitern Nachrichten an den Singleton — sie routen zu einem gestoppten Manager und landen in Dead Letters.
Für Workloads, bei denen das inakzeptabel ist, erwäge:
- Niedrigeres TTL (15 s gibt schnelleres Failover, mehr Renewals).
- Pufferung beim Sender — der Singleton-Proxy puffert, solange
der Cluster keinen Host hat, und leert den Puffer, sobald einer
da ist. Der Puffer ist gedeckelt (
withBufferSize, Default 1000); jenseits des Limits gehen Nachrichten mit einer Warnung in die Dead Letters, damit eine Lücke, die sich nie schließt, nicht unbegrenzt wachsen kann. - Andere Architektur — Singletons sind inhärent seriell-bei-Failover.
Vergleich mit Sharding mit Lease
Abschnitt betitelt „Vergleich mit Sharding mit Lease“| Singleton + Lease | Sharding + Lease | |
|---|---|---|
| Was geschützt wird | Singleton-Einzigartigkeit | Koordinator-Einzigartigkeit |
| Was bei Lease-Verlust stoppt | Der Singleton-Actor | Allokationen des Koordinators |
| Failover-Fenster-Kosten | Singleton unverfügbar | Neue Shard-Allokationen werden in Warteschlange gestellt |
| Bestehende Arbeit während Failover | Pausiert | Läuft für bereits allokierte Shards weiter |
Singleton-Failover ist disruptiver — der Singleton ist die Workload. Sharding-Failover betrifft nur neue Allokationen — existierende Entities verarbeiten weiter.
Wohin als Nächstes
Abschnitt betitelt „Wohin als Nächstes“- Singleton-Überblick — das Fundament.
- Singleton-Manager — die Per-Node-Wahl-Logik.
- Sharding mit Lease — dasselbe Muster für Sharding.
- Coordination — die Lease-Abstraktion.
- KubernetesLease — Produktions-Backend.
