Skip to content

Step 1 of 3 · Reading · ~3 min

Transaction Boundaries

Transactions & ACID

BEGIN, COMMIT, ROLLBACK: Making Several Writes One

Moving 100 from account A to account B is two writes. If the second one fails, the first must be taken back, or money has vanished. A transaction groups writes so that they either all take effect or none does. After this lesson your store has BEGIN, COMMIT and ROLLBACK, plus savepoints for undoing part of a transaction.

Auto-commit and explicit transactions

Outside a transaction every statement is its own: SET a 1 takes effect immediately and cannot be taken back. BEGIN opens a transaction; from then on writes belong to it until COMMIT makes them permanent or ROLLBACK abandons them. Opening one inside another is an error (ERR transaction already active), as is COMMIT, ROLLBACK or a savepoint command with nothing open (ERR no active transaction).

How to take writes back: the undo log

There are two ways to build ROLLBACK. One keeps the transaction's writes aside and only applies them at commit. The other applies writes immediately (so the transaction sees its own changes) and remembers how to reverse each one. This lesson uses the second, which is what most engines do for in-place updates.

The reversal record is an undo entry: the key and the value it had before this write (or "did not exist"). Every write inside a transaction first pushes an undo entry, then changes the store:

store: a=1
BEGIN
SET a 2        undo: [(a, 1)]                    store: a=2
SET b 7        undo: [(a, 1), (b, absent)]       store: a=2, b=7
SET a 3        undo: [(a, 1), (b, absent), (a, 2)]
ROLLBACK       replay the undo entries NEWEST FIRST:
               (a, 2) -> a=2;  (b, absent) -> delete b;  (a, 1) -> a=1

Newest first matters: if a was written twice, restoring the older entry last is what leaves it at its original value. COMMIT just discards the undo entries; the writes are already in the store. A DEL of a key that does not exist prints NOT_FOUND and writes nothing, so it needs no undo entry.

Savepoints: a partial rollback

SAVEPOINT name remembers how long the undo log is right now. ROLLBACK TO name undoes only the entries after that mark, newest first, and keeps the transaction open: the savepoint stays, so you can roll back to it again, but savepoints created after it are discarded. RELEASE name forgets the savepoint and every later one without undoing anything. If a name is used twice, commands act on the most recent one. An unknown name is ERR no such savepoint: <name>.

BEGIN
SET a 1
SAVEPOINT s1        mark = 1
SET b 2
SAVEPOINT s2        mark = 2
SET c 3
ROLLBACK TO s1      undoes c and b, keeps a; s2 is gone, s1 stays

Commands

SET, DEL, GET, DUMP as before; BEGIN, COMMIT, ROLLBACK, SAVEPOINT <name>, ROLLBACK TO <name>, RELEASE <name> print OK or an error; STATUS prints txn=yes|no undo=<undo entries> savepoints=<names oldest first, comma separated, or ->.

Common mistakes

  • Restoring in forward order. A key written twice ends up with the wrong value.
  • Saving the undo entry after the change, so it records the new value.
  • Forgetting that "absent" must be undone by deleting.
  • Dropping the savepoint on ROLLBACK TO. It stays; only the later ones go.

Your exercise

The starter parses commands and prints results. You write write_key, undo_to, begin, commit, rollback, savepoint, rollback_to and release. The mistake to avoid: replaying undo entries oldest first.

Up nextIsolation Levels: What Other Transactions Can SeeTransactions & ACID

Discussion

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

Sign in to post a comment or reply.

Loading…