EsRedisTransaction
Description
Represents a Redis transaction for executing a group of commands atomically using the MULTI/EXEC commands. This class ensures that all commands within the block are executed as a single, uninterrupted operation. It inherits from the abstract superclass <EsRedisCommandBatcher>, sharing a common structure with <EsRedisPipeline> but differing in its execution semantics (atomicity vs. performance).
You don't create instances of <EsRedisTransaction> directly. Instead, an instance is created and managed for you when you send the EsRedisClient>>transaction: message. This transaction object is then passed into your code block.
The transaction works by intercepting command group messages (like strings, hashes, etc.) and creating temporary, rewired command groups. When you send a command to one of these groups (e.g., tx strings set: 'k' value: 'v'), the command is queued for execution. When your block finishes, the transaction object sends the EXEC command, causing Redis to execute all queued commands atomically.
Nested Operations
Transactions can be nested inside a pipeline. In this scenario, the transaction's commands (MULTI, the commands within the block, and EXEC) are all queued into the parent pipeline's command batch. The entire sequence is then sent to the server in a single network round-trip when the outermost pipeline is executed.
Transactions can be used with the WATCH command for optimistic locking. If a watched key is modified by another client after being watched but before EXEC is called, the transaction will be aborted, and an <EsRedisTransactionAbortedException> will be raised.
The transaction behaves differently depending on whether the client is in synchronous or asynchronous mode.
Asynchronous Transactions
When the client is in asynchronous mode (isAsync: true), the transaction behavior is enhanced:
-
Each command you send within the transaction: block immediately returns an individual
<EsFuture> that will be completed with that specific command's result.
-
The transaction: message itself returns a "master" <EsFuture>. This master future is completed
with an array of all the results from the transaction, but only after EXEC` has been processed by the server.
This dual-future mechanism provides flexibility, allowing you to coordinate actions based on the completion of individual commands or wait for the entire atomic batch to finish.
Instance Variables
Example
Synchronous Example
| results |
client strings set: 'balance' value: 100.
"All commands in the block are sent after MULTI and executed by EXEC"
results := client transaction: [:tx |
tx strings incrby: 'balance' amount: 50.
tx strings decrby: 'balance' amount: 20.
tx strings get: 'balance'.
].
"results => #(150 130 '130')"
self assert: [(client strings get: 'balance') =: '130']
Asynchronous Example
| fIncr fGet fMaster |
client isAsync: true.
(client strings set: 'async_counter' value: 100) waitFor.
"The transaction: message returns a master future for all results."
fMaster := client transaction: [:tx |
"Each command returns its own future."
tx strings set: 'another_key' value: 'x'.
fIncr := tx strings incr: 'async_counter'.
fGet := tx strings get: 'async_counter'.
].
"You can wait on individual futures."
self assert: [fIncr waitFor = 101].
self assert: [fGet waitFor = '101'].
"Or wait on the master future to get all results at once."
self assert: [fMaster waitFor = #('OK' 101 '101')].
Nested Transaction in Pipeline Example
| results txResults |
results := self client pipeline: [:pipe |
pipe strings set: 'before' value: '1'.
pipe transaction: [:tx |
tx strings set: 'tx_key' value: 10.
tx strings incr: 'tx_key'.
].
pipe strings set: 'after' value: '2'.
].
"The result of the EXEC command is an array at index 5"
txResults := results at: 5.
self assert: [txResults = #('OK' 11)].
WATCH / Abort Example
| key |
key := 'watched_key'.
self client strings set: key value: 100.
self should: [
self client server watch: key.
"Another client modifies the key while it's being watched"
client2 strings set: key value: 999.
"This transaction will now fail"
self client transaction: [:tx |
tx strings set: key value: 101.
].
] raise: EsRedisTransactionAbortedException.
"The transaction was not executed, so the value remains what client2 set it to."
self assert: [self client strings get: key) = '999'].
Class Methods
None
Instance Methods
discard
Mark this transaction to be discarded instead of executed.
pipeline:
Nesting a pipeline inside a transaction is not a valid Redis operation.
This is now explicitly disallowed to prevent unexpected behavior.
transaction:
Nesting a transaction inside a transaction is not a valid Redis operation.
This is now explicitly disallowed to prevent unexpected behavior.
Last modified date: 01/22/2026