Dirty Write Explained: When Uncommitted Data Gets Overwritten
Deep Dive: Why Cross-Table Dirty Writes Create Permanently Inconsistent State
A dirty write occurs when Transaction B overwrites data that Transaction A has written but not yet committed.
🎯 System Scenario & Requirements
System Goal
A dirty write occurs when Transaction B overwrites data that Transaction A has written but not yet committed.
Actors & Components
- Application Workers
- Volatile Memory Tier
- Persistent Disk Tier
- Worker Process
- In-Memory Cache
- Primary Database
- Edge Traffic Ingress
- Stateless Microservices
Functional Requirements
- TLDR: A dirty write occurs when Transaction B overwrites data that Transaction A has written but not yet committed.
- The result is not a rollback or an error — it is silently inconsistent committed data: one table reflects Transaction B's intent, another still reflects Transaction A's.
- Every mainstream relational database prevents dirty writes automatically via row-level write locks held until commit.
Constraints & Assumptions
- It is 14:32:01 on a Saturday afternoon.
- A live car auction is in its final seconds.
⚡ Execution Stages & Decision Trace
Stage 1 — What Makes a Write "Dirty": Uncommitted Data in the Crossfire
The word "dirty" in database terminology always means uncommitted .
Data that has been written by a transaction but not yet committed exists in a state of uncertainty — it may be rolled back, it may be committed, but it has no permanent reality yet.
Deep Dive
Scenario context
A dirty write occurs when Transaction B overwrites data that Transaction A has written but not yet committed. The result is not a rollback or an error — it is silently inconsistent committed data: one table reflects Transaction B's intent, another still reflects Transaction A's. Every mainstream relational database prevents dirty writes automatically via row-level write locks held until commit. The danger re-emerges…
This walkthrough focuses on Deep Dive: Why Cross-Table Dirty Writes Create Permanently Inconsistent State.
Before making a design choice, clarify:
- The auction example involves two tables: cars and invoices .
- This is not incidental — it is what makes dirty writes particularly devastating in practice.
- When a transaction spans multiple tables, its business logic depends on all writes succeeding or failing together.
Concepts in play: database-isolation-levels-anomalies, caching, consistency-replication, jvm-internals-and-garbage-collection
Stage 1 — What Makes a Write "Dirty": Uncommitted Data in the Crossfire
- The word "dirty" in database terminology always means uncommitted .
- Data that has been written by a transaction but not yet committed exists in a state of uncertainty — it may be rolled back, it may be committed, but it has no permanent reality yet.
- A dirty read occurs when Transaction B reads that uncertain, uncommitted data.
- It is looking at something that may never have existed.
Observable outcome: It is looking at something that may never have existed.
Stage 2 — How Dirty Writes Corrupt Two Tables at Once: The Step-by-Step Mechanics
- The sequence below shows exactly how the auction failure unfolds at the database level, without row-level locking protection.
- Each arrow is a database operation; the Notes show the intermediate state of the cars row.
- The key moment is the second UPDATE by T2 Bob: it hits the cars row while T1 Alice's write is still open, overwriting it before T1 has any chance to commit or roll back.
- From that point on, T1 is writing to the invoices table based on the assumption that it owns the car — but the car row no longer reflects T1's intent.
sequenceDiagram
participant T1 as T1 Alice
participant DB as Database
participant T2 as T2 Bob
T1->>DB: BEGIN
T1->>DB: UPDATE cars SET owner=alice WHERE id=CAR-4521
Note over DB: cars row: owner=alice, uncommitted. T1 still open.
T2->>DB: BEGIN
T2->>DB: UPDATE cars SET owner=bob WHERE id=CAR-4521
Note over DB: Dirty write. Bob overwrites Alice uncommitted row.
T1->>DB: UPDATE invoices SET buyer=alice, amount=28500
T1->>DB: COMMIT
Note over DB: cars.owner=bob, invoices.buyer=alice. Both committed.
T2->>DB: COMMIT
Note over DB: Final state is permanently inconsistent across tables.
Observable outcome: From that point on, T1 is writing to the invoices table based on the assumption that it owns the car — but the car row no longer reflects T1's intent.
Stage 3 — Concurrent Buyer Requests, One Database Row: The System Architecture View
- The following diagram shows how the auction service's two concurrent request threads interact with the cars and invoices databases.
- The two UPDATE arrows converging on the Car DB node represent the dirty write — both threads are writing to the same row without serialization, producing the inconsistent committed state at the bottom.
- This architecture-level view makes clear why application-layer validation is insufficient: both threads passed the business validation step (both confirmed the car was available), and both proceeded to write.
- The race condition lives entirely within the database write path.
graph TD
A[Buyer A Browser] -->|POST /auction/bid at T=103ms| B[Auction Service]
C[Buyer B Browser] -->|POST /auction/bid at T=187ms| B
B -->|T1: UPDATE cars SET owner=alice| D[Car DB - CAR-4521 row]
B -->|T2: overwrite owner=bob before T1 commits| D
B -->|T1: UPDATE invoices SET buyer=alice| E[Invoice DB - INV-9901 row]
D -->|After both commits| F[cars.owner = bob]
E -->|After T1 commit| G[invoices.buyer = alice]
F --> H[Permanently inconsistent state across tables]
G --> H
Observable outcome: The race condition lives entirely within the database write path.
Stage 4 — Which Isolation Levels Prevent Dirty Writes — and Which Anomalies They Still Allow
- Dirty writes sit at the absolute bottom of the anomaly severity scale: they are prevented by every practical isolation level.
- The table below shows the full picture across all standard levels.
- In practice, even Read Uncommitted in PostgreSQL, MySQL, and SQL Server acquires write locks, making dirty writes impossible regardless of stated isolation level.
- † MySQL InnoDB Repeatable Read prevents phantom reads via gap locks.
Observable outcome: † MySQL InnoDB Repeatable Read prevents phantom reads via gap locks.
Stage 5 — When Dirty Writes Re-Emerge in the Real World: Distributed System Edge Cases
- Within a single relational database, dirty writes are a solved problem.
- The challenge reappears when data is distributed across nodes, services, or stores that lack a unified lock manager.
- In a multi-master setup (CouchDB, MySQL Group Replication in multi-primary mode, Cassandra with multiple coordinators), two nodes can accept conflicting writes to the same logical record concurrently.
- Each node acquires its local row lock, preventing dirty writes on that node , but there is no global lock across nodes.
Observable outcome: Each node acquires its local row lock, preventing dirty writes on that node , but there is no global lock across nodes.
Stage 6 — All Seven Concurrency Anomalies: Where Dirty Write Fits in the Map
- Understanding dirty writes fully requires placing them in the context of the complete anomaly taxonomy.
- The table below maps every known concurrency anomaly across the four dimensions that determine where it sits in the prevention hierarchy.
- Dirty write and dirty read are the only two anomalies that involve uncommitted data.
- They are prevented earliest in the isolation hierarchy (Read Committed).
Observable outcome: They are prevented earliest in the isolation hierarchy (Read Committed).
End-to-end outcome
After working through the stages, you should be able to explain:
- Default isolation levels in relational databases silently prevent dirty writes — but that protection ends at the database boundary.
- Every SQL database at Read Committed or above blocks dirty writes.
- A dirty write is the result of Transaction B overwriting Transaction A's uncommitted data before T1 finishes.
- Because both transactions ultimately commit, the resulting inconsistency is permanent: different tables reflect the intents of different transactions, with no error, no rollback, and no automatic detection.
Alternatives considered
Choosing Write Isolation: Consistency Guarantees vs. Concurrent Throughput
Preventing dirty writes is not free.
Failure and degraded modes
One Car, Two Owners: The Auction Failure That Should Never Happen
- Detection: It is 14:32:01 on a Saturday afternoon.
- User or operator impact: It is 14:32:01 on a Saturday afternoon.
