Raft Linearizable Read Path Playbook (ReadIndex vs Lease Read)

2026-03-23 · software

Raft Linearizable Read Path Playbook (ReadIndex vs Lease Read)

Date: 2026-03-23
Category: knowledge
Scope: How to design low-latency Raft read paths without silently violating linearizability.


1) Why this matters

Most teams optimize Raft writes first and discover too late that reads dominate traffic.

If every read goes through full log proposal/commit, latency and throughput suffer. If you “optimize” too aggressively (e.g., local leader read with weak time assumptions), you can return stale state during partitions or clock anomalies.

This playbook gives a practical ladder:

  1. Log-commit reads (simplest, safest, slowest)
  2. ReadIndex reads (linearizable, lower write-path overhead)
  3. Lease reads (fastest, but requires bounded clock/lease assumptions)
  4. Follower serializable reads (fast but explicitly stale-allowed)

2) Mental model in 90 seconds

A Raft read is safe only if it is ordered after a point where the serving node is still the valid leader for a quorum-known term.

You can establish that in two common ways:

If neither is true, a local read can be stale.


3) Read path options and tradeoffs

3.1 Log proposal read ("read-as-write")

Mechanism:

Pros:

Cons:

Use when:

3.2 ReadIndex (quorum-checked linearizable read)

Mechanism (leader-side):

  1. leader asks/uses heartbeat quorum confirmation,
  2. obtains safe read index,
  3. waits until local apply index >= read index,
  4. serves read locally.

Pros:

Cons:

Use when:

3.3 Lease-based linearizable read

Mechanism:

Pros:

Cons:

Use when:

3.4 Serializable follower read

Mechanism:

Pros:

Cons:

Use when:


4) Recommended production policy

Default policy:

This keeps semantics clear instead of mixing hidden consistency modes under one API.


5) Implementation blueprint

5.1 Strict read endpoint (safe baseline)

For each strict read request:

  1. request readIndex token from Raft core,
  2. record requestedReadIndex,
  3. block until state machine appliedIndex >= requestedReadIndex,
  4. read KV state machine snapshot/view,
  5. return with metadata (servedBy, appliedIndex, readIndexLagMs).

Operational guard:

5.2 Lease-read fast path (optional)

Serve strict read from lease path only when all are true:

Fallback chain:

LeaseRead -> ReadIndex -> (timeout/error policy)

Never fallback from strict endpoint to serializable silently.

5.3 Follower read endpoint (explicitly stale)

Expose separate API mode/header (example):

Return metadata:

Make staleness explicit to callers.


6) Lease safety guardrails (practical)

If using lease reads, define a conservative lease budget:

Example rule-of-thumb:

lease_max <= election_timeout - (2 * worst_rtt) - clock_skew_budget - pause_budget

If computed lease budget is small/negative, disable lease reads and stick to ReadIndex.

Additional hardening:


7) What to measure (must-have telemetry)

For read safety/perf, track at least:

If you cannot separate latency by read mode, you cannot tune safely.


8) Common failure modes

A) Hidden consistency downgrade

Symptom:

Root cause:

Fix:

B) Lease reads during clock anomalies

Symptom:

Root cause:

Fix:

C) Applied index lag not enforced

Symptom:

Fix:

D) ReadIndex latency spikes under quorum turbulence

Symptom:

Fix:


9) Rollout plan (low-risk)

  1. Start with ReadIndex-only strict reads.
  2. Add full telemetry and strict timeout/error semantics.
  3. Introduce lease reads for a small traffic slice.
  4. Continuously compare correctness/perf vs ReadIndex baseline.
  5. Auto-disable lease path on clock/leadership anomalies.
  6. Keep serializable mode explicit and opt-in.

10) Practical takeaway

For most teams, ReadIndex is the best default strict-read path: linearizable, simpler than lease risk, and cheaper than read-as-write.

Use lease reads as an optimization layer, not as the foundation. When in doubt, pay a little latency to keep semantics honest.


References