TL;DR: There's no proper support for Redis Transactions using the Reactive API
The reason lies in the execution model: How Redis executes transactions and how the reactive API is supposed to work.
When using transactions, a connection enters transactional state, then commands are queued and finally executed with EXEC
. Executing queued commands with exec makes the execution of the individual commands conditional on the EXEC
command.
Consider the following snippet (Lettuce code):
RedisReactiveCommands<String, String> commands = …;
commands.multi().then(commands.set("key", "value")).then(commands.exec());
This sequence shows command invocation in a somewhat linear fashion:
- Issue
MULTI
- Once
MULTI
completes, issue a SET
command
- Once
SET
completes, call EXEC
The caveat is with SET
: SET
only completes after calling EXEC
. So this means we have a forward reference to the exec command. We cannot listen to a command that is going to be executed in the future.
You could apply a workaround:
RedisReactiveCommands<String, String> commands = …
Mono<TransactionResult> tx = commands.multi()
.flatMap(ignore -> {
commands.set("key", "value").doOnNext(…).subscribe();
return commands.exec();
});
The workaround would incorporate command subscription within your code (Attention: This is an anti-pattern in reactive programming). After calling exec()
, you get the TransactionResult
in return.
Please also note: Although you can retrieve results via Mono<TransactionResult>
, the actual SET
command also emits its result (see doOnNext(…)
).
That being said, it allows us circling back to the actual question: Because these concepts do not work well together, there's no API for transactional use in Spring Data Redis.
Mono.when
?var command = commands.set("key", "value").doOnNext(…)
thenreturn Mono.when(commands.exec(), command)
? And with that said I can't findmulti
as a method in Spring Boot 2.4.1, was it removed? – Wellfavored