MediumSchedulingPython 3

Worker Lease Registry

Coordinate worker ownership with TTL leases, heartbeat extension, stale-worker rejection, and fencing tokens.

40m3 sample tests7 hidden tests

Implement LeaseRegistry, an in-memory lease table for long-running worker jobs.

Requirements

  • LeaseRegistry(ttl_seconds) initializes the registry with a default lease time-to-live in seconds.
  • acquire(run_id, worker_id, now) returns a lease dictionary or None.
  • A successful acquire returns a dict with at least: run_id, worker_id, lease_id, fencing_token, and expires_at (now + ttl_seconds).
  • lease_id is "{run_id}:{worker_id}:{fencing_token}" (e.g. "run-1:worker-a:1").
  • Only one active lease may exist per run.
  • An expired lease can be acquired by another worker.
  • Expiry is inclusive: a lease is expired when expires_at <= now.
  • Every new lease gets a monotonically increasing fencing_token.
  • Fencing tokens are registry-wide: each successful acquire (any run) receives the next integer after the previous successful acquire. Tokens start at 1.
  • heartbeat(lease_id, fencing_token, now) extends the current lease (expires_at = now + ttl_seconds) and returns True or False. Heartbeat must not revive an expired lease; treat expiry like validate.
  • validate(run_id, lease_id, fencing_token, now) returns whether a worker may still mutate the run (active lease for that run, matching ids/token, not expired).
  • release(lease_id, fencing_token) releases only the current lease for that id/token and returns whether the release succeeded (bool). Release may succeed past expires_at, provided the lease remains current for its run.

Example

python
1registry = LeaseRegistry(ttl_seconds=10) 2lease = registry.acquire("run-1", "worker-a", now=0) 3assert lease["fencing_token"] == 1 4assert lease["lease_id"] == "run-1:worker-a:1" 5assert registry.acquire("run-1", "worker-b", now=5) is None 6assert registry.acquire("run-1", "worker-b", now=10)["fencing_token"] == 2

Constraints

  • Keep it single-process and in memory.
  • Use the provided now value. Don't call wall-clock time.

Editor