CodePark/ questions / Transactions, pipelining & Lua
START PRACTICINGPRACTICE
Backend question · Redis & caching › Transactions, pipelining & Lua

How does WATCH give you optimistic locking? Walk through this transfer.

WATCH balance:alice
GET balance:alice        -- "100"
MULTI
DECRBY balance:alice 30
INCRBY balance:bob 30
EXEC                     -- nil if balance:alice changed after WATCH

WATCH makes EXEC cancel the transaction if another client changed a watched key. The steps are:

  • WATCH the keys on your connection.
  • Read what you need.
  • Decide in the application. Here, check that Alice has at least 30.
  • Queue the writes inside MULTI.
  • Call EXEC.

At EXEC, Redis checks the watched keys. If another client changed any of them since WATCH, the transaction does not run and EXEC returns nil. The client then retries the whole read-check-write loop. EXEC or UNWATCH clears the watches.

This is check-and-set without holding a lock. It is called optimistic because it assumes conflicts are rare. Under heavy contention (many clients changing the same keys), retries pile up. Then a Lua script that checks and updates in one step is better.

In Node, watches belong to a connection. So use a dedicated connection, or the client's isolated execution feature. Don't use a shared connection, because other requests on it could interfere.

  • The order is WATCH, read, decide, MULTI, then EXEC.
  • EXEC returns nil if a watched key changed.
  • On failure, retry the whole loop.
  • WATCH is good for low contention. Use Lua for high contention.
  • Watches belong to one connection, so avoid shared connections.