InMemoryLease
InMemoryLease ist die Dev-/Test-Implementierung des
Lease-Interfaces. Sie hält den
Lease-State im Prozessspeicher — mehrere InMemoryLease-Instanzen,
die denselben name teilen, schließen sich also korrekt aus,
aber nur innerhalb eines Prozesses.
import { InMemoryLease, LeaseOptions } from 'actor-ts/coordination';
const leaseOptions = LeaseOptions.create() .withName('my-singleton') .withOwner('instance-1') .withTtlMs(30_000);const lease = new InMemoryLease( leaseOptions,);
await lease.acquire(); // → true (erstes Acquire)Wann du sie einsetzt
Abschnitt betitelt „Wann du sie einsetzt“- Unit Tests für Actors, die einen
Lease-Parameter bekommen — übergibInMemoryLease, um Lease-Verhalten ohne echtes Backend zu verifizieren. - Dev für Code, der einen Lease braucht, aber du willst Kubernetes oder etcd nicht lokal aufsetzen.
- MultiNodeSpec-Tests — jeder “Node” läuft in einem Prozess, also schließt sich ein über die Test-Nodes geteiltes InMemoryLease korrekt aus.
Wann du sie NICHT einsetzt
Abschnitt betitelt „Wann du sie NICHT einsetzt“Der gemeinsame Store
Abschnitt betitelt „Der gemeinsame Store“Jedes InMemoryLease in einem Prozess konkurriert gegen einen
modul-globalen Store (inMemoryLeaseStore) — es gibt kein
Registry pro Instanz zu verdrahten. Zwei InMemoryLease-Instanzen,
die denselben name teilen, schließen sich also automatisch aus:
const leaseOptions = LeaseOptions.create() .withName('singleton-x') .withOwner('node-a') .withTtlMs(30_000);const leaseA = new InMemoryLease( leaseOptions,);
const lease2Options = LeaseOptions.create() .withName('singleton-x') // gleicher Name .withOwner('node-b') .withTtlMs(30_000);const leaseB = new InMemoryLease( lease2Options,);
await leaseA.acquire(); // → trueawait leaseB.acquire(); // → false (leaseA hält ihn)Das ist das MultiNodeSpec-Muster — jeder simulierte Node
bekommt ein InMemoryLease für denselben name, und sie kämpfen
um den Lease, wie es echte verteilte Peers tun würden.
Da der Store ein einzelnes modul-globales Objekt ist, gilt der gegenseitige Ausschluss nur innerhalb eines Prozesses — separate Prozesse bekommen jeweils ihren eigenen Store und sehen die Leases der anderen nicht.
Verhaltensdetails
Abschnitt betitelt „Verhaltensdetails“Die Implementierung:
acquire()CAS-t den Registry-Slot atomar. Gibttruezurück, wenn sie ihn beansprucht hat;false, wenn ein anderer Owner ihn hält.- TTL-Ablauf — wenn der Halter nicht innerhalb von
ttlMsrenewt, gibt das Registry automatisch frei (eine setTimeout-getriebene Bereinigung). onLostfeuert, wenn ein anderer Halter übernimmt (via TTL-Ablauf-Mechanismus) oder wenn das Registry forciert geleert wird.- Renewal läuft auf einem
setIntervalzumrenewalIntervalMs(Defaultttl / 3).
InMemoryLease nutzt echte Timer (setInterval) und bietet keinen
Scheduler-Injektionspunkt — es gibt keinen ManualScheduler, den
man einhängen könnte. Für deterministische Lease-Timing-Tests mocke
Date.now(); um geteilten Lease-State zwischen Tests
zurückzusetzen, leere das exportierte Singleton inMemoryLeaseStore
direkt (inMemoryLeaseStore._clear()).
Beispiel: ein Singleton-Test
Abschnitt betitelt „Beispiel: ein Singleton-Test“import { describe, it, expect } from 'bun:test';import { TestKit } from 'actor-ts/testkit';import { InMemoryLease, LeaseOptions } from 'actor-ts/coordination';import { StartSingletonOptions } from 'actor-ts';
describe('SingletonManager mit Lease', () => { it('nur ein Halter spawnt den Singleton', async () => { const tk1 = TestKit.create('node-1'); const tk2 = TestKit.create('node-2');
const leaseOptions = LeaseOptions.create() .withName('singleton') .withOwner('n1') .withTtlMs(30_000); const lease1 = new InMemoryLease( leaseOptions, ); const lease2Options = LeaseOptions.create() .withName('singleton') .withOwner('n2') .withTtlMs(30_000); const lease2 = new InMemoryLease( lease2Options, );
// (Cluster-Verdrahtung + singletonActor ausgelassen)
const singleton1Options = StartSingletonOptions.create() .withTypeName('s') .withActor(singletonActor) .withLease(lease1); cluster1.singleton.start(singleton1Options);
const singleton2Options = StartSingletonOptions.create() .withTypeName('s') .withActor(singletonActor) .withLease(lease2); cluster2.singleton.start(singleton2Options);
// Nur einer sollte je das Singleton-Actor-Child haben. // ... per Probes asserten ... });});Diese Art Test verifiziert die Lease-Integration, ohne von einer echten K8s-API abzuhängen.
Wohin als Nächstes
Abschnitt betitelt „Wohin als Nächstes“- Koordination im Überblick — das Gesamtbild.
- Lease-API — der Vertrag,
den
InMemoryLeaseimplementiert. - KubernetesLease — die Produktions-Alternative.
- MultiNodeSpec — das Multi-Node-Test-Harness, das dazu passt.
