Background
Sections
IntroductionRequirements & Problem AnalysisConstraints & AssumptionsEstimation TechniquesFunctional vs Non-Functional RequirementsMoSCoW PrioritizationSystem Design FundamentalsArchitecture DiagramClass DiagramComponent DiagramData Flow Diagram (DFD)ER Diagram (Entity-Relationship Diagram)High Level Design (HLD)Low Level Design (LLD)Sequence DiagramState DiagramUse Case DiagramData StorageDocument StoresFile StorageGraph DatabasesIn-Memory DatabasesKey-Value StoresNewSQLNoSQL DatabasesObject StorageSQL Databases (RDBMS)Time-Series DatabasesWide-Column StoresDatabase ConceptsACID PropertiesCAP TheoremConsistency ModelsIndexingNormalization & DenormalizationReplicationSharding & PartitioningTransactions & Isolation LevelsScalabilityAuto-Scaling & ElasticityConsensus & Leader ElectionLoad BalancingReplication & Read ReplicasSharding & PartitioningVertical vs Horizontal ScalingAvailability & ReliabilityBackup & Data DurabilityCircuit BreakerData ConsistencyDisaster RecoveryFault Tolerance & FailoverGraceful DegradationHigh AvailabilityNetworkingCDNDNSFirewalls & VPNHTTP & HTTPSLoad Balancer & Reverse ProxyTCP/IP & UDPWebSocketsCachingCache InvalidationCache Read/Write PatternsCaching LayersEviction PoliciesRedis vs MemcachedMessaging & CommunicationDead-Letter QueueIdempotencyKafka vs RabbitMQ vs SQSMessage QueuesPub/SubCompute & ServicesAPI GatewayContainers & OrchestrationMonolith vs MicroservicesServerlessService DiscoveryService MeshWeb Server & App ServerAPI DesignAPI Versioning & IdempotencyAuthentication & AuthorizationGraphQLgRPCPaginationRate Limiting & ThrottlingRESTSecurityAuthentication & AuthorizationData PrivacyEncryptionInput Validation & InjectionOAuth2 & JWTSecrets ManagementXSS & CSRFStorage & File SystemsBackup & RetentionBlock vs File vs Object StorageData Lakes & WarehousesDistributed File SystemsEphemeral StorageObservability & MonitoringDistributed TracingHealth ChecksLoggingMetricsSLI, SLO, SLADesign PatternsBulkhead PatternCircuit Breaker PatternCreational PatternsRate Limiter PatternRetry PatternStructural & Behavioral Patterns

Data Consistency

7 min read

In a Nutshell

Data consistency is the guarantee that everyone reading your data sees a correct, agreed-upon view of it — even when that data is copied across many machines and being updated concurrently. In a single-node database this is trivial; in a distributed system it becomes one of the hardest problems, because replicas take time to sync and networks fail. The central tension, framed by the CAP theorem, is that during a network partition you must choose between staying consistent (reject requests until you can guarantee correctness) and staying available (serve possibly-stale data). This topic looks at consistency through the reliability lens: what guarantees you can offer, and how they interact with availability.

2D minimalistic diagram showing a write updating one replica, then two readers: one reading the updated replica and seeing the new value, another reading a not-yet-synced replica and seeing the old value, illustrating the consistency challenge of keeping all readers in agreement across replicas

How It Actually Works

The Consistency Spectrum

Consistency isn't binary — it's a spectrum from weak (cheap, fast, available) to strong (expensive, slower, correct):

weak ◀─────────────────────────────────────────────────▶ strong
Eventual   Read-your-writes   Monotonic   Causal   Linearizable
  │             │                │           │          │
cheapest    session         no going    respects    every read
most        guarantees      backward    cause→      sees latest
available                               effect      write (as if
                                                    single copy)
Model Guarantee Typical Use
Strong (Linearizable) Every read sees the most recent write Payments, inventory, locks
Causal Causally-related operations seen in order Comment threads, collaborative apps
Read-your-writes You always see your own updates Profile edits, settings
Monotonic reads Time never goes backward for a reader Feeds, timelines
Eventual All replicas converge eventually Likes, view counts, DNS

Strong vs Eventual: The Core Trade-off

Strong Consistency Eventual Consistency
Read freshness Always latest May be stale briefly
Latency Higher (coordination) Lower
Availability during partition Lower (may reject) Higher (keeps serving)
Complexity for app Simple to reason about App must tolerate staleness
Examples Spanner, CockroachDB, etcd Cassandra, DynamoDB (default), DNS

Strong consistency requires nodes to coordinate on every operation (consensus/quorum), which costs latency and reduces availability during partitions. Eventual consistency lets each replica act independently and reconcile later — fast and available, but the application must handle temporarily-stale reads.

Achieving Strong Consistency

  • Consensus (Raft/Paxos): a majority must agree before a write commits (see Consensus & Leader Election).
  • Quorum reads/writes: with N replicas, require W acks to write and R to read where R + W > N guarantees an overlap that sees the latest write.
Quorum example: N=3, W=2, R=2  →  R + W = 4 > 3 = N  ✅
  Write must reach 2 of 3 nodes.
  Read must query 2 of 3 nodes → at least one has the latest write.

Handling Eventual Consistency's Conflicts

When replicas accept independent writes, they can diverge and must reconcile:

Technique How It Resolves Conflicts
Last-Write-Wins (LWW) Highest timestamp wins (simple, can lose data)
Version vectors Track causal history, detect concurrent writes
CRDTs Data types that mathematically merge without conflict
Application merge App logic decides (e.g., shopping cart union)

Consistency Is a Per-Feature Decision

The pragmatic reality: you don't pick one consistency model for the whole system — you pick per data type based on the cost of being wrong.

Within ONE e-commerce app:
  Payment/balance   → STRONG   (double-charge is unacceptable)
  Inventory count   → STRONG-ish (oversell must be bounded)
  Product reviews   → CAUSAL   (replies must follow their parent)
  "Likes" count     → EVENTUAL (a stale count for 2s is fine)
  Recommendations   → EVENTUAL (staleness is invisible to users)

2D minimalistic diagram showing quorum consistency with three replica nodes: a write hitting two of three nodes (W=2) shown with checkmarks, and a read querying two of three nodes (R=2), with the overlapping node highlighted to show R+W>N guarantees the read sees the latest write

Seeing It in Action

Scenario: Read-your-writes consistency in a social app with eventual-consistency storage.

# The store is eventually consistent (fast, available), but users must
# always see their OWN posts immediately, or the app feels broken.

def create_post(user_id, content):
    write_ts = store.write(f"posts:{user_id}", content)  # async-replicated
    # Remember the version this user just wrote
    session[user_id]["last_write_ts"] = write_ts

def read_feed(user_id):
    required_ts = session[user_id].get("last_write_ts", 0)
    # Read from a replica, but require it to be caught up to the user's write
    return store.read(f"posts:{user_id}", min_version=required_ts)
    # If the chosen replica is behind required_ts, the client retries another
    # replica or falls back to the primary — guaranteeing read-your-writes.

What this achieves: the system keeps the low latency and high availability of eventual consistency for the 99% case, while giving each user a strong guarantee about their own writes — the specific consistency property that makes the app feel correct. This is the essence of choosing consistency per-feature rather than globally.

Interview Questions

  1. Q: Explain the trade-off between strong and eventual consistency. Hint: Strong = every read sees the latest write, simple to reason about, but requires cross-node coordination → higher latency and lower availability during partitions. Eventual = replicas serve independently and converge later → low latency and high availability, but reads can be stale and the app must tolerate that. It's fundamentally the CAP trade-off (C vs A during partitions).

  2. Q: How do quorum reads/writes provide strong consistency, and what's the rule? Hint: With N replicas, require W nodes to acknowledge a write and R nodes for a read. If R + W > N, the read and write node sets must overlap, so at least one node in any read has the latest write. E.g., N=3, W=2, R=2. Tuning R and W trades read vs write latency and availability.

  3. Q: Give examples of consistency models between strong and eventual. Hint: Causal (causally-related ops seen in order — replies after their parent), read-your-writes (you always see your own updates), monotonic reads (a reader never sees time go backward). These "session guarantees" are cheaper than full linearizability but fix the most jarring anomalies of pure eventual consistency.

  4. Q: In an eventually-consistent system, how are conflicting concurrent writes resolved? Hint: Last-write-wins by timestamp (simple but can silently drop data), version vectors (detect concurrency and surface conflicts), CRDTs (data types that merge deterministically without conflict), or application-level merge (e.g., union a shopping cart). The right choice depends on whether losing a write is acceptable.

  5. Q: Should a whole system use one consistency model? How do you decide? Hint: No — choose per data type by the cost of being wrong. Money/inventory/locks need strong consistency; comment threads need causal; likes/view counts/recommendations tolerate eventual. Using strong consistency everywhere wastes latency and availability; using eventual everywhere breaks correctness-critical features.

References

Dive Deeper