Redis Transactions

A Redis transaction lets you queue several commands and execute them all at once, in order, without any other client interrupting. This guarantees that no command from another connection slips in between your queued commands while they run.

The Bank Transfer Analogy

Moving money between two accounts requires two steps: deduct from Account A, add to Account B. If the server crashes after step one, Account A loses money without Account B gaining it. A transaction wraps both steps so they either both succeed or neither runs.

Without a transaction (dangerous):
  Step 1: DECRBY account:A 500   ← runs OK
  (server crash happens here)
  Step 2: INCRBY account:B 500   ← NEVER RUNS
  
  Result: 500 units vanished from A and never reached B.

With a transaction (safe):
  MULTI
  DECRBY account:A 500
  INCRBY account:B 500
  EXEC                           ← both run together or neither runs

  Result: Both commands run as one unit.

The Four Transaction Commands

MULTI   ← start a transaction (begin queuing)
EXEC    ← execute all queued commands
DISCARD ← cancel the transaction (clear the queue)
WATCH   ← monitor a key; abort EXEC if it changes before EXEC runs

Step-by-Step Transaction Example

127.0.0.1:6379> MULTI
OK              ← queuing mode started

127.0.0.1:6379> SET msg "hello"
QUEUED          ← not executed yet, just queued

127.0.0.1:6379> INCR counter
QUEUED

127.0.0.1:6379> SET status "active"
QUEUED

127.0.0.1:6379> EXEC
1) OK           ← result of SET msg
2) (integer) 1  ← result of INCR counter
3) OK           ← result of SET status

All three ran in sequence, uninterrupted.

Cancelling a Transaction

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> SET promo "DEAL50"
QUEUED
127.0.0.1:6379> DISCARD
OK              ← queue cleared, nothing executed

Error Handling Inside Transactions

Redis distinguishes two kinds of errors inside a transaction:

Error Type 1 – Syntax error before EXEC
  (command does not exist or has wrong syntax)
  
  MULTI
  SET name "Raj"
  BADCOMMAND arg  ← unknown command
  EXEC            ← entire transaction aborted
  → (error) EXECABORT

Error Type 2 – Runtime error during EXEC
  (command exists but applied to wrong data type)

  SET counter "hello"   ← counter is a string, not a number

  MULTI
  SET name "Raj"
  INCR counter          ← will fail: "hello" is not a number
  SET city "Delhi"
  EXEC
  1) OK                 ← SET name succeeded
  2) (error) ERR value is not an integer  ← INCR failed
  3) OK                 ← SET city still succeeded

  Redis does NOT roll back on runtime errors.
  The other commands in the transaction still run.

This second behaviour surprises many developers. Redis does not provide full rollback like a relational database. If you need rollback-like safety, you must check for errors after EXEC and handle them manually.

WATCH – Optimistic Locking

WATCH lets you monitor one or more keys before a transaction. If any watched key changes between your WATCH call and your EXEC call, Redis aborts the transaction and returns nil instead of results. Your code then retries.

Scenario: Two processes both try to update the same inventory count.

Process A:
  WATCH stock:item:77
  current = GET stock:item:77  → "10"
  MULTI
  SET stock:item:77 "9"        ← buy 1 item
  EXEC
  → OK (nothing changed stock:item:77 before EXEC)

Meanwhile, Process B (runs between A's WATCH and A's EXEC):
  SET stock:item:77 "8"        ← bought 2 items

Now back to Process A's EXEC:
  → (nil)                      ← EXEC aborted! stock changed!
  Process A sees nil and retries the whole sequence.

Flow diagram:
  Process A: WATCH → GET → MULTI → queue → EXEC
                                              │
               Did watched key change? YES ───┴──▶ return (nil), retry
                                      NO ─────────▶ execute normally

WATCH Retry Pattern

Loop:
  WATCH stock:item:77
  current = GET stock:item:77
  new_value = current - 1

  MULTI
  SET stock:item:77 new_value
  result = EXEC

  if result == nil:
    retry   ← someone else changed the key; start again
  else:
    success

When to Use Transactions

Use MULTI/EXEC when:
  ✓ You update two or more related keys that must change together.
  ✓ You want to prevent any other client from interleaving commands.
  ✓ You build atomic operations without needing a full rollback.

Use WATCH when:
  ✓ You read a value, compute something from it, and write back.
  ✓ You want to abort if someone else modifies the key first.
  ✓ You handle low-contention scenarios where retries are rare.

Key Points

  • MULTI starts queuing; EXEC runs all queued commands at once without interruption.
  • DISCARD clears the queue and exits transaction mode.
  • Syntax errors before EXEC abort the entire transaction. Runtime errors during EXEC only fail the individual command — others still execute.
  • Redis transactions do not roll back on runtime errors the way relational databases do.
  • WATCH implements optimistic locking: if a watched key changes before EXEC, the transaction aborts and you retry.

Leave a Comment