← index

Redis Transaction Visualizer

Modelled on src/multi.c, Redis 7.2.14, with the queueing path from processCommand in src/server.c.

A transaction gives you isolation, not rollback. Between EXEC and the last queued command no other client's command runs, but if one command fails partway through, the commands before it stay applied and the ones after it still run (multi.c:190–243). The two ways a transaction can fail are not the same thing, and the page exists to show the difference.

Client

server.c:4159 processCommand
Type a command, or run one of the scenarios below.

MULTI, EXEC, DISCARD, WATCH, UNWATCH, RESET, SET, GET, INCR, DECR, SADD, LPUSH, LPOP, RPOP, SPOP, PING.

Scenarios

0 ms

The two failures are different

multi.c:165–176

A command that fails to queue — unknown command, or the wrong number of arguments — is rejected before it is ever stored (server.c:3885), and the rejection sets CLIENT_DIRTY_EXEC (multi.c:107). EXEC then discards everything and replies:

-EXECABORT Transaction discarded because of previous errors.

server.c:1873, replied at multi.c:172

A command that queues cleanly and then fails at EXEC — INCR on a string, a wrong-type operation — does not abort anything. The loop at multi.c:190 has no break: that command's error becomes one element of the reply array and every other command still runs. Nothing is rolled back; multi.c:253 only frees the queue.

A watched key that changed is a third outcome and not an error at all. EXEC replies a null array (multi.c:174) — (nil) in redis-cli.

Client flags

server.h:338
CLIENT_MULTI1<<3. The client is inside a MULTI, so commands are queued instead of run (server.c:4159). CLIENT_DIRTY_CAS1<<5. A watched key was touched. EXEC replies a null array, not an error (multi.c:174). CLIENT_DIRTY_EXEC1<<12. A command failed to queue. EXEC replies EXECABORT and discards the transaction (multi.c:172). CLIENT_DENY_BLOCKING1ULL<<41. Set for the duration of EXEC so a queued command cannot block (multi.c:183).

Command queue

INACTIVE
Queue is empty

Once the client is dirty, queueMultiCommand stores nothing further, though the client still gets +QUEUED (multi.c:67, server.c:4168).

Watched keys

multi.c:377 touchWatchedKey
Not watching any keys

A write is not the only thing that breaks a watch. Expiry (db.c:1687) and eviction (evict.c:679) both reach touchWatchedKey, and FLUSHDB, FLUSHALL and SWAPDB mark every watcher at once (db.c:619, db.c:1525). A watched key that merely expires between WATCH and EXEC is enough on its own — EXEC checks for it at multi.c:160.

Keyspace

No data yet