Skip to content
Transactions: BEGIN / COMMIT / ROLLBACK
step 1/5

Reading — step 1 of 5

Read

~3 min readTransactions & MVCC

Transactions: BEGIN / COMMIT / ROLLBACK

Move money between two accounts and you must update two keys. If the process dies between the writes, the books are silently wrong forever. A transaction groups operations into an all-or-nothing unit — the A (atomicity) in ACID:

BEGIN
  PUT alice 90
  PUT bob   110
COMMIT        -- both apply, or neither

The write-buffer idiom

The simplest correct implementation never touches the store until commit. All writes go into a per-transaction buffer; COMMIT applies the buffer; ROLLBACK throws it away:

python

Because nothing touched the store early, rollback is free — there is no undo log to walk. And because the buffer is a dict, writes to the same key collapse: the transaction's last write wins, and n= counts distinct keys, not operations.

Read-your-own-writes

A GET inside a transaction consults the buffer first, then falls through to the store. The three-way distinction is where implementations go wrong:

  • key in buffer with a value → return it (yes, even though it is uncommitted);
  • key in buffer as the DEL sentinel → return <nil> — do not fall through to the store;
  • key absent from the buffer entirely → read the committed store.

Encoding "deleted" as None and testing truthiness conflates the last two cases, so a buffered delete leaks the old committed value. Use a dedicated sentinel and test membership with in.

Reads outside any transaction never see buffered writes — until COMMIT, the rest of the world observes nothing at all.

What real stores do

Redis MULTI/EXEC queues commands and runs the batch atomically — but with no isolation story between concurrent clients. etcd builds atomic updates out of compare-and-swap on revisions. Postgres and FoundationDB offer full ACID with real isolation levels. Our exercise is single-threaded, so isolation can wait — MVCC, next lesson, adds it.

Your exercise

Implement direct PUT/DELETE/GET plus BEGIN, COMMIT, ROLLBACK. The grader-caught mistakes, from the real tests: (1) the buffered delete — with a committed, BEGIN, DELETE a, GET a must print <nil>, and after ROLLBACK a plain GET a prints the original value again (the visible test ends on exactly this sequence, printing 100); (2) exact error tokens — a second BEGIN inside a transaction prints ERR already in transaction; COMMIT or ROLLBACK with no open transaction prints ERR no transaction; (3) exact success tokens — BEGIN prints OK, committing two buffered keys prints COMMIT n=2 (a lone buffered DELETE commits as COMMIT n=1), and rollback prints ROLLBACK; (4) COMMIT and ROLLBACK must actually close the transaction, or the visible test's second BEGIN would wrongly answer ERR already in transaction.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…