Concurrency in Distributed Data Systems
An examination of isolation, MVCC, lost updates, write skew, replication lag, conflict resolution, and serializability in distributed data systems.
The happens-before relationship, which establishes whether one operation occurred before another, forms the mathematical basis of concurrency in data systems. When two operations are unaware of each other and share no causal dependency, they are considered concurrent regardless of their physical timestamps. Concurrent reads and writes against shared data can produce inconsistent state and race conditions. Transaction mechanisms group operations into a logical unit and rely on atomicity, consistency, isolation, and durability guarantees to prevent these anomalies.
Isolation prevents concurrently executing transactions from observing each other's intermediate state. It aims to make the system behave as though transactions had run serially. Fully serializable mechanisms can be expensive, so modern databases often implement weaker isolation algorithms.
Weak Isolation and MVCC
Read committed, one of the basic transaction isolation levels, prevents dirty reads and dirty writes. Databases usually block concurrent writers with row-level locks so that only one transaction can update a row at a time. To prevent dirty reads without paying the cost of read locks, they expose the previous committed version until the active write completes.
Read committed still permits read skew or nonrepeatable reads, where a transaction obtains different results when it reads the same data at different times. Long analytical queries and backup operations can therefore observe a persistently inconsistent view. Snapshot isolation addresses this problem by allowing each transaction to read from a consistent snapshot taken at its start and hiding later changes made by other transactions.
This behavior is commonly implemented with Multi-Version Concurrency Control, or MVCC. Its central principle is that readers do not block writers and writers do not block readers. The database keeps several versions of each row and labels them with unique, monotonically increasing transaction identifiers. A row version is visible only if the transaction that created it committed before the reader and the row had not been deleted when the reader began.
Lost Updates and Write Skew
MVCC resolves many read anomalies, but races between concurrent writers still appear in different forms. In a read-modify-write cycle, two transactions may read the same value and then overwrite one another. The result is a lost update. Atomic write operations, explicit locking with SELECT FOR UPDATE, or database-level conflict detection that aborts a transaction can prevent this anomaly. Behavior varies by product and version. Under some conditions, MySQL/InnoDB at repeatable read does not automatically detect lost updates, while PostgreSQL can reject the conflict.
Write skew is more subtle. It occurs when two transactions read the same objects, make decisions from that shared premise, and then update different objects. Consider a rule requiring at least one doctor to remain on duty. Two doctors can concurrently observe that the rule is satisfied and each remove only their own duty record. Both writes succeed, but the shared premise becomes false.
A phantom occurs when one transaction's write changes the result set of another transaction's search condition. Snapshot isolation protects read-only queries from phantoms, but it does not stop them from causing write skew in read-write cycles. When no existing row can be locked, an application can insert artificial records to materialize the conflict. This technique moves concurrency control into the application data model and should generally remain a last resort.
Replication Lag and Consistency
When a system moves from a single node to distributed replication, isolation interacts directly with network failures and replication lag. In asynchronous single-leader replication, reads directed to follower nodes for scaling may return stale data. The system then provides only eventual consistency for those reads.
Replication lag can also prevent users from immediately seeing their own changes. Read-after-write consistency routes data recently modified by a user to the leader or to a synchronized follower. Sequential reads from different replicas can create the impression that time has moved backward when a later request reaches an older copy. Monotonic reads avoid this by keeping a user on a replica whose state is at least as recent as the state previously observed. Consistent-prefix reads address a related causal problem. They prevent effects from becoming visible before their causes when related writes replicate at different speeds.
Conflicts in Multi-Leader and Leaderless Systems
Multi-leader and leaderless topologies allow writes to occur at several nodes at the same time. A single global order cannot be imposed without additional coordination. These designs tolerate node failures and regional network partitions well, but concurrent write conflicts are unavoidable.
Last-write-wins, or LWW, assigns a timestamp to every write and retains the value with the greatest timestamp. For genuinely concurrent operations, timestamp order does not express a meaningful causal order. LWW can therefore discard writes that were accepted successfully and create permanent data loss.
Conflict-free Replicated Data Types and Operational Transformation provide more deterministic forms of automatic conflict resolution. OT transforms character positions according to concurrent operations that have already been applied. CRDTs instead give each element a unique and immutable identifier and derive positions from those identifiers. Local-first synchronization engines use these methods to let devices work against local copies while offline and merge concurrent updates after connectivity returns.
Serializability with 2PL, Serial Execution, and SSI
Preventing phantoms, write skew, and related anomalies requires a true serializability guarantee. Single-node and distributed systems generally reach this guarantee through three main approaches.
Two-Phase Locking, or 2PL, is the traditional pessimistic method. Writers block other writers and readers, while readers delay writers. Transactions acquire locks while running and release them at the end. In theory, phantom prevention requires predicate locks covering an entire search condition. Their cost is high, so implementations often approximate them with index-range locks. Although 2PL prevents the full set of isolation anomalies, it reduces concurrency and is vulnerable to deadlocks, which can make latency unstable.
Actual serial execution removes concurrency coordination from the transaction engine. Every transaction runs sequentially on one thread. This design can deliver high throughput when the active data set fits in memory and transactions execute as predefined stored procedures without waiting for network or disk I/O.
Serializable Snapshot Isolation, or SSI, combines the performance properties of snapshot isolation with serializability. It was introduced in the literature in 2008 as an optimistic algorithm. Transactions run without waiting for locks, but the database checks for isolation violations before commit. It tracks whether a transaction read from an older MVCC snapshot and whether a later write invalidated a value or predicate on which the transaction depended. When a concurrent write makes that premise stale, the transaction is aborted and must be retried. Readers do not block writers under SSI, which makes the method attractive for read-heavy workloads.
Isolation in data-intensive systems is not merely a hardware optimization. It defines the computational rules used to validate mutations under concurrency. Engineers must understand the boundaries of logical causality if they are to prevent corruption under disk delay, replica lag, and network partition. The choice between 2PL, which enforces integrity before commit, and replicated designs that reconcile conflicts later with structures such as CRDTs depends on the failure cases the physical network permits the application to tolerate. Operational stability requires the isolation model assumed by application logic to match the mathematical guarantees that the deployed database can actually provide.