8. Coordination Primitives

Lease with TTL Expiry

middistributed-systems

etcd's leases are its mechanism for self-cleaning state: attach keys to a lease, and when the lease's TTL runs out without being renewed, everything attached to it is considered gone -- no manual cleanup needed. This is what powers etcd-based service discovery: a service registers itself under a lease and keeps renewing it (a heartbeat); if the service crashes and stops renewing, its registration disappears automatically once the TTL elapses.

now is always caller-supplied, never a real clock (same as the Token Bucket / Circuit Breaker exercises). grant() and attach() are done for you. Implement:

  • renew(lease_id, now, ttl): refresh an existing lease's expiry to now + ttl. No-op if the lease doesn't exist, OR if it has already expired as of now -- an expired lease can't be renewed back to life, it's gone.
  • revoke(lease_id): explicitly kill a lease RIGHT NOW, regardless of its TTL -- real etcd clients call this on a graceful shutdown ("I'm intentionally leaving, don't wait for my heartbeat to time out"). Every key currently attached to this lease becomes dead immediately. No-op if the lease doesn't exist. Afterward, the lease_id must behave exactly like an already-expired one -- renew() on it is also a no-op.
  • is_alive(key, now): is key still alive at time now? False if it was never attached to a lease, the lease doesn't exist, or the lease has expired (now >= its expiry time).

The missing link: "expired" and "revoked" must be indistinguishable

A lease can stop being valid two different ways -- its TTL quietly runs out, or someone explicitly calls revoke() -- but from every OTHER method's point of view, those two must look identical. is_alive doesn't need a separate "was this revoked?" flag, and renew doesn't need special-case revoked handling: if revoke fully removes the lease's bookkeeping (the same bookkeeping an expired lease already lacks), every other method's existing "does this lease_id exist" check already does the right thing for free.

Example

lm = LeaseManager()
lm.grant("lease1", now=0, ttl=10)     # expires at time 10
lm.attach("key1", "lease1")
lm.is_alive("key1", now=5)             # -> True
lm.revoke("lease1")
lm.is_alive("key1", now=5)             # -> False, immediately, TTL notwithstanding

Constraints

  • Expiry is exclusive: is_alive is False once now reaches the expiry time, not just after it.
  • Renewing an expired lease must be a complete no-op -- it doesn't come back to life, and its expiry time doesn't change.
  • A revoked lease stays revoked: calling renew() on it afterward must also be a no-op, exactly as if it had expired.

Real-world grounding

Real etcd leases also support attaching MULTIPLE keys to one lease, so a whole group of related keys expires (or is revoked) together -- this exercise's keys mapping already supports that (many keys, one shared lease_id), which is exactly why revoke only has to touch self.leases once and every attached key goes dark automatically.

hints
lease_manager.pysign in to run
Loading...
test result

This is a preview. Sign in to run this exercise against the real tests.

Lease with TTL Expiry — nossletter.tech