Atomicity
The "A" in ACID, and the property Chapter 01's entire bank story was really about. This chapter goes from the plain-English idea to exactly how a database engine pulls it off.
| Level | Definition |
|---|---|
| Beginner | All the steps in a transaction happen together, like a single action — or none of them happen at all. |
| Technical | Atomicity guarantees that a transaction's set of operations is applied as an indivisible unit — a partial application is never observable, whether the transaction succeeds, fails midway, or the system crashes. |
| Interview-grade | Atomicity is implemented via undo logging (or an MVCC equivalent): every change a transaction makes is recorded such that it can be fully reversed, and the database guarantees that either every recorded change is made permanent at commit, or every one is reversed at rollback or crash recovery. |
◆ Story
An ATM withdrawal is really two separate database-level (or system-level) effects: debit the account, and signal the cash dispenser. Picture the exact moment the machine's power fails between those two effects. Without atomicity, you either lose money (debited, no cash) or the bank loses money (cash dispensed, never debited) — and crucially, nobody chose either outcome; it's purely an accident of timing. Atomicity's entire job is to make that accident of timing impossible to observe: the debit is either fully applied or fully un-applied, deterministically, regardless of exactly when the crash happened.
◆ Under the hood
Most relational databases (PostgreSQL, MySQL/InnoDB) implement atomicity using an undo log: before a transaction modifies a row, the database first records the row's previous value in the undo log. If the transaction commits, the undo log entries for it are simply discarded — they're no longer needed. If the transaction rolls back (explicitly, or because the connection dies, or because the whole server crashes and restarts), the database walks the undo log backward and restores every changed row to its recorded previous value. This is exactly how a crash mid-transaction is recoverable: on restart, the database finds any transaction that has an undo log but no commit record, and rolls it back automatically as part of crash recovery — you never have to detect or handle this yourself.
The undo log is written before the actual row is modified — this ordering is what makes rollback and crash recovery possible even if the crash happens mid-write.
BEGIN; UPDATE accounts SET balance = balance - 10000 WHERE id = 'A'; UPDATE accounts SET balance = balance + 10000 WHERE id = 'B'; -- if BOTH updates succeeded: COMMIT; -- if EITHER failed (e.g. account B doesn't exist, constraint violation): ROLLBACK;
@Transactional public void transfer(String fromId, String toId, BigDecimal amount) { Account from = accountRepository.findById(fromId).orElseThrow(); Account to = accountRepository.findById(toId).orElseThrow(); if (from.getBalance().compareTo(amount) < 0) { throw new InsufficientFundsException(fromId); // triggers rollback of BOTH updates below — see Chapter 08 } from.setBalance(from.getBalance().subtract(amount)); to.setBalance(to.getBalance().add(amount)); // no explicit commit call — Spring commits automatically if the method returns normally }
💻 Code example
BEGIN; UPDATE accounts SET balance = balance - 10000 WHERE id = 'A'; UPDATE accounts SET balance = balance + 10000 WHERE id = 'B'; -- if BOTH updates succeeded: COMMIT; -- if EITHER failed (e.g. account B doesn't exist, constraint violation): ROLLBACK;
| Failure | Guaranteed outcome |
|---|---|
| Application throws an exception mid-transfer | ROLLBACK — neither account is changed |
| Database server crashes after the first UPDATE, before COMMIT | ROLLBACK — undo log replayed on restart |
| Network connection drops after both UPDATEs but before COMMIT is acknowledged | ROLLBACK — an uncommitted transaction is never made durable |
| Both UPDATEs succeed and COMMIT is acknowledged | COMMIT — both changes are permanent (Durability, Chapter 05) |
▲ Common mistake
Atomicity guarantees "all statements in the transaction," not "all side effects of my business logic." Sending a confirmation email or calling a third-party payment API inside a database transaction is not rolled back by a database ROLLBACK — the email still sends, the API call still fired. This is precisely the seed of the distributed transaction problem covered starting in Chapter 15: atomicity is a database-transaction concept, not a "my whole method" concept.
Want a visual for this concept?
Generate a diagram tailored to “Atomicity” — the AI picks whichever visual (flowchart, comparison, sequence, etc.) best fits.
Sign in to generate a visual →