March 2026 • Rich Robertson

Designing a Correct Distributed Lease Service: Tenure on Raft

Lease ownership is a correctness problem, not a convenience API.

Most distributed outages blamed on “capacity” are really coordination failures in disguise. The hard question is not whether the system can do more work; it is whether every participant can still agree on who is allowed to act, for how long, and with what authority when clocks drift, leaders fail, and delayed messages reappear. Tenure is interesting in that context because it frames lease ownership as an explicitly replicated correctness boundary rather than as an incidental feature in a larger storage system, and the public codebase and design docs make that framing concrete rather than purely rhetorical (Gray & Cheriton, 1989; Ongaro & Ousterhout, 2014).

Correctness Model

A lease service exists to answer one narrow question correctly: who is allowed to act right now?

  • ownership must be linearizable
  • expiry must be computed from an authority the system can defend
  • renewal must preserve single-owner semantics across failover
  • downstream systems still need fencing, because expiry alone does not stop stale actors

That is the reason to treat a distributed lease service as infrastructure in its own right. Once multiple workers, schedulers, controllers, or reconcilers can attempt the same action, correctness depends on making ownership explicit and making ambiguity expensive. Throughput matters, but throughput without coordination discipline just lets a system violate invariants faster.

Distributed Systems Fail Because of Coordination, Not Just Throughput

Plenty of systems can survive high request rates. Far fewer survive contradictory decisions made under partial failure. The dangerous cases are familiar: two controllers both believe they are primary, a scheduler retries after a timeout and races its own first attempt, or an old actor resumes after a pause and writes state long after its authority should have ended. None of those failures are solved by adding queue depth or CPU. They are solved by explicit coordination semantics and a replicated state machine that gives the system one defensible answer at a time (Schneider, 1990; Gilbert & Lynch, 2002).

Concrete Failure Mode

Imagine controller A pauses after successfully acting as primary, controller B takes over after the lease expires, and then A resumes and writes one more update from its old world view. Without replicated expiry semantics plus downstream fencing, both actors can appear valid long enough to corrupt the same resource.

The Core Primitive: “Who Is Allowed to Act Right Now?”

The right abstraction is not “distributed mutex” in the casual sense. It is time-bounded authority. A lease says that a named owner may act on a resource until an expiry chosen by the service. That is stronger than optimistic hope and narrower than general transaction processing. It is a deliberate answer to coordination problems where temporary exclusive authority is enough to serialize dangerous work such as leadership, scheduling, shard movement, failover orchestration, or singleton task execution (Gray & Cheriton, 1989).

That framing matters because it keeps the system honest. The service is not trying to solve every consistency problem. It is trying to solve the ownership problem precisely enough that other components can build on top of it.

Why Leases Instead of Locks

Traditional locks are often described as if ownership persists until explicit release. In a single process, that is fine. In a distributed system, it is a trap. Processes pause, packets disappear, leaders crash, and clients die while still “holding” something. A lease is explicit about the real world: ownership is temporary unless renewed. That boundedness gives the system a recovery path after client loss and prevents abandoned ownership from becoming permanent. Gray and Cheriton made the case decades ago: leases trade perpetual lock state for time-bounded authority that can be reclaimed safely when communication is imperfect (Gray & Cheriton, 1989).

Leases also force a sharper question about time. If ownership ends automatically, whose clock decides? If the answer is “each client decides for itself,” the service has already lost its correctness boundary. Lease semantics only become defensible when the system has a single authority for time evaluation during grant and renewal.

Why Tenure Exists as a Purpose-Built Lease Service Rather Than a General-Purpose KV Store

A general-purpose key-value store can often be made to approximate lease semantics, but that does not mean leases are its first-class contract. A purpose-built lease service can put the whole interface around ownership, renewal, release, expiry, and fencing tokens instead of treating them as conventions layered on top of arbitrary reads and writes. That narrower scope matters operationally because the hard part is not storing bytes. The hard part is ensuring that every state transition preserves single-owner meaning under failover and partial failure.

This is the architectural lens I am using for Tenure here. The important point is not hidden implementation detail; it is that Tenure is best understood as a lease-specific replicated state machine, not as a generic storage substrate with some helper APIs attached.

Why Scala Matters Here

That same design goal is also why Scala is a meaningful implementation choice rather than a cosmetic one. A lease service is mostly coordination logic: explicit states, guarded transitions, asynchronous replication steps, and failure handling that cannot afford ambiguous local mutation. Scala fits that shape well because it makes immutable modeling natural, keeps algebraic state representations practical, and gives the implementation a clearer way to express “these are the only valid lease states and transitions” instead of spreading that logic across ad hoc mutable objects.

That does not mean the language somehow proves distributed correctness on its own. Raft, fencing, quorum rules, and expiry semantics still do the real systems work. But Scala helps keep the local program honest. It is a strong fit for a correctness-first coordination service because immutability reduces accidental state drift, typed state modeling makes illegal transitions harder to smuggle in, and JVM-based asynchronous composition gives concurrency structure without forcing thread management to become the primary design surface (Scala, 2025). In a system like Tenure, that combination matters: the implementation language should make concurrency and correctness boundaries easier to express, not easier to blur.

Representative Scala Shape

sealed trait LeaseState
case object Free extends LeaseState

final case class Held(
  owner: OwnerId,
  expiry: Instant,
  token: Long
) extends LeaseState

def renew(
  current: LeaseState,
  requester: OwnerId,
  now: Instant,
  ttl: FiniteDuration
): Either[LeaseError, Held] =
  current match {
    case held: Held
        if held.owner == requester &&
           now.isBefore(held.expiry) =>
      Right(held.copy(expiry = now.plusMillis(ttl.toMillis)))

    case held: Held if now.isBefore(held.expiry) =>
      Left(LeaseOwnedByAnotherRequester)

    case _ =>
      Left(LeaseExpired)
  }

Typical Mutable OO Shape

final class LeaseRecord {
  OwnerId owner;
  Instant expiry;
  long token;
  boolean free;

  void renew(OwnerId requester, Instant now, Duration ttl) {
    if (!free && owner.equals(requester) && now.isBefore(expiry)) {
      expiry = now.plus(ttl);
      return;
    }
    throw new LeaseException();
  }
}
Representative implementation shape: Scala’s sealed states and immutable updates make the legal lease transitions explicit, while a mutable object-oriented model tends to collapse state, identity, and concurrency-sensitive mutation into one place. The point is not that Java cannot implement the protocol. It is that Scala makes the correctness boundary easier to express directly.

If you want to compare that framing to the project itself, the public Tenure repository is worth reading alongside this post, especially its README, architecture spec, API contract, and request-flow diagrams. Those materials explicitly emphasize time-bounded ownership, leader-mediated expiration decisions, fencing tokens, and a replicated state-machine model, which is exactly the slice this post is focused on.

Raft-Based System Model

For a lease service, Raft is attractive because it gives a comprehensible path to a linearizable write leader backed by replicated log agreement. A client proposal becomes durable system state only after the leader appends it, replicates it to a quorum, and applies it in log order. That is exactly the shape a lease service wants: one write authority at a time, ordered state transitions, and an unambiguous rule for what counts as committed ownership (Ongaro & Ousterhout, 2014; Lamport, 2001).

Tenure: Lease Service Architecture (Raft-Based)

Tenure lease service architecture using a Raft leader and two followers Workers and a scheduler call an API layer that forwards writes to a Raft leader, which replicates log entries to two followers and applies them to a lease state machine persisted to storage. Worker A Worker B Scheduler gRPC / API Acquire Renew Release Raft Leader linearizable writes Follower 1 replica Follower 2 replica Raft Log ordered commands Lease State Machine owner / expiry / token Persistence writes forward to leader
Tenure’s useful abstraction is not “three nodes with replication.” It is a single write authority backed by quorum replication, ordered state-machine application, and persisted lease metadata.

Key Takeaways

  • Correctness depends on every lease mutation flowing through the current leader, not on clients choosing a convenient node.
  • The log, state machine, and persistence path matter because lease ownership has to survive crashes as committed state, not as in-memory hope.

That model narrows ambiguity. Followers are not independent authorities; they are replicas that participate in commitment. Clients may talk to an API endpoint anywhere, but lease-changing commands must resolve to the leader path if the system wants one linearizable answer.

Lease Acquire — Linearizable Write Path

Lease acquire linearizable write path A worker sends Acquire for resource R1 to the API, which forwards to the leader for validation, replication, application, token assignment, and a success response after quorum acknowledgement. Worker A API Leader Follower 1 Follower 2 Acquire(R1, owner=A, ttl=10s) request forward to leader validate current lease state AppendEntries AppendEntries ack ack apply state machine owner=A, expiry=tL+10s, token=1 leader accepted success(owner=A, token=1)
Lease acquisition is a write protocol, not a cache lookup. The service only answers after the proposed ownership change has become committed replicated state and the leader has assigned the resulting lease metadata.

Key Takeaways

  • “Acquire” has to validate against current committed state, not a client’s assumption that the lease is free.
  • Token assignment belongs in the committed transition so downstream systems can distinguish the newest authority from every older one.

Lease State Machine

A lease service becomes intelligible when its state machine is narrow. There is some representation of free, some representation of held, and a small number of legal transitions. Complexity comes from the guards around those transitions: whether the caller is the current owner, whether the lease has expired according to service authority, and whether the transition should advance the fencing token.

Central Safety Invariant

The lease system only works if this stays true.

At most one owner per resource while now < expiry.

Lease State Transitions

Lease state transitions between free and held State machine showing transitions from Free to Held on acquire, renew as a self-loop for the same owner, expire and release back to Free, and a rejected acquire while held and unexpired. Acquire rejected while Held and unexpired A different owner must wait for expiry or release. Free no owner Held owner, expiry, token Acquire Free -> Held Expire / Release Held -> Free Renew same owner
The point of the state machine is discipline. Most bugs in lease systems are illegal transitions disguised as retries, stale renewals, or “helpful” reacquire shortcuts.

Key Takeaways

  • Rejected acquires are as important as successful ones, because denial preserves single-owner semantics during the held interval.
  • Renew is not a new acquire by convention; it is a guarded transition allowed only for the current owner while authority still exists.

Time Model and Why Leader-Based Time Authority Matters

Leases force the system to mix consensus with time. Consensus orders state transitions, but expiry depends on a time calculation. The least dangerous model is to make the leader’s evaluation time authoritative for grant and renewal, then replicate the resulting expiry as part of committed state. That does not make clocks perfect. It makes the system’s decision process singular. Without that singularity, each client or follower can reinterpret lease validity independently, which is exactly how contradictory ownership appears.

No client-side TTL enforcement. Ever.

Leader-Based Time Authority

Leader-based time authority for lease expiry Diagram showing client and follower clocks ignored while the leader clock determines expiry as leader time plus requested TTL. Client clock = 12:00:03 ignored for expiry Leader clock = 12:00:00 ttl = 10s expiry = 12:00:10 Follower clock = 11:59:58 ignored for grant Committed lease carries expiry chosen by leader clients do not negotiate time truth
The service can tolerate imperfect clocks more easily than it can tolerate multiple time authorities. The leader decides the effective expiry for a grant or renewal, and that decision is what gets replicated.

Key Takeaways

  • Ignoring client clocks is a correctness choice, because clients have every opportunity to be slow, fast, or paused.
  • Replicating the computed expiry is what keeps failover from turning “what time was it?” into a semantic fork.

This is also why lease design should stay modest about what time can prove. Spanner shows one disciplined answer to time uncertainty: expose bounds and incorporate them into the correctness model rather than pretending uncertainty is absent (Corbett et al., 2012). A lease service without globally synchronized clocks should make the simpler move: choose one authority for expiry decisions and make downstream consumers rely on fencing, not just on wall-clock optimism.

Fencing Tokens and Downstream Safety

Expiry is necessary, but it is not sufficient. A client can continue acting after its lease expired if it was partitioned, paused, or merely delayed. That is the classic zombie writer problem. The standard answer is a monotonically increasing fencing token issued on successful acquisition or handoff. Downstream systems must reject operations carrying an older token than the one they have already accepted. This turns lease history into an order, not just a timer.

Safe Lease Handoff

Safe lease handoff after expiry Timeline showing worker A acquiring token one, renewing, lease expiry, and worker B subsequently acquiring token two. time → Acquire by A token=1 Renew by A extends expiry expiry lease becomes free Acquire by B token=2 The new owner does not inherit A’s authority; it receives a later token and a fresh interval.
Handoff safety comes from two facts together: A’s authority ends at expiry, and B’s later acquisition carries a strictly newer token. That combination is what lets downstream systems distinguish a legitimate successor from a delayed predecessor.

Key Takeaways

  • Safe reacquire is not “same lease, new owner.” It is a new committed ownership epoch with a later token.
  • That epoch boundary is what makes stale requests rejectable instead of merely suspicious.

Preventing Zombie Writes

Fencing token enforcement preventing zombie writes A delayed stale actor with token one attempts a write, a current actor with token two succeeds, and the stale write is later rejected because its token is older. Stale Actor A token=1 Current Actor B token=2 Protected Service stores highest token seen Resource R1 state mutation delayed write current write token=2 accepted token=1 rejected later
The lease service cannot physically stop a delayed client from sending traffic. Fencing tokens let downstream systems perform the final correctness check and refuse older authority even if it arrives late.

Key Takeaways

  • Without fencing, an expired client can still corrupt downstream state if it was delayed rather than dead.
  • Downstream enforcement is part of the lease protocol boundary, not an optional extra.

Failure Modes

A lease service is only as credible as its behavior under failure. This is where many hand-wavy designs collapse. Every interesting failure mode is really a question about whether the system can preserve ownership meaning when observation becomes incomplete.

Leader Failover and Lease Safety

Leader failover and lease safety An old leader grants a lease, crashes, a new leader is elected, and a client renews only if the replicated lease state still shows the lease as valid. Old Leader grants lease Crash leadership lost New Leader elected from quorum Client renews against new leader renewal valid only if replicated state still shows owner + unexpired lease
Failover should not create a second source of truth. The new leader inherits whatever the replicated log and state machine can defend, and nothing else.

Key Takeaways

  • A client does not keep authority because it remembers a successful call to the old leader; it keeps authority only if that lease exists in replicated state and remains valid.
  • This is why leader failover tests are fundamentally lease semantics tests, not just election tests.

If the old leader granted something that was not committed to a quorum, the new leader must behave as if that grant never became authoritative. That is harsh, but it is correct. Raft’s value here is that it gives a precise answer to what survives failover: committed state, not local memory (Ongaro & Ousterhout, 2014).

Partition Handling

Network partition handling for lease safety A majority partition containing the leader and one follower continues safely, while an isolated minority node cannot grant or renew leases because it lacks quorum. majority partition minority isolated node Leader can commit with quorum Follower acks replication Isolated Node cannot safely grant cannot safely renew no quorum path
CAP is not an abstract slogan here. During partition, the minority side must lose the ability to issue lease-changing answers, because availability without quorum would manufacture conflicting ownership (Gilbert & Lynch, 2002).

Key Takeaways

  • The minority partition staying silent is not a usability bug; it is the mechanism that prevents split-brain lease issuance.
  • Any node that cannot prove quorum-backed authority must refuse grant and renewal operations.

That same discipline explains expiry and reacquire behavior. If a client disappears and later returns, the system should not care about the client’s narrative. It should care only whether the lease remained valid in replicated state and whether a later owner has already taken over. Delayed or stale clients are ordinary conditions, not special exceptions.

Closing on Ambiguity and Ownership

The real job of a distributed lease service is not to make mutual exclusion feel convenient. It is to make ownership ambiguity difficult to express. Raft helps by giving the system one ordered place where lease-changing decisions become authoritative. Leader-based time authority helps by ensuring expiry is computed from a single defensible perspective. Fencing tokens help by carrying that ordering into downstream state changes where stale actors would otherwise remain dangerous.

That is why Tenure is worth examining as a dedicated lease system. The value is not that it stores a bit saying “locked.” The value is that it treats temporary exclusive authority as replicated state with explicit failure behavior. If you want to inspect the current project directly, start with the Tenure codebase on GitHub and then follow the architecture and API documents from there. The architectural point is narrow and important: in distributed systems, ownership must be something the system can prove, not something a client merely believes.

What to Read Next

For the broader context around this kind of correctness work, see Distributed Systems Engineering: Correctness, Coordination, Reliability.

If you want the simpler entry points into the same cluster, start with Distributed Systems Interview Guide and What Is a Distributed Lock? (With Examples). Then continue with Fencing Tokens Explained and Distributed Lock Failure Modes in Production to connect lease theory to stale-writer incident handling. For the adjacent consensus comparison, continue with Raft vs Paxos vs EPaxos: A Practical Guide.

References