Database

ACID in Database Management

I once watched a bank transfer silently eat $4,000. The debit went through. The credit didn't. No error, no rollback, no trace. The database wasn't config...

29 Apr 2024

ACID in Database Management

I once watched a bank transfer silently eat $4,000. The debit went through. The credit didn't. No error, no rollback, no trace. The database wasn't configured for proper transaction management.

That's the problem ACID solves.

ACID stands for Atomicity, Consistency, Isolation, and Durability. These four properties guarantee that database transactions behave predictably, even when things go wrong. If your system handles money, inventory, or anything where partial updates cause real damage, you need ACID.

Atomicity

A transaction is all or nothing. Every operation inside it either completes successfully, or the entire thing rolls back. There's no halfway state.

Think of it like a light switch. It's on or off. You never get stuck at 50%.

If you debit one account and the credit fails, atomicity ensures the debit is undone. Without it, money vanishes.

Consistency

Every transaction moves the database from one valid state to another. All constraints, foreign keys, and rules defined in your schema are enforced before and after.

If a transaction would violate a constraint, it's rejected. The database protects its own integrity.

Isolation

Concurrent transactions don't interfere with each other. Each one executes as if it's the only transaction running.

In practice, databases offer different isolation levels (Read Uncommitted, Read Committed, Repeatable Read, Serializable) because full isolation is expensive. The higher the isolation, the fewer concurrency bugs, but the more you pay in throughput.

Durability

Once a transaction commits, it stays committed. Even if the server crashes, the power goes out, or the disk fails, committed data survives.

Databases achieve this through write-ahead logs (WAL). Changes are written to a log on disk before they're applied. On recovery, the database replays the log to restore the last consistent state.

What This Looks Like in Code

Typescript
async function transferFunds(
  senderId: number,
  recipientId: number,
  amount: number
): Promise<void> {
  const tx = await database.beginTransaction();
  try {
    await tx.execute(
      `UPDATE accounts SET balance = balance - $1 WHERE id = $2`,
      [amount, senderId]
    );
    await tx.execute(
      `UPDATE accounts SET balance = balance + $1 WHERE id = $2`,
      [amount, recipientId]
    );
    await tx.commit();
  } catch (error) {
    await tx.rollback();
    throw error;
  }
}

If anything fails between the debit and credit, rollback() undoes everything. That's atomicity in action.

The Trade-off

ACID gives you safety. But it costs performance. Every guarantee requires coordination: locks, logs, synchronization. Under heavy load, strict ACID compliance can become a bottleneck.

That's why NoSQL databases often relax these guarantees in favor of throughput and availability (see BASE principles). The right choice depends on what failure mode scares you more: inconsistent data, or slow responses.

For financial systems, healthcare, and anything where correctness matters more than speed, ACID isn't optional. It's the foundation.

Keep reading