Transactions
On Adda a 'like' must record the like AND fire the notification — a crash halfway can't leave one without the other. Transactions make "all or nothing" real.
The problem
Adda ships a feature: when you like a post, the author gets a notification. In code that is two steps — insert the like, insert the notification. Then Fahim reports something strange: sometimes he sees his like on a post, but the author swears they were never notified. Sometimes it is the reverse. The server crashed — or a shard hiccupped — in the gap between those two lines, and the pair came apart.
Shuvo reaches for the cleanest analogy on the whiteboard: money. Transfer $100 from Alice to Bob — subtract 100 from Alice, add 100 to Bob. If the server crashes in the gap, Alice is out $100 and Bob never got it. The money simply evaporated, exactly like Adda's orphaned like.
It gets worse under concurrency. Two withdrawals hit Alice's $150 balance at the same moment, both read "150," both approve, and she withdraws $200 she never had. Nothing in the individual operations is wrong. The danger lives in the seams between them — the moments where a crash or another request can interleave.
A first attempt
The naive guard is "check before you write": read the balance, verify it is enough, then subtract. But the check and the write are two separate steps, so another request can slip in between them — you have just moved the seam, not closed it.
Tanvir might reach for a lock around the whole operation. Now correctness depends on Adda's code never crashing while holding that lock, never forgetting to release it, and coordinating locks across every path that touches the balance. That is a huge amount of fragile bookkeeping to reinvent for something the database can guarantee for you — if you wrap the steps in a transaction.
The insight
Group the steps into one transaction and let the database enforce ACID: the group either fully happens or fully does not, and concurrent transactions do not corrupt each other.
- Atomicity: all steps commit, or none do. A crash mid-way rolls back cleanly.
- Consistency: the transaction moves the database from one valid state to another (constraints hold).
- Isolation: concurrent transactions behave as if run one at a time — no peeking at half-finished work.
- Durability: once committed, it survives crashes, because it was written to a durable log first.
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 'alice';
UPDATE accounts SET balance = balance + 100 WHERE id = 'bob';
COMMIT; -- both, or (on crash) neitherHow it works
Write intentions to a log first
Before touching the real data, the database appends the change to a write-ahead log (WAL) — the same WAL Adda's followers replay in replication. If it crashes, recovery replays committed transactions and discards uncommitted ones — this is how atomicity and durability are enforced.
Isolate concurrent transactions
Using locks or multi-version snapshots (MVCC), each transaction sees a stable view and cannot read another's uncommitted changes. This is what stops the two-withdrawal race.
Commit atomically
COMMIT flips the transaction to durable in one indivisible act. Before that point, none of its changes are visible; after it, all of them are.
Choose an isolation level
Stronger isolation prevents more anomalies but allows less concurrency. You pick the weakest level that is still correct for the operation.
Isolation levels trade safety against throughput. From weakest to strongest, and what each still allows:
| Isolation level | Dirty read | Non-repeatable read | Phantom read |
|---|---|---|---|
| Read Uncommitted | possible | possible | possible |
| Read Committed | prevented | possible | possible |
| Repeatable Read | prevented | prevented | possible |
| Serializable | prevented | prevented | prevented |
Concrete numbers
Isolation is not free. On a contended row, Serializable can cut throughput by 2–5x versus Read Committed, because transactions must wait or abort and retry when they conflict. Most systems default to Read Committed (Postgres — Adda's choice) or Repeatable Read (MySQL) and reserve Serializable for the few operations that truly need it.
The distributed case is far more expensive — and this is where Adda's sharding comes back to bite. A two-phase commit (2PC) across 3 nodes needs two network round trips plus durable writes at each participant — tens of milliseconds across a datacenter, hundreds across regions — and it blocks: if the coordinator dies mid-commit, participants hold locks until it recovers. That single blocking property is why large systems avoid distributed transactions and favor idempotent, eventually-consistent workflows (like the saga pattern) instead.
When to use it
The trade-off
Stronger guarantees cost concurrency. Higher isolation prevents more anomalies but serializes more work, and distributed transactions add network latency and blocking on top. Use the weakest isolation that is still correct, and keep transactions short — a long transaction holds locks and starves everyone waiting behind it.
Prefer idempotency over distributed transactions
When the like lives on one shard and the notification on another, a two-phase commit is often the wrong tool — it blocks on coordinator failure. Instead, make each step idempotent and retryable and stitch them with a saga (with compensating actions to undo). You trade strict atomicity for availability, which is usually the right call across Adda's network.
Practice
Recap
- A transaction makes a group of operations all-or-nothing and crash-safe via ACID.
- Isolation levels trade anomaly prevention against concurrency — use the weakest that stays correct.
- Distributed transactions (2PC) give atomicity but block and cost round trips; sagas plus idempotency are the common alternative.
Consistency & the CAP Theorem
Why distributed atomicity fights availability.
Replication
Durability and how commits survive a node failure.
Idempotency
Making retryable steps safe — the saga alternative to 2PC.
In an interview
When a design touches money, inventory, or bookings, say "this needs a transaction" and name the ACID property at risk. For the multi-service case, resist reaching for two-phase commit — explain that it blocks, and propose idempotent steps with a saga instead. Showing you match isolation level to the operation, not the whole database, is what stands out.
How is this guide?
Last updated on
Consistency & the CAP Theorem
The network link between Adda's two regions goes dark, and its shards start disagreeing. Ria must choose: stay consistent or stay available. She can't have both — that's CAP.
Monolith vs Microservices
Adda has been one codebase since the laptop under Ria's desk. It was a joy — until the team grew and every deploy became terrifying. Splitting into services helps, but hands Adda a distributed-systems bill.