Taryana
Back to All Technical Articles/Demystifying Database ACID Transactions: 4 Pillars of Data Integrity and Concurrency Anomaly Mitigations
DatabaseACIDTransactionsArchitectureConcurrencyPostgreSQL

Demystifying Database ACID Transactions: 4 Pillars of Data Integrity and Concurrency Anomaly Mitigations

Lessons from real production war stories handling negative balances, phantom rows, and write skew disasters: how the 4 ACID pillars and transaction isolation strategies safeguard data integrity under heavy concurrency.

Taryana•2026-10-04•13 min read

1. Why Application-Level Validation Inevitably Collapses Under Concurrency

Have you ever experienced that sinking feeling when your e-commerce platform or digital wallet ledger suddenly reveals an unaccounted cash discrepancy, user accounts drop into negative balances, or two on-call emergency doctors manage to book the exact same leave slot simultaneously, leaving an ER ward completely unstaffed?

That exact nerve-wracking incident is what my engineering team and I wrestled with when we first launched a high-throughput order and balance mutation system serving thousands of transactions per second. In the early stages, our application code looked deceptively bulletproof:

TypeScript CRDT Reference6 baris kode
> // A naive early approach that passed 100% of unit tests locally | const sender = await getUser(senderId); ...Klik untuk melihat implementasi lengkap →

In a local development environment with manual testing, the logic above always succeeded. But the moment the platform was deployed into production and bombarded by thousands of real-world users, catastrophic emerged:

  • The Outage: The server process crashed or hit a network timeout immediately after debitUser finished executing, before creditUser ever ran. The sender's money vanished into thin air, but the recipient received nothing.
  • The Negative Balance : Two withdrawal requests hit the server in the exact same millisecond across separate worker threads. Both threads read the same initial balance ($1,000), both passed the if (sender.balance >= 800) check, and both deducted money. The balance plummeted to -$600 without triggering a single application-level error.
  • The Leave Quota Meltdown: Two doctors opened the hospital portal and saw the status: "2 doctors currently on-duty, minimum requirement of 1 is met". Both clicked "Request Time Off" in the exact same second. Both transactions committed cleanly because each checked its own snapshot, leaving the emergency room with exactly 0 doctors on duty!

From these painful production incidents, we learned an irreplaceable truth: you cannot guarantee data integrity simply by writing procedural checks in your application code (Node.js, Go, Python, etc.). You need rock-solid mathematical and architectural guarantees enforced directly by the database engine: ACID (, Consistency, Isolation, Durability).

Executive Summary & Core Insight

Production Architecture Blueprint: 4 ACID Pillars & Isolation Strategy To preserve invariant guarantees without throttling system throughput, we architected a multi-layered defense:

  1. 1 (All-or-Nothing Guarantee): Every mutation is wrapped in an indivisible unit of work. If step 5 of a 6-step workflow fails, the database automatically rolls back all previous 4 steps cleanly to the initial state.
  2. 2Consistency (State Invariants Preservation): Two-layer invariant protection: declarative database schema constraints (CHECK (balance >= 0), FOREIGN KEY) as the ultimate gatekeeper, paired with application-level domain validations.
  3. 3Isolation ( Boundary): Precise selection of (Read Committed, Repeatable Read, or Serializable) to repel 4 fatal read anomalies.
  4. 4Durability (Non-Volatile Persistence): Guaranteeing that a COMMIT SUCCESS acknowledgement survives power loss, kernel panics, and hardware crashes through non-volatile storage persistence.
Diagram Arsitektur: 4 Pilar ACID dan Mitigasi Anomali KonkurensiACID Transactions
Skenario Alur:
Pinch touchpad / scroll untuk zoom • Klik-drag untuk geser (kiri, kanan, atas, bawah)
Transaksi Konkuren (Apps)Transaksi 1 (Worker A)Transfer $300 (Debit A)Transaksi 2 (Worker B)Transfer $300 (Credit B)Batas EksekusiBEGIN ... COMMIT / ROLLBACKDemarkasi Isolasi & ValidasiIsolation LevelREAD COMMITTEDPencegahan AnomaliNo Partial WritesMekanisme KontrolPessimistic / Snapshot BoundaryDatabase ConstraintsCheck InvariantCHECK (balance >= 0)Relational IntegrityFOREIGN KEY & UNIQUEBusiness RulesTotal Saldo A + B = $1,500Durability (Disk)Commit GuaranteeNon-Volatile StorageCrash ResistanceZero Lost DataState DurabilityCOMMIT = IMMUTABLESQLVerifyPersist
Atomicity: Eksekusi all-or-nothing. Jika terjadi error di langkah parsial, seluruh mutasi dibatalkan 100% via Rollback.
Integritas ACIDZero Data Anomaly

2. 4-Pillar ACID Topology (In-Depth Architecture Matrix)

Before diving into anomalies and mitigation strategies, let us inspect how each ACID pillar shares specific responsibilities:

ACID PillarFormal GuaranteeEnforcement LayerRecovery & Defense MechanismReal Production Problem Solved
All-or-Nothing (Indivisible unit of work)RDBMS Engine Core Rollback & Undo StatePrevents hanging money when server crashes mid-transfer
ConsistencyState Invariant Preservation (Valid state transitions)Database Schema & Domain LogicCheck Constraints, Foreign Keys, RulesPrevents negative balances, orphaned records, and broken rules
Isolation Demarcation (Separating parallel transactions) Lock ManagerPessimistic Lock, MVCC, Serializable EngineEliminates , , and
DurabilityNon-Volatile Persistence ( survival)Storage Engine & Disk SubsystemNon-volatile sync commit confirmationGuarantees committed data survives unexpected power cuts

3. Deconstructing the 4 ACID Pillars: Production War Stories

A. Atomicity: The All-or-Nothing Principle

Problem: The "Lost Money" Incident When Server Crashes Mid-Transfer

In a peer-to-peer balance transfer, operations must Account A and credit Account B. Early in our architecture, our network connection between the API and the database dropped immediately after the step completed. The result? Account A lost $500, but Account B never received it. Manual took hours and damaged customer trust.

Solution: Unified Transaction Boundaries

ensures that operations are sealed within an unshakeable boundary:

PostgreSQL Schema Reference5 baris kode
> BEGIN; | UPDATE accounts SET balance = balance - 500 WHERE id = 'ACC-A'; ...Klik untuk melihat implementasi lengkap →

If the second step fails or connection terminates before COMMIT is safely acknowledged, the RDBMS automatically executes an rollback. Account A is instantaneously restored to its pre-transaction balance, leaving no trace of partial corruption.


B. Consistency: Protecting Invariants and Valid States

Problem: Invariant Violations from Fragile Application-Level Checks

Engineers often assume controller validations are enough: "We already have if (balance < 0) throw new BadRequest() in TypeScript!". Yet, during manual database migrations, admin bulk updates, or unforeseen , records with -$250 balances slipped through, corrupting financial reports.

Solution: Two-Layer Invariant Defense

We enforce invariants across two strict layers:

  1. 1Database-Enforced Consistency: Declarative constraints that no query can bypass:
PostgreSQL Schema Reference2 baris kode
> ALTER TABLE accounts ADD CONSTRAINT chk_positive_balance CHECK (balance >= 0); | ALTER TABLE orders ADD CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE RESTRICT; ...Klik untuk melihat implementasi lengkap →
  1. 2Application-Enforced Consistency: Preserving conservation laws, such as ensuring that the global sum of internal ledger accounts remains unchanged before and after an internal transfer.

C. Isolation: Demarcating Concurrent Transactions

Problem: Transactions Overwriting and Peeking at Uncommitted Work

When hundreds of users concurrently update the same inventory rows, interleaved query execution leads to catastrophic lost updates and where shoppers see items that were never actually reserved.

Solution: Strict Isolation Boundaries

Isolation creates a computational illusion: each executing transaction behaves as if it were the sole transaction running across the entire database engine. The strictness of this barrier is configured through .


D. Durability: Non-Volatile Persistence Guarantee

Problem: Vanishing Transactions After Sudden Data Center Power Outages

Imagine a user seeing "Payment Confirmed! Order #TX-9988", but a second later the data center suffers a sudden blackout. If updates were only held in volatile RAM without disk synchronization, that payment disappears forever upon reboot.

Solution: Confirmed Non-Volatile Persistence

Durability guarantees that once a client receives COMMIT SUCCESS, changes are guaranteed to be stored in non-volatile storage. Even if the server loses power a millisecond later, the database engine reconstructs the entire committed state upon reboot.


4. Concurrency Anomaly Taxonomy in Production

Without rigorous transaction isolation, your system is vulnerable to 4 deadly anomalies:

TEXT Reference6 baris kode
> [ Transaction A ] ─── (Reads Data / Range) ─────┐ | ├──> [ CONCURRENCY ANOMALY ] ...Klik untuk melihat implementasi lengkap →

1. Dirty Read (G1a)

Problem:

Transaction A reads uncommitted mutations made by Transaction B. Transaction B then fails and rolls back. Transaction A has now acted upon fictitious data that never formally existed in the system.

Real-World Scenario:

A cashier sees an invoice marked as "PAID". Seconds later, the customer's payment transaction fails and rolls back. The cashier hands over the merchandise to a customer whose payment never cleared.


2. Non-Repeatable Read (Fuzzy Read - P2)

Problem:

Transaction A reads row X at T1 (balance: 1,000). At T2, Transaction B deducts 300 and commits. When Transaction A re-reads row X at T3 within the same transaction, the value has mutated to $700.

Real-World Scenario:

A financial auditor prints a balance summary where the header total does not match the ledger footer because another transaction modified balances mid-audit.


3. Phantom Read (A3)

Problem:

Occurs in range queries. Transaction A counts pending orders: SELECT COUNT(*) FROM orders WHERE status = 'PENDING'; (Result: 5). Transaction B inserts a new pending order and commits. When Transaction A re-runs the range query within the same transaction, the count jumps to 6. The newly appeared record is a .

Real-World Scenario:

A warehouse system dispatches 5 delivery vans based on the initial query, but leaves the 6th phantom order stranded at the dock.


4. Serialization Anomaly & Write Skew (A5B)

Problem:

The most subtle and catastrophic anomaly in modern systems. Occurs when two transactions read overlapping datasets, each performs a valid check based on its local snapshot, and both commit mutations to different rows that together violate a global system invariant.

Production Incident & Bottleneck Case

Real Production Case: On-Call Emergency Doctor Quota System Invariant: "At least 1 doctor must remain active on-duty at all times." Initial State: Doctor A is active, Doctor B is active (Total = 2 on duty).

  • Transaction 1 (Doctor A requests leave): Checks SELECT COUNT(*) FROM on_call WHERE active = true; -> Result = 2 (Safe, > 1). Sets Doctor A to inactive.
  • Transaction 2 (Doctor B requests leave - ): Checks SELECT COUNT(*) FROM on_call WHERE active = true; -> Result = 2 (Safe, > 1). Sets Doctor B to inactive.

Both transactions commit without row-level lock conflicts because each updated a separate row! The final database state: 0 doctors on duty. The emergency room is left completely unattended.


5. ANSI SQL Isolation Levels vs Anomaly Protection Matrix

defines four isolation tiers balancing protection against throughput:

LevelProduction Best Use Case
Read UncommittedVulnerableVulnerableVulnerableVulnerableFast approximate telemetry / Non-financial analytics
Read CommittedPreventedVulnerableVulnerableVulnerableDefault for most RDBMS (Standard OLTP)
Repeatable ReadPreventedPreventedPrevented (In PostgreSQL)VulnerableInventory auditing, consistent reports
SerializablePreventedPreventedPreventedPreventedBanking ledgers, ticket booking, quota systems

6. Production Concurrency Mitigation Strategies

To neutralize these anomalies without crashing throughput, we utilize three architectural patterns:

Approach 1: Pessimistic Locking (SELECT ... FOR UPDATE)

Best suited for high-contention workflows such as flash-sale inventory decrements or digital wallet balance deductions.

PostgreSQL Schema Reference7 baris kode
> BEGIN; | -- Exclusively locks row; concurrent transactions are forced to queue ...Klik untuk melihat implementasi lengkap →

[!TIP]

The Golden Anti-Deadlock Rule:

Always acquire row locks in deterministic ascending order (ORDER BY id ASC). If Transaction 1 locks A then B, while Transaction 2 locks B then A, a fatal deadlock is guaranteed!


Approach 2: Optimistic Concurrency Control (OCC)

Ideal for low-to-medium contention paths, such as updating user profiles or CMS documents. Avoids upfront locking by checking a version column.

PostgreSQL Schema Reference8 baris kode
> -- 1. Read without any locks (lightning-fast, non-blocking) | SELECT id, balance, version FROM accounts WHERE id = 'ACC-101'; ...Klik untuk melihat implementasi lengkap →

If the update affects 0 rows, another transaction modified the record first. The application catches this and retries.


Approach 3: Serializable Isolation with Automatic Retry Loops

Enforces the highest isolation tier: SERIALIZABLE. The database engine actively monitors dependency graphs. If a hazard is detected, the database aborts one transaction with error code 40001 (serialization_failure). The backend application then automatically retries with full jitter.


7. Production Code Implementation: Transaction Boundary Runner (TypeScript)

Here is the production-tested Transaction Boundary Runner in Node.js / TypeScript that handles retryable failures (40001 serialization failures and 40P01 deadlocks) with exponential backoff and full jitter:

TypeScript CRDT Reference75 baris kode
> // src/libs/database/executeAcidTransaction.ts | import { Pool, PoolClient } from 'pg'; ...Klik untuk melihat implementasi lengkap →

8. High-Scale Production Transaction Checklist

Based on our journey safeguarding high-volume transaction systems, keep this checklist handy before launching features:

  1. 1Keep Transaction Boundaries As Lean As Possible: Never make external HTTP calls (payment gateways, webhooks, SMS) inside BEGIN ... COMMIT. External network latency holds row locks indefinitely, causing rapid starvation.
  2. 2Combine Read Committed with Selective Pessimistic Locking: For 90% of your application, standard Read Committed is ideal. Selectively add SELECT ... FOR UPDATE on sensitive balance and stock rows for minimal computational overhead.
  3. 3Design Operations: Ensure financial mutations accept an key so automatic retry loops never result in duplicate balance debits.
  4. 4Enforce Two-Tier Invariant Checks: Never rely solely on application code for data validity; declare database constraints (CHECK balance >= 0) as an impenetrable last line of defense.
ACID Database Engine Simulator
Memuat lab interaktif...

Ingin bereksplorasi lebih dalam?

Buka seluruh koleksi simulator sistem terdistribusi di Central Sandbox Playground.

Buka Central Sandbox →