Building Reliable Data Systems, Part 2: Replication, Partitioning, and Transactions

Building Reliable Data Systems, Part 2: Replication, Partitioning, and Transactions

Part 1 looked at data on one machine. Part 2 asks what changes when one machine is not enough.

Distribution gives us availability, lower latency, and scale. It also gives us replication lag, conflicts, hot partitions, routing complexity, and transaction anomalies.

This is the part of Designing Data-Intensive Applications where databases stop looking like storage engines and start looking like distributed systems.

Part 2 is about scaling data across machines. The theme: replication and partitioning buy availability and scale, while transactions define how much correctness the application can safely assume.

Replication Buys Resilience And Proximity

Replication means keeping copies of data on multiple machines.

We replicate for several reasons:

  • Availability: survive machine or datacenter failures.
  • Latency: keep data close to users.
  • Read scalability: serve reads from multiple replicas.
  • Disconnected operation: continue during network interruptions.

The hard question is not “can we copy data?” It is “what happens when copies disagree?”

StrategyHow It WorksMain Trade-off
Single-leaderOne node accepts writes; followers replicateSimple writes, potentially stale reads
Multi-leaderMultiple leaders accept writesBetter regional writes, conflict handling required
LeaderlessClients read/write several replicasHigh fault tolerance, more complex consistency

Single-leader replication is easiest to reason about. Writes go to one place. Followers copy from it. The common cost is stale reads from replicas.

Multi-leader replication is attractive for multi-region systems because users can write to a nearby region. The cost is conflict resolution. Two leaders can accept incompatible writes before they hear from each other.

Leaderless systems push coordination to clients and quorums. They can tolerate failures well, but reads and writes now have to reason about multiple versions.

Leaderless replication often uses quorum-style reads and writes: write to several replicas, read from several replicas, and compare versions. This improves fault tolerance, but it is not magic consistency. Sloppy quorums and hinted handoff can keep the system available during failures, but they also weaken the simple mental model that a quorum always means “the same set of replicas agreed.”

Replication Lag Is A Product Problem

Synchronous replication waits for replicas before acknowledging a write. Asynchronous replication acknowledges quickly and lets replicas catch up later.

ModeBenefitRisk
SynchronousStronger consistencyHigher latency and lower availability
AsynchronousFast writes and better availabilityStale reads or possible data loss

Most production systems choose a middle ground because fully synchronous replication everywhere is slow, and fully asynchronous replication can surprise users.

Replication lag creates real product bugs:

  • A user updates a profile and refreshes into old data.
  • A comment reply appears before the original comment.
  • A user moves between replicas and appears to go backward in time.

DDIA gives names to useful guarantees:

GuaranteeWhat It Protects
Read-your-writesUsers see their own writes
Monotonic readsUsers do not observe time going backward
Consistent prefixCausal order is preserved

These are weaker than full linearizability, but often much closer to what users actually need.

Engineering takeaway: replication lag is not only a database detail. It changes the user experience and the promises the product can make.

Conflicts Need A Policy

Multi-leader and leaderless systems can accept concurrent writes to the same logical data. Once that happens, the system needs a conflict policy.

Conflict StrategyBenefitRisk
Last write winsEasy to implementCan silently lose data
Application mergeDomain-awareHard to design correctly
Version vectorsTracks concurrencyMore metadata and complexity
Human resolutionGood for rare semantic conflictsDoes not scale for frequent conflicts

Last write wins is tempting because it is simple. It is also dangerous because it can throw away data without telling anyone.

Application-defined merge logic can be much better. A shopping cart can merge added items. A collaborative editor needs a deeper strategy. A financial ledger should probably avoid ambiguous concurrent mutation entirely.

Conflict resolution is not an afterthought. It is part of the data model.

Partitioning Splits The Dataset

Replication copies data. Partitioning divides it.

When data or load outgrows one machine, partitions let different nodes own different slices of the dataset. The goal is to spread storage, reads, writes, and query execution.

StrategyStrong FitWeakness
Key-range partitioningRange queries and ordered scansHot spots with sequential keys
Hash partitioningEven load distributionPoor range queries

Key-range partitioning keeps nearby keys together. That is excellent for range scans, but dangerous if new writes cluster in one range, such as timestamp-based keys.

Hash partitioning spreads keys evenly. That helps load balance, but destroys ordering. Range queries become expensive because the relevant keys may be scattered everywhere.

Hybrid designs are common. A system might partition by hashed user ID but sort events by timestamp inside each user’s partition.

Secondary Indexes Complicate Distribution

Primary-key partitioning is only the first problem. Real systems also need secondary indexes.

Index TypeHow It WorksTrade-off
Local indexEach partition indexes only its own dataSimple writes, scatter-gather reads
Global indexIndex is partitioned by indexed valueTargeted reads, more expensive writes

Local indexes keep writes simple because the primary partition can update its own index. But a query by secondary attribute may need to ask every partition.

Global indexes make reads more targeted, but writes may touch multiple partitions or require asynchronous index maintenance.

This is a recurring distributed systems pattern: optimizing one path usually makes another path more expensive.

Rebalancing And Routing Are Operational Concerns

Clusters change. Data grows, nodes fail, and new machines are added.

Rebalancing moves partitions so the cluster remains balanced. Good rebalancing should avoid moving too much data, overloading the system, or breaking routing while requests are still flowing.

Routing then decides how clients find the right partition.

Routing StrategyDescription
Request any nodeThe contacted node forwards to the owner
Routing tierA partition-aware layer sends requests to the right node
Client-side routingClients know the partition map directly

The more distributed a system gets, the more metadata matters. Partition maps, membership, ownership, and failure detection become part of the system’s correctness story.

Engineering takeaway: partitioning creates scale, but it also creates routing, indexing, and rebalancing work that must be designed explicitly.

Transactions Define The Correctness Contract

Transactions let application code pretend that a group of reads and writes happens as one unit.

The familiar ACID properties are:

PropertyMeaning
AtomicityAll writes happen, or none do
ConsistencyApplication-defined invariants are preserved
IsolationConcurrent transactions do not interfere unexpectedly
DurabilityCommitted data survives crashes

Isolation is the property that causes the most surprises.

Isolation LevelGuaranteeStill To Watch
Read committedPrevents dirty reads and dirty writesNon-repeatable reads
Snapshot isolationGives each transaction a consistent snapshotWrite skew and some phantoms
SerializableBehaves as if transactions ran one at a timeUsually more coordination cost

Read committed is common and useful, but it does not make a transaction see a stable snapshot.

Snapshot isolation gives each transaction a consistent view of the database, often using MVCC. This is excellent for read-heavy workloads because readers and writers do not block each other as much.

But snapshot isolation is not serializability. Write skew can still happen when two transactions read overlapping data, make decisions that look valid independently, and then write different rows that violate a shared invariant.

Serializable isolation is the strongest model. Transactions behave as if they ran one at a time in some order, which is why it rules out anomalies such as lost updates, write skew, and phantoms when implemented correctly.

Serializable StrategyIdeaCost
Actual serial executionRun transactions one at a timeLimited throughput per execution lane
Two-phase lockingBlock conflicting reads/writesContention and deadlocks
Serializable snapshot isolationRun optimistically, abort unsafe commitsRetries and conflict detection

Engineering takeaway: weak isolation prevents some bugs, not all bugs. Know which anomalies your database allows before relying on it for invariants.

What I’d Remember In Practice

  • Replication improves availability and latency, but creates stale reads and conflicts.
  • Product semantics should drive consistency guarantees, not database defaults.
  • Conflict resolution is part of the data model.
  • Partitioning scales load, but secondary indexes and routing become harder.
  • Rebalancing is an operational feature, not just a background chore.
  • Transactions are only as safe as their isolation level.
  • Serializable isolation is the clean correctness model, but it has real coordination cost.