Transactions ACID and Isolation Levels

Relational Databases for Designers · 45 min

System Design · Module 3 · Lesson 3 of 4 · 45 min

Transactions ACID and Isolation Levels

Distinguish atomic commits from isolated decisions, understand PostgreSQL snapshots, and handle transaction aborts correctly.

A transaction boundary groups work; the isolation and predicates determine whether concurrent work preserves the intended invariant.

01 The idea

The problem this lesson solves

Transaction A reads a committed value at its statement snapshot.

Prerequisite: earlier lessons introduce the vocabulary and shared examples used here. Follow the module order.

AtomicityThe transaction’s accepted changes commit together or roll back together.
SnapshotThe set of committed row versions visible to a read under the chosen isolation rules.
Serialization failureAn abort indicating the transaction must be retried to obtain a valid serializable outcome.

02 Worked example

Follow one worked example

Transactions ACID and Isolation Levels worked diagram01A readsSnapshot 102B commitsNew version03A reads againSnapshot 2 may differ04Retry scopeWhole transactionRead events in time order, from left to right.Transactions ACID and Isolation Levels compact diagram01A readsSnapshot 102B commitsNew version03A reads againSnapshot 2 may differ04Retry scopeWhole transaction
A reads: Snapshot 1 • B commits: New version • A reads again: Snapshot 2 may differ • Retry scope: Whole transaction.

Trace the example in order. Follow the same identities, units and state transitions through the diagram and the events below.

1
Read a valueTransaction A reads a committed value at its statement snapshot.
2
Commit elsewhereTransaction B updates the row and commits.
3
Read againAt Read Committed, A’s next SELECT can see B’s newer committed value.
4
Choose a guaranteeUse the required isolation and atomic operation; retry whole transactions when the database requests it.

A transaction boundary groups work; the isolation and predicates determine whether concurrent work preserves the intended invariant.

03 The method

Mechanisms decisions and a guided tutorial

Work through the explanation in order, then apply it in the tutorial. Each comparison names a cost as well as a benefit.

Choice or conceptPurposeBoundary or cost
Read CommittedFresh statement snapshotsRepeated reads can change
Repeatable Read in PostgreSQLStable transaction snapshotSerialization anomalies remain possible
SerializableSerializable accepted outcomesWhole-transaction retries may be required
Atomic operationProtects a stated transitionMust still check its result

Separate ACID's promises

Atomicity means a transaction's changes commit together or not at all. Consistency means constraints and correctly written transaction logic preserve rules; it does not mean every replica instantly agrees. Isolation controls what concurrent transactions can observe. Durability concerns surviving failures after commit, under the configured persistence guarantees. Replication settings can affect which failures preserve an acknowledged transaction. These are different promises, so ‘the database is ACID’ is not an explanation of your booking algorithm.

Choose the isolation behavior you need

PostgreSQL Read Committed takes a fresh snapshot for each statement and does not allow dirty reads. Repeatable Read gives a stable transaction snapshot but still permits serialization anomalies; its implementation prevents phantom reads. Serializable aims for an outcome equivalent to some serial execution and can abort transactions that must be retried. Retry the whole failed transaction with a bounded policy. A transaction around a separate read and unconditional update does not automatically protect a business invariant.

A transaction can contain incorrect logic

Imagine reading an account balance and later writing a replacement value computed from that earlier read. Another transaction can act in between under weaker isolation. Committing both statements together does not prove that the earlier decision stayed valid.

State the business invariant first: balances may not go below zero, seats have one owner, or inventory cannot be oversold. Then choose conditional updates, locks, constraints or serializable transactions that actually defend it. “We use transactions” describes a mechanism, not the complete correctness argument.

Understand the snapshot boundary

At PostgreSQL Read Committed, each statement receives a new snapshot of committed data. Two SELECT statements in the same transaction may therefore see different committed values. Repeatable Read uses a stable transaction snapshot, but that does not automatically remove every serialization anomaly.

Serializable can reject executions that would not match any valid serial order. Such rejection is an expected possible outcome, not proof that the database is broken. Retrying requires a fresh transaction that reruns the reads and decisions. Repeating only the final UPDATE can keep the stale premise that caused the problem.

Keep external effects outside unsafe retries

A transaction retry may run application code more than once. If that code sends an email or charges a card before the database commits, repeating it can duplicate an external effect. Use stable operation identities and an explicit workflow for effects that cannot share the database transaction.

Bound retries and include backoff where appropriate. Treat a genuine business conflict differently from a transient transaction abort. A seat already owned by someone else should not be endlessly retried as if another attempt will make the requirement disappear. Logs and metrics should distinguish aborts, conflicts and infrastructure failures.

Guided tutorial

1. Open two sessions

Use a disposable PostgreSQL database and record each session’s statements.

2. Observe statement snapshots

Read, commit a change in the other session, and read again at Read Committed.

3. Compare Repeatable Read

Begin a fresh transaction at Repeatable Read and repeat the observation.

4. Design retries

Write which errors cause a whole-transaction retry and which return a business conflict.

-- Setup once in a disposable database, before opening both sessions:
CREATE TABLE sd_lab_values (id integer PRIMARY KEY, value integer NOT NULL);
INSERT INTO sd_lab_values VALUES (1,10);
-- Session A:
BEGIN ISOLATION LEVEL READ COMMITTED;
SELECT value FROM sd_lab_values WHERE id = 1;
-- Session B, outside A's transaction:
UPDATE sd_lab_values SET value = 20 WHERE id = 1;
-- Session A again: this reads the newer committed value 20.
SELECT value FROM sd_lab_values WHERE id = 1;
COMMIT;

04 Cheat sheet

Review the decisions

Use this table to explain the mechanism and its limit without memorizing a product name.

Choice or conceptRememberCheck
Read CommittedFresh statement snapshotsRepeated reads can change
Repeatable Read in PostgreSQLStable transaction snapshotSerialization anomalies remain possible
SerializableSerializable accepted outcomesWhole-transaction retries may be required
Atomic operationProtects a stated transitionMust still check its result
AtomicityThe transaction’s accepted changes commit together or roll back together.
SnapshotThe set of committed row versions visible to a read under the chosen isolation rules.
Serialization failureAn abort indicating the transaction must be retried to obtain a valid serializable outcome.

05 Where & why

Check the primary references

Use official references for protocol and implementation details. The questions here are teaching prompts.

Primary reference
PostgreSQL 16 transaction isolation

Read PostgreSQL 16 transaction isolation

Primary reference
PostgreSQL explicit locking

Read PostgreSQL explicit locking

Primary reference
Multicolumn index behavior

Read Multicolumn index behavior

State the implementation version and the workload assumptions when defending your own design.

06 Interview questions

Explain the reasoning out loud

Answer before opening the explanation. The questions progress from vocabulary to decisions and failure behavior.

What does Atomicity mean?
The transaction’s accepted changes commit together or roll back together.
What does Snapshot mean?
The set of committed row versions visible to a read under the chosen isolation rules.
What does Serialization failure mean?
An abort indicating the transaction must be retried to obtain a valid serializable outcome.
Does atomicity mean one transaction runs at a time?
No. It groups commit or rollback of that transaction’s work. Isolation governs interaction with concurrent transactions.
Why can two reads differ inside Read Committed?
Each statement can see a newer committed snapshot. A transaction boundary alone does not freeze all its reads.
Does PostgreSQL Repeatable Read permit phantom reads?
Its implementation prevents them, while still allowing serialization anomalies. State implementation-specific behavior rather than assume every database matches it.
What must be retried after a serialization failure?
The whole transaction, including reads and decisions, under a bounded retry policy.
Why is an external charge dangerous inside retryable logic?
The remote effect may happen even if the local transaction aborts. Stable downstream identity and recovery are needed to prevent duplicate effects.

07 Practice problems

Practice a new case

Solve the new cases before revealing hints. Complete worked solutions follow the set.

Name the promise

Easy
Identify which ACID property groups two accepted writes into one commit.
Follow-up
State the assumption or edge case that makes the answer correct.
Show the hint
Separate grouping from concurrency.

Choose the retry unit

Easy
Choose between retrying the final UPDATE and the whole transaction after serialization failure.
Follow-up
State the assumption or edge case that makes the answer correct.
Show the hint
Earlier reads formed part of the decision.

Two reads

Medium
Explain two different SELECT results inside a Read Committed transaction after another session commits.
Follow-up
State the assumption or edge case that makes the answer correct.
Show the hint
Snapshots can be per statement.

Business conflict

Medium
A conditional stock decrement returns zero rows because stock is exhausted; should it be automatically retried forever?
Follow-up
State the assumption or edge case that makes the answer correct.
Show the hint
Successful SQL execution can still report a business failure.

Retry side effect

Medium
A transaction sends an email then aborts and retries; identify the risk.
Follow-up
State the assumption or edge case that makes the answer correct.
Show the hint
Database rollback cannot unsend a remote message.

State the invariant

Hard
Defend a rule that at least one of two on-call doctors remains active when both can update concurrently.
Follow-up
State the assumption or edge case that makes the answer correct.
Show the hint
A stable snapshot alone may let both make locally valid decisions.

Worked solutions

1. Name the promise

Atomicity groups the transaction’s changes; it does not by itself provide every isolation guarantee.

2. Choose the retry unit

Retry the whole transaction with a fresh snapshot and bounded attempts.

3. Two reads

The second statement can see the newly committed row version. This is consistent with PostgreSQL Read Committed behavior.

4. Business conflict

No. Return the documented unavailable/conflict result. Distinguish it from a transient serialization or deadlock abort.

5. Retry side effect

The email can be sent twice. Move delivery into a durable post-commit workflow with a stable operation identity and appropriate deduplication/recovery.

6. State the invariant

Specify the cross-row invariant, coordinate the updates with suitable locks or serializable transactions, and retry the complete aborted transaction. Verify the concurrent case rather than relying on one successful sequential test.