skip to content

ACID Guarantees

The four promises a transactional engine makes — atomicity, consistency, isolation, durability — taken one at a time, with the mechanism that delivers each. Reciting the acronym is table stakes; interviewers probe whether I know what each letter actually costs the engine and where the guarantee has fine print.

part ofRelational database conceptsoverview, primer and where to startread it →
on this pageshow

questions

20

Atomicity is one of the four ACID properties of a database transaction. What does it guarantee, and what happens to the work a transaction has already done if it fails halfway through?

level: juniorimportance: must knowfreq 80%

answer

  1. all-or-nothing unit of work
  2. half-transfer state must not persist
  3. commit record = single flip point
  4. rollback discards uncommitted work, not a commit
  5. stops at the database boundary

basics

~20 s

Atomicity means a transaction is all-or-nothing: either every change it made becomes visible together, or none does. If it fails partway, the engine reverses the work already done, leaving the database as if the transaction never ran.

solid answer

~60 s

Atomicity is the all-or-nothing guarantee: a transaction's changes take effect as a single indivisible unit. There is no observable state in which half of a transfer happened — money debited but never credited. If the transaction fails partway, whether from an error, an explicit rollback, a lost connection, or a server crash, the engine undoes everything it had already applied. From the outside the database looks exactly as it did before the transaction started. Mechanically this rests on the engine recording enough information to reverse each change — undo records, or prior row versions kept in place — before or as the change is made, so reversal is always possible. Crash recovery uses the same machinery: a transaction with no durable commit record is rolled back during startup. Worth noting what it does not cover: atomicity says nothing about concurrent visibility (that is isolation) or about surviving a crash after commit (durability), and it stops at the database boundary — emails sent or HTTP calls made inside the transaction are not rolled back.

code

sql · 5 lines
sql
BEGIN;
  UPDATE accounts SET balance = balance - 100 WHERE id = 'A';
  UPDATE accounts SET balance = balance + 100 WHERE id = 'B';
COMMIT;
-- a crash anywhere before COMMIT leaves both rows unchanged

go deeper

for a junior

State the all-or-nothing rule with the transfer example and mention that a crash before commit leaves nothing behind.

for a middle

Add the mechanism — undo information plus a single durable commit record — and draw clean lines against isolation and durability.

for a senior

Discuss the boundary of the guarantee: external side effects, cross-database work, transaction sizing, and the cost of aborting large transactions.

for a principal

Reason about where the atomicity boundary should sit in a system design, when a distributed commit protocol is justified versus an outbox-style pattern, and what the organisation pays for each.

## The guarantee A transaction is a group of statements the database treats as one indivisible unit of work. Atomicity is the promise that this unit is applied entirely or not at all. There is no persistent outcome where some of its statements took effect and others did not. The canonical example is a transfer: 1. subtract 100 from account A 2. add 100 to account B If the system fails between the two, money has vanished. Atomicity ensures that outcome cannot survive: either both rows change or neither does. ## What counts as \"failing halfway\" Atomicity has to hold under several very different failures, which is why it needs real machinery rather than careful application code: - **A statement errors** — a constraint violation, a type error, a deadlock victim. The application (or the engine) ends the transaction and the work is discarded. - **The application decides to abort** — business logic finds a problem and rolls back deliberately. - **The session dies** — the client crashes or the network drops. The server eventually notices and rolls the transaction back; nothing it had done becomes visible. - **The server crashes** — on restart, any transaction without a durable commit record is undone as part of recovery. This is the case application code cannot possibly handle itself. In every case the end state is the same: as if the transaction never ran. ## How engines deliver it Two ingredients. **Reversal information.** Before a change becomes irreversible, the engine durably records how to undo it: a before-image of the row, an inverse operation, or — in multi-version engines — simply the prior version of the row, which stays in place while the new one is written. Rollback walks that information backwards. **A commit marker.** Committing is a single atomic act: writing the commit record. That one small durable write flips the transaction from \"nothing counts\" to \"everything counts\". This is the trick that makes an arbitrarily large set of changes atomic — the atomicity of the whole set is delegated to the atomicity of one record. Before it exists, recovery undoes the transaction; after it exists, recovery keeps and completes the transaction. Because of the second point, rollback is not really \"undoing a commit\" — nothing was ever committed. It is discarding uncommitted work. ## What atomicity is not Candidates lose points by conflating the four properties, so keep the boundaries sharp: - **Isolation** governs what *other* transactions see while yours is running. Atomicity alone would still permit another session to observe your half-finished state; it is isolation that forbids it. - **Durability** governs what survives a crash *after* commit. Atomicity says the unit is indivisible; durability says the committed unit does not evaporate. - **Consistency** in the ACID sense means the transaction moves the database from one valid state to another with respect to declared constraints. Atomicity is one of the mechanisms that helps, but a transaction can be perfectly atomic and still write nonsense. ## The boundary of the guarantee Atomicity covers changes *inside the database*. It does not extend to side effects performed by application code inside the transaction block: an email sent, a message published to a broker, a file written, a payment API called. If the transaction later rolls back, those effects remain. This is the dual-write problem, and it is why systems that need \"the row and the message both happen or neither\" write the message into a table in the same transaction and let a separate process deliver it. Similarly, a single transaction spanning two independent databases is not atomic by default; making it so needs a distributed commit protocol, with its own costs. ## Practical consequences Because abort is always available, the correct application pattern is to let errors propagate and roll back rather than to hand-write compensating updates. Compensation is error-prone precisely because it cannot cover the crash case. Because rollback must reverse work already done, aborting a very large transaction is not free — in some engines it costs as much as the original work. That is an operational argument for keeping write transactions bounded, not for avoiding transactions. And because the transaction is the unit of atomicity, its boundaries are a design decision: too wide and you hold locks and undo state for a long time; too narrow and operations that must be all-or-nothing get split across units and lose the guarantee. ## Interview framing \"All-or-nothing. Every change lands together or none does, including after a crash — because the engine keeps undo information and because commit is one atomic record that decides the fate of the whole unit.\"

  • How is atomicity different from durability?
    Atomicity is about indivisibility: the transaction's changes all take effect or none do. Durability is about permanence after the fact: once a commit is acknowledged, those changes survive a crash. A system could in principle be atomic but not durable (a committed transaction is lost on power failure) or durable but not atomic (half a transfer is permanently written). ACID requires both.
  • A transaction updates a row and also sends a confirmation email, then rolls back. What happens to the email?
    It has already been sent and cannot be recalled — atomicity covers only changes inside the database. This is the dual-write problem: two systems, one transaction, no shared commit. The usual fix is to write the intent into a table inside the same transaction and have a separate worker send the email after commit, so the row and the eventual send share the database's atomicity.

A wedding ceremony: many steps happen, but nobody is half-married. One declaration at the end makes the whole sequence count, and if it never comes, none of it does.

saying these in an interview costs you the question

  • Saying atomicity means other transactions cannot see intermediate state (that is isolation)
  • Saying atomicity means committed data survives a crash (that is durability)
  • Believing rollback 'undoes a commit' rather than discarding uncommitted work
  • Assuming external side effects like emails or HTTP calls are rolled back with the transaction
  • Thinking application-level compensating updates are equivalent to a real rollback

context

open as a page

In the ACID acronym for database transactions, what does the C (consistency) actually guarantee, and who decides what 'consistent' means?

level: juniorimportance: must knowfreq 62%

basics

~20 s

C means a transaction moves the database from one valid state to another: every declared integrity rule (primary key, unique, not null, check, foreign key) holds at commit. The engine enforces the declared rules; the developer decides which rules to declare.

open as a page

When a database returns success for a COMMIT, what exactly has it promised the client, and what has it not promised?

level: juniorimportance: must knowfreq 58%

basics

~20 s

It promises the committed changes survive a crash or power loss of that node: the data reached stable storage, not just memory. It does not promise the data survives losing that machine or its disk, nor that any replica already has it.

open as a page

What does the isolation property of ACID promise about transactions that run at the same time, and why do real database engines let you weaken that promise?

level: juniorimportance: must knowfreq 80%

basics

~20 s

Isolation means concurrent transactions must not see each other's unfinished work: the outcome should match some order where they ran one at a time. Enforcing that fully costs locking, blocking and aborts, so engines offer weaker levels that trade specific anomalies for throughput.

open as a page

When a transaction is rolled back, changes it already made to rows and indexes have to disappear. Mechanically, how does a relational engine achieve that?

level: middleimportance: must knowfreq 54%

basics

~20 s

The engine durably records how to reverse each change before making it — before-images or inverse operations — and rollback replays those backwards. Multi-version engines instead leave the old row version in place and simply never make the new one visible.

open as a page

Your service checks 'SELECT ... WHERE email = ?' and inserts the user only if no row came back, yet duplicate emails still appear in production. Explain why, and what actually prevents duplicates.

level: middleimportance: must knowfreq 58%

basics

~20 s

Two sessions can both run the SELECT before either INSERT, so both see nothing and both insert. Only a UNIQUE constraint on the column makes the invariant unbreakable; the application then catches the unique-violation error instead of pre-checking.

open as a page

Why does making a database commit genuinely durable cost latency, and what physically has to happen before the engine can acknowledge the commit?

level: middleimportance: must knowfreq 50%

basics

~20 s

Before acknowledging, the engine must get the transaction's record onto storage that survives power loss — an fsync that the device honours, not just a write into the OS page cache. That is a physical round trip to the device, and adds a network round trip too if a replica must confirm.

open as a page

Name the four transaction isolation levels defined by the SQL standard and explain how each is defined — what distinguishes one level from the next?

level: middleimportance: must knowfreq 78%

basics

~20 s

READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE. The standard defines each by which phenomena it may permit: dirty reads at the lowest level only, then non-repeatable reads, then phantoms, and SERIALIZABLE permits none. Levels are upper bounds, so engines may be stricter.

open as a page

Your primary database node dies and a replica is promoted, yet several transactions the application had already been told were committed are missing. How is that possible, and how would you configure the system so it cannot happen?

level: seniorimportance: must knowfreq 48%

basics

~20 s

With asynchronous replication, the primary acknowledges a commit once it is durable locally; replicas receive it slightly later. If the primary dies before shipping those transactions and you promote a replica, they are lost. Preventing it means requiring at least one replica to acknowledge before the client is told committed.

open as a page

A single UPDATE statement modifies 1,000 rows and hits a constraint violation on row 700. What happens to the 699 rows it already changed, and does the answer depend on whether an explicit transaction is open?

level: middleimportance: should knowfreq 46%

basics

~20 s

The statement is atomic too: all 699 changes are reversed, so the statement leaves no partial effect. Whether the surrounding transaction survives differs by engine — some abort the whole transaction, others undo only the failed statement and let you continue.

open as a page

When exactly does a relational engine check a constraint such as a UNIQUE or FOREIGN KEY rule — per row, at the end of the statement, or at COMMIT — and why does the answer matter?

level: middleimportance: should knowfreq 45%

basics

~20 s

By default most constraints are checked immediately, at the end of each statement, so intermediate rows inside one statement do not trip them. Constraints declared DEFERRABLE INITIALLY DEFERRED are instead checked once at COMMIT, which lets a transaction pass through temporarily invalid states.

open as a page

An operator kills a session running a huge UPDATE, and the database stays busy — and keeps blocking other sessions — for a long time afterwards. Why can aborting a transaction be as expensive as running it, and what follows for how you size write transactions?

level: seniorimportance: should knowfreq 34%

basics

~20 s

Rollback is real work: the engine must reverse every change already made, including index maintenance, and the transaction's locks are only released when that finishes. Killing the session starts the rollback; it does not skip it. So keep write transactions bounded and chunk bulk changes.

open as a page

Inside a database transaction, your application code also calls an external payment API over HTTP. The transaction later rolls back. What does the database's atomicity guarantee say about that HTTP call, and how do teams handle the mismatch?

level: seniorimportance: should knowfreq 42%

basics

~20 s

Nothing — atomicity covers only changes inside that database. The HTTP call already happened and is not reversed. Teams keep the external effect out of the transaction: record the intent in a table in the same transaction, and let a separate worker perform the call after commit, idempotently.

open as a page

How do you decide whether a business invariant should be enforced by a database constraint or by application code, and what do you do about invariants that no constraint can express?

level: seniorimportance: should knowfreq 48%

basics

~20 s

Push every invariant the engine can express into the schema — it then holds for all writers with no race window. Invariants spanning many rows or tables need a serialized write path: a lock on a designated row, a serializable transaction, or a derived column kept correct by a trigger.

open as a page

Some databases offer a mode where COMMIT returns before the transaction is guaranteed on durable storage. What does the system gain, exactly what can be lost, and when is that trade defensible?

level: seniorimportance: should knowfreq 42%

basics

~20 s

You gain much lower commit latency and far higher small-transaction throughput, because commits no longer wait on the storage flush. You risk losing the most recent commits — a bounded window, typically well under a second — if the machine loses power or the OS panics. The database still comes back structurally intact.

open as a page

How do database engines actually implement SERIALIZABLE isolation, and what does each approach cost the application that uses it?

level: seniorimportance: should knowfreq 48%

basics

~20 s

Two families: pessimistic two-phase locking with range/predicate locks, which serializes by blocking and produces deadlocks; and optimistic serializable snapshot isolation, which never blocks readers but aborts transactions whose read/write dependencies could not come from any serial order. Optimistic requires an application retry loop.

open as a page

Many multi-version engines implement their REPEATABLE READ level as snapshot isolation. What does snapshot isolation actually guarantee, and why does that make the SQL standard's level names an unreliable guide?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Snapshot isolation gives every transaction a consistent view of committed data as of its start, with writers checked for conflicting updates to the same rows. It avoids all three phenomena the standard lists, yet still allows non-serializable schedules, so passing the standard's checklist does not equal serializability.

open as a page

How would you decide which isolation level the transactions in a production service should run at, and how do you keep that decision safe as the codebase grows?

level: principalimportance: should knowfreq 38%

basics

~20 s

Start from a documented default (usually READ COMMITTED), identify the few transactions whose correctness depends on stable reads, and raise only those. Prefer pushing invariants into constraints or atomic statements, keep transactions short, and monitor lock waits and serialization failures.

open as a page

When a dataset is split across shards or owned by separate services so the engine cannot enforce a foreign key across the boundary, how do you decide where that invariant lives and how do you keep the data trustworthy?

level: principalimportance: nice to knowfreq 30%

basics

~20 s

Choose a partition key that keeps related rows together so most invariants stay engine-enforced. For the ones that genuinely cross a boundary, name an owning service, enforce on write in that owner, and add continuous reconciliation plus a repair path — the invariant becomes eventual, so design for detecting and fixing violations.

open as a page

You are setting the durability configuration for a new platform whose data ranges from payment ledgers to high-volume device telemetry. How do you decide what each dataset's commit should wait for, and what evidence backs the decision?

level: principalimportance: nice to knowfreq 32%

basics

~20 s

Classify data by two questions: can the writer replay it, and did anyone outside act irreversibly on the acknowledgement? That yields an acceptable loss window per dataset, which maps to a commit level — relaxed local, node-durable, or replica/quorum-acknowledged — and the latency budget each costs. Then verify by testing real failover.

open as a page