Why this matters
- Wrong consistency assumptions cause double-spend, ghost reads, and "I transferred but balance didn't change" support tickets.
- API contracts should name the consistency level — not bury it in implementation details.
- ShardPay uses different models per endpoint: linearizable commits, read-your-writes for sessions, eventual for analytics.
- Interviewers ask you to pick a model for a scenario and justify the latency/availability cost.
Models from strong to eventual
Transfer commit on ShardPay's ledger is linearizable: once the API returns 201 Created, any subsequent linearizable read on that account sees the debit. The merchant dashboard polls a replica with read-your-writes via session token. The finance team's nightly rollup reads from an analytics warehouse that may lag by minutes — eventual consistency with a documented asOf watermark.
Choosing the wrong model for an endpoint is a design bug, not a performance optimization gone wrong.
Spectrum overview
- Linearizable (strong) — Operations appear to execute atomically in real-time order. ShardPay's
POST /transferscommit point is linearizable: two concurrent transfers on the same account serialize as if one finished before the other started. - Sequential consistency — All nodes agree on the same order of operations, but that order may not match real-time. ShardPay's internal audit log stream across regions is sequentially consistent — all consumers see transfers in the same order, though timestamps may not match wall clocks.
- Causal consistency — If operation A causally precedes B (B reads A's write), every node sees A before B. ShardPay's comment thread on a disputed charge respects causality: replies never appear before the parent note, even on lagging replicas.
- Read-your-writes — A session always sees its own prior writes. After a merchant transfers funds, their dashboard session token routes reads to a replica that has applied that write — without always hitting the leader.
- Monotonic reads — A session never sees time go backward. ShardPay's transaction list API never shows a newer page followed by an older one when paginating with a session cursor.
- Eventual consistency — Replicas converge if writes stop; no bound on staleness during churn. ShardPay's global "total volume processed" counter on the marketing site is eventual — off by 0.1% for an hour is acceptable.
Matching models to ShardPay endpoints
Financial correctness lives at the strong end of the spectrum. Product UX and analytics slide toward weaker models with explicit user communication.
| Use case | Model | User-visible guarantee |
|---|---|---|
| Transfer commit | Linearizable | Debit visible immediately on strong read |
| Post-transfer balance (same session) | Read-your-writes | User sees their own transfer |
| Transaction history scroll | Monotonic reads | No flickering backward in time |
| Merchant analytics | Eventual | Chart labeled ~5 min delay |
| Cross-shard aggregate | Eventual | Recomputed by batch job |
Transfer commit
ModelLinearizableUser-visible guaranteeDebit visible immediately on strong readPost-transfer balance (same session)
ModelRead-your-writesUser-visible guaranteeUser sees their own transferTransaction history scroll
ModelMonotonic readsUser-visible guaranteeNo flickering backward in timeMerchant analytics
ModelEventualUser-visible guaranteeChart labeled ~5 min delayCross-shard aggregate
ModelEventualUser-visible guaranteeRecomputed by batch job
ShardPay consistency model by use case
Walkthrough: Session token for read-your-writes
- Merchant POSTs a transfer; leader commits at
commitIndex=1842. - API response includes
sessionTokenencoding{ replicaHint, minCommitIndex: 1842 }. - Dashboard GET
/balancesendsSession-Tokenheader. - Router picks replica
R2only ifR2.appliedIndex >= 1842; otherwise falls back to leader. - Merchant sees updated balance without every read paying leader latency.
// ShardPay session-aware read
public Balance readBalance(String accountId, Optional<SessionToken> session) {
if (session.isPresent()) {
Replica replica = replicaPool.firstAtOrAbove(session.get().minCommitIndex());
if (replica != null) return replica.read(accountId);
}
return leader.read(accountId);
}
Weaker models still need contracts
Eventual consistency is not "anything goes." ShardPay documents convergence guarantees: analytics counters reconcile within 15 minutes; notification dedupe uses transferId so duplicates from merge never double-notify.
Weaker-model discipline
- Bounded staleness — Even eventual paths often have SLOs. ShardPay alerts when analytics warehouse lag exceeds 30 minutes — weaker than linearizable, but not unbounded.
- Monotonic writes — A client's writes are seen in issue order at all replicas. ShardPay's mobile app queues offline transfers; when connectivity returns, the server applies them in client sequence order per account.
- Consistent prefix — Replicas never see later writes without earlier ones in the same stream. ShardPay's Kafka consumers processing ledger events never apply
TransferSettledbeforeTransferInitiatedfor the sametransferId. - API surfacing — Return
asOfTimestampandconsistency: eventualin response headers. ShardPay's analytics API includes"dataFreshness": "2026-10-01T23:55:00Z"so finance teams know what they are looking at.
Example: Ghost read without read-your-writes
Before ShardPay added session tokens, a merchant transferred $5,000 and immediately polled a random replica. The replica had not yet applied the write; the dashboard showed the old balance. The merchant initiated a second transfer, believing the first failed — support escalated a near double-spend. Read-your-writes on the session path fixed the UX; linearizable reads remain available via ?fresh=true for integrations that need them.
Quick recall
Everything you need if you only revisit this box.
- Name the consistency model in your API contract — ShardPay tags endpoints explicitly.
- Stronger models cost latency and availability; match model to business risk per endpoint.
- Read-your-writes and monotonic reads bridge UX and performance without full linearizability on every poll.
Test yourself
Answer these before moving on — recall is what makes it stick.