InMemoryLease
此内容尚不支持你的语言。
InMemoryLease is the dev / test implementation of the
Lease interface. It holds
the lease state in process memory — so multiple InMemoryLease
instances sharing the same name mutually exclude correctly, but
only within a single process.
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 (first acquire)When to use it
Section titled “When to use it”- Unit tests for actors that take a
Leaseparameter — passInMemoryLeaseto verify lease-related behavior without a real backend. - Dev for code that needs a lease but you don’t want to set up Kubernetes or etcd locally.
- MultiNodeSpec tests — every “node” runs in one process, so an InMemoryLease shared across the test nodes mutually excludes correctly.
When NOT to use it
Section titled “When NOT to use it”The shared store
Section titled “The shared store”Every InMemoryLease in a process competes against one
module-global store (inMemoryLeaseStore) — there is no
per-instance registry to wire up. So two InMemoryLease
instances that share the same name mutually exclude
automatically:
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') // same name .withOwner('node-b') .withTtlMs(30_000);const leaseB = new InMemoryLease( lease2Options,);
await leaseA.acquire(); // → trueawait leaseB.acquire(); // → false (leaseA holds it)This is the MultiNodeSpec pattern — every simulated node
gets an InMemoryLease for the same name, and they fight
for the lease like real distributed peers would.
Because the store is a single module-global, mutual exclusion holds only within one process — separate processes each get their own store and can’t see each other’s leases.
Behavior details
Section titled “Behavior details”The implementation:
acquire()atomically CAS-es the registry slot. Returnstrueif it claimed;falseif held by another owner.- TTL expiry — if the holder doesn’t renew within
ttlMs, the registry releases automatically (a setTimeout-driven cleanup). onLostfires if another holder takes over (via the TTL expiry mechanism) or if the registry is forcibly cleared.- Renewal runs on a
setIntervalatrenewalIntervalMs(defaultttl / 3).
InMemoryLease uses real timers (setInterval) and exposes no
scheduler-injection hook — there is no ManualScheduler to wire
in. For deterministic lease-timing tests, mock Date.now(); to
reset shared lease state between tests, clear the exported
inMemoryLeaseStore singleton directly (inMemoryLeaseStore._clear()).
Example: a singleton test
Section titled “Example: a 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 with lease', () => { it('only one holder spawns the 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 wiring + singletonActor elided)
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);
// Only one should ever have the singleton actor child. // ... assert via probes ... });});This kind of test verifies the lease integration without depending on a real K8s API.
Where to next
Section titled “Where to next”- Coordination overview — the bigger picture.
- Lease API — the
contract
InMemoryLeaseimplements. - KubernetesLease — the production alternative.
- MultiNodeSpec — the multi-node test harness this pairs with.
