Skip to content
Back to Architecture Decisions

Consistency Trade-offs

The CAP/PACELC choice between strong and eventual consistency, and how to pick per data type.

Why

Strong consistency (linearizable reads) is simple to reason about but limits availability and latency under partitions. Eventual consistency is fast and always-available but lets reads see stale data, which is a bug if you assumed freshness. The trade-off isn't global — it's per data type: a bank balance needs strong consistency (overdraft is a legal issue), a view counter tolerates eventual (a 2-second stale count is fine). Making the choice explicit per data type prevents the two failure modes: over-requiring strong consistency (slow, unavailable) and under-requiring it (stale-data bugs).

When

During data modeling, per data type. Takes 30 min per type. Revisit if staleness bugs or availability issues appear.

How

1. For each data type, ask: "what's the harm if a read sees 2-second-old data?"
   - No harm (view count, leaderboard) → eventual consistency OK
   - Annoying (profile update) → eventual with short lag (1s) OK
   - Harmful (balance, inventory, permission) → strong consistency required
2. For strong-consistency data, use a single-writer (DB primary) and linearizable reads
3. For eventual data, use read replicas / async replication with a documented staleness bound
4. If you mix, label each read with its consistency level so callers know what they're getting
5. Test the eventual paths: inject a 2s replication lag and confirm the app still behaves correctly

Example

Data types and consistency:
  account_balance   → STRONG (overdraft = legal issue) — read from primary
  product_inventory → STRONG (oversell = chargeback) — read from primary, lock on write
  user_profile      → EVENTUAL, <1s lag — read from replica
  view_count        → EVENTUAL, <5s lag — read from replica, batched increments
  search_index      → EVENTUAL, <30s lag — async reindex, acceptable

Mixed read endpoint:
  GET /v1/account  → { balance (strong), profile (eventual, ~0.5s stale) }

Common Problems

  • ⚠Defaulting to strong consistency for everything — the system is slow and unavailable when a replica lags, for data that didn't need freshness.

Best Practices

  • ✓Document the staleness bound for every eventually-consistent read — 'eventually' is not a spec; '<2s' is.
  • ✓Put the consistency choice in the data model doc so a future engineer doesn't 'fix' a strong-consistency requirement by adding a cache.

Related Resources