-
Notifications
You must be signed in to change notification settings - Fork 83
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Transactions & Pipeline: the current issue and ideas to fix it #681
Comments
I'm not very knowledgable so feel free to ignore me or tell me off if any of my suggestions are terrible / don't make sense 😄
Again, feel free to ignore me or tell me off if any of these doesn't make sense, just thought they might help spark an idea or something |
It sounds like modelling the individual operations with |
I believe it is doable to keep things within the same effect. I'm aware of |
Maybe you can use |
@kyri-petrou your suggestion seems to work pretty well (see #685). Still on early stages but already showing promising results. I think we'll still have the issue of not being able to guarantee the running order, but I guess that's a compromise we can make in favor of reliable execution of transactions. |
@gvolpe that's good news! I'm not too familiar with the cats / scala inners, but for running order would it make any difference if you used |
@kyri-petrou both |
@gvolpe ah okay, I see your concern. I'm afraid I know very little on CE / JVM scheduling to have an opinion What gave me the idea for my suggestion above came from Lettuce documentation and how they use the async API for transactions: https://github.com/lettuce-io/lettuce-core/wiki/Transactions#transactions-using-the-asynchronous-api I might be extrapolating here but it seems to suggest that as long as you call their async api in order in a single thread it will be executed in order? Again my understanding is not too great so there might be missing other aspects I'm not aware of like the CE scheduling, but maybe this helps in some way |
@kyri-petrou I think your understanding is correct. After all, you can only execute one transaction at a time per I will go ahead and merge it, I'm happy enough with the current UX, and I want to fix pipelining next. |
FYI: #689 improves UX by a large margin 💪🏽 |
😮 that's some great stuff there, well done! |
Introduction
I've had this on my mind for a very long time, so let me see if I can communicate the issue so that others may help.
The current implementation of transactions is almost right, but it can't make any guarantees about commands running in order, because that's up to the runtime system (i.e. the scheduler). Thus, I've been recommending people not to use it.
Note this issue also applies to pipelining, as these share the same core implementation (see runner.scala).
Problem
Say we have a transaction mixing
SET
andGET
commands, just as the following (taken from examples):Most of the time, this will work, but it's unreliable. And the reason lies deep down at the core of the Java implementation, and how
redis4cats
models it.A transaction consists of any set of supported commands that are queued (not run) between a call to
MULTI
followed by eitherEXEC
orDISCARD
. Just like database transactions are modeled viaBEGIN
/COMMIT
/ROLLBACK
.This library models this problem as a
Resource
, where the acquisition meansMULTI
, and the release means eitherEXEC
orDISCARD
, depending on the result of the inneruse
block.Because all the Java commands are basically
CompletableFuture
s lifted intoF
viaAsync
, once evaluated after aMULTI
call, it will never yield a result as these are queued on Redis waiting for the transaction to be committed or rolled back.So we fork every command via
.start
in order (see runner). Once theResource
hits the release part, fibers are joined or canceled to avoid leaks, depending on the transaction result.The MAIN ISSUE with this approach, is that we cannot guarantee the commands will run in order, even if we fork them in order. This is because the execution of every fiber is up to the scheduler, not to us. It is part of the runtime system.
Another implication of transactions, is that every command needs to be run in the same thread, otherwise it would be detected as part of a different transaction (or not even a transaction). To try and mitigate this issue, we introduce RedisExecutor, which simply operates on a fixed thread-pool of size one. This is wrong on so many levels, because it affects EVERYTHING, not only transactions.
This means that by default, the library will run all your commands on a single thread, affecting the overall throughput of your application. It can be configured to a higher value, but it is 1 by default.
Solutions (mainly ideas)
Custom Scheduler
Last night (for some odd reason) this issue appeared in my brain, and made me think about schedulers. Since the main issue is the scheduling of fibers, it seems all we need is a custom scheduler used for Redis transactions, no?
It is very low level, but the issue lays right there on the runtime system, so we need to tweak the JVM scheduler to ensure commands are run sequentially.
Is this doable? I don't know, but I'm laying down the idea here. Perhaps, at some point in the future, I'll give it a try if nobody does first.
Sync API
Another idea that should work — albeit less ideal — is to re-implement transactions using the underlying synchronous API, in which we don't need to deal with
CompletableFuture
s.I'm pretty sure this one would work, but it would involve a lot of code duplication, and who really wants to do that work? Definitely not me.
Remove support
Not really a solution, but I'd rather delete all this code instead of having a broken implementation. I guess this should be the last resort.
How can you help?
This issue is quite fundamental, so it would require knowledgeable people who like to tinker with code and runtimes a lot, but I believe there's also a lot to learn from the experience of diving deep into this interesting feature.
Another simple issue in which anyone can help is in updating the documentation for transactions and pipelining. We should add a big fat warning sign at the top, telling people not to use it until this problem is resolved (we can link this issue).
If you would like to help, simply comment below when you're ready to take on the issue. I don't really have much time at the moment, but I can probably provide a bit of guidance. Come chat on Matrix / Gitter.
The text was updated successfully, but these errors were encountered: