Guide

No single consistency model fits a whole product. A bank balance and a like count need different guarantees. The useful question for each piece of data is: what does it cost when a reader acts on a stale value?

The big idea

Ask the question per entity, not per system. "Do we need strong consistency?" has no good answer. "Does a stale seat count let two people book seat 14C?" does.

The answers usually sort into three groups:

  • Linearizable: stale reads cause a wrong decision. Balances, seats, usernames.
  • Causal or read-your-writes: users notice staleness in their own actions. Comment threads, profile edits.
  • Eventual: approximate values are fine. Likes, feeds, analytics.
FeatureModelWhy
Account balance, ledgerLinearizableA stale read can approve an overdraft
Seat reservationLinearizable on the seatTwo buyers must not both get 14C
Username uniquenessLinearizableTwo signups must not claim one handle
Editing your own profileRead-your-writesYou notice the stale value; others do not
Comment threadsCausalReplies must not appear before their parents
Like or view countsEventual1,203 vs 1,207 harms nobody

Walking through it

A post gets 5,000 likes per second during a spike. If each like is a linearizable increment on one row, every increment queues on that row's leader. At 2 ms per serialized increment, the ceiling is 1,000 / 2 = 500 per second. That is 10 times too slow.

Make the count eventual instead. Twenty app servers buffer their likes and flush one delta per second:

Per server:  5,000 / 20 = 250 likes per second, sent as one write of +250
Database:    20 writes per second for this post, instead of 5,000
Readers:     see a count up to about one second old

That is fine for likes. It would be wrong for a bank balance.

For read-your-writes on Postgres, record the WAL position (LSN) of each user's last write. Read from a replica only when its replayed position has reached that LSN. Otherwise read from the primary. Do not fix this with a sleep.

Common mistakes

  • Choosing one model for the whole system. Walk through the entities and assign a model to each.
  • Using strong reads everywhere. On DynamoDB, a strongly consistent read costs twice the read capacity. That adds up on a hot table.
  • Sharding an inventory counter and assuming no overselling. Each shard can go negative on its own, so the invariant breaks.

Check yourself

  1. Why is a seat reservation linearizable while a like count is eventual? A stale seat read can sell the same seat twice. A stale like count is off by a few numbers and nobody is harmed.
  2. Why not fix read-your-writes with a sleep after each write? A sleep is a guess about replication lag. It fails when lag spikes, and it slows every write.

Key takeaways

  • Ask what a stale value costs, per entity, before choosing a model.
  • Strong consistency belongs in a small core: ledgers, inventory, uniqueness.
  • Eventual consistency is fine for counts, feeds and analytics, which can be derived from the core.
  • Name the model in design reviews, for example "linearizable on the seat, read-your-writes for the cart".

Further learning

Strong consistency usually needs a consensus protocol underneath. See Consensus and Raft for how that works.

For a worked case where seats must not be sold twice, see the ticket booking case study.