Open stefanorg opened 1 year ago
/cc @cescoffier (redis), @gsmet (redis), @machi1990 (redis)
Can you provide a reproducer?
Also, you cannot use withTransaction if you are already in one.
Yes i can provide a reproducer. Also i noticed that if i use redis (simple mode with devservices) everything work fine.
The same code, with Cluster Mode redis
give the errors i wrote. Maybe this can be the issue?
Hum, that's definitely the issue. We have a few issues around the clustered mode. @Ladicek is going to have a look soon.
ok, you need me to upload the reproducer?
Please do upload the reproducer, that will be very useful. Thanks!
you can find the reproducer here https://github.com/stefanorg/quarkus-rate-limit-poc
I've provided a docker-compose.yml to spin up a redis cluster. Hope this help! Thanks
Is there any progress / workaround on that issue ? I'm experiencing the same in a cluster mode:
TransactionResult resetFileResult = redisDS.withTransaction(tx -> {
tx.key().rename(OLD_KEY, NEW_KEY);
tx.value(Long.class).getdel(SEQ_KEY);
});
Produces:
java.util.concurrent.CompletionException: ERR EXEC without MULTI
Ooops, I totally forgot about this issue. It seems quite possible that this is caused by https://github.com/vert-x3/vertx-redis-client/issues/365, which was fixed recently. I'll try to reproduce and confirm.
OK, I was able to reproduce and upgrading to Vert.x 4.4.6 doesn't seem to help. So https://github.com/vert-x3/vertx-redis-client/issues/365 is not the cause of the problem here. I'll dig deeper.
So the crucial problem here is that the underlying Vert.x Redis client is not transaction-aware. It treats MULTI
and EXEC
as any other command. In the Redis cluster mode, this means that the client sends these commands to random nodes, because there's no key associated with them. This means:
MULTI
may end up on a different node than the following commands, meaning that those commands don't actually execute in a transaction.EXEC
may end up on a different node than MULTI
, which means the transaction is not ended properly (and EXEC
fails).This requires substantial changes on the Vert.x Redis client side. I can see 3 ways of handling a Redis transaction in a cluster:
WATCH
, UNWATCH
, MULTI
, EXEC
, DISCARD
) are detected and the command is rejected directly by the client.MULTI
is queued and is only emitted when the first command with a key is executed. This first command binds the connection to the corresponding node of the Redis cluster. All subsequent commands are targeted to that node. If some of the subsequent commands have a key that belongs to another node, this would fail on the Redis side. If WATCH
is used before MULTI
, its key(s) determine to which node the connection is bound and the subsequent MULTI
doesn't need to be queued. If WATCH
keys belong to multiple nodes, that would fail just like today. This provides roughly the same guarantees as a normal Redis transaction with a standalone Redis instance.MULTI
is queued and is emitted for each node that is targeted by any of the subsequent commands. EXEC
is submitted to all nodes that were targeted by any of the previously executed commands. WATCH
would be distributed to all nodes to which its keys belong. This is unlike a normal Redis transaction, because it's in fact N independent Redis transactions, uncoordinated.The 1st option would be fairly easy to implement. The 2nd and 3rd options, not so much.
For my particular case - the 2nd option is quite suitable. The keys in each transaction needs to be anyway bound to the same node and as you said - this will replicate the behavior of non-cluster redis And will not end up with multiple transactions with no way to discard all if something goes wrong (like in 3rd case)
All subsequent commands are targeted to that node.
In addition to targeting the node, my experiments showed that you also need to target the same connection. Clustered client uses connection pool, so all commands in transactions IMO should re-use the same connection.
That is correct, the withTransaction
method in Quarkus already works like that (it borrows a connection from the pool and runs all commands on it).
Describe the bug
Hi all, i want to execute di code (basically a ratelimit algorithm) and everything work fine
based on documentation https://quarkus.io/guides/redis-reference i want to execute those commands in a transaction (using MULTI) so i changed the code to use
withTransaction
but this way i get different kind of exceptions trying to execute the code:
sometime this:
sometime i get
ERR MULTI calls can not be nested
The same happen if i use
withTransaction
in the reactive variant of ReactiveRedisDataSourceWhat i'm doing wrong? Thanks
Expected behavior
No response
Actual behavior
No response
How to Reproduce?
No response
Output of
uname -a
orver
No response
Output of
java -version
No response
GraalVM version (if different from Java)
No response
Quarkus version or git rev
2.16.5
Build tool (ie. output of
mvnw --version
orgradlew --version
)./mvnw --version Warning: JAVA_HOME environment variable is not set. 100% Apache Maven 3.8.6 (84538c9988a25aec085021c365c560670ad80f63) Maven home: /home/scorallo/.m2/wrapper/dists/apache-maven-3.8.6-bin/67568434/apache-maven-3.8.6 Java version: 17.0.6, vendor: Amazon.com Inc., runtime: /home/scorallo/.jdks/corretto-17.0.6 Default locale: it_IT, platform encoding: UTF-8 OS name: "linux", version: "5.19.0-38-generic", arch: "amd64", family: "unix"
Additional information
No response