-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Fix script loading behavior on script flush with pipelines #1497
Fix script loading behavior on script flush with pipelines #1497
Conversation
020bac9
to
b7b213c
Compare
b7b213c
to
bdf88bd
Compare
Regarding the tests, maybe listing the redis version during the docker run could be useful. Somehow the node 6 tests failed before some small fix. But that should have worked with the |
Isnt this case is handled by this code? If the script is missing then we will get the NOSCRIPT error, which will force ioredis to load it. |
Not in pipeline mode. That gets skipped as the result of sendCommand is no promise. const result = container.sendCommand(evalsha);
// This is false, result is an instance of Pipeline
if (isPromise(result)) { |
I did another commit to handle the NOSCRIPT errors in 53472a7 Resetting the scripts is still useful with this as without it, it would execute I'm totally not sure whether those changes are fine. It might have some side effects in the ordering (in the tests it was fine). Not sure about the cluster support. |
Now everything's working properly. |
731bbb4
to
f36a582
Compare
lib/script.ts
Outdated
if (err.toString().indexOf("NOSCRIPT") === -1) { | ||
throw err; | ||
} | ||
const command = new Command("eval", [this.lua].concat(args), options); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replacing that with a SCRIPT LOAD
would be another task.
redis._addedScriptHashes[this.sha] = true
return redis.pipeline(['script', 'load', this.lua], ['evalsha', this.sha]])
.exec()
.then(([,res]) => res)
I might refactor the script load behavior once I have some time. Scripts themselves are loaded synchronously instead of using another pipeline or the same pipeline.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd have other fixes that uses pipeline-based script loading: https://github.com/marcbachmann/ioredis/compare/fix-redis-reconnect-NOSCRIPT-errors...marcbachmann:pipelined-evalsha?expand=1
As it's a bit bigger, and I don't like to merge it into this pr.
The only disadvantage is that it doesn't use script exits
checks anymore and just uses script load
after the interval expired. I guess redis already hashes by sha internally and only loads the script if it's not present. So that should be fine.
But as there's no roundtrip anymore and script caching time can be set much higher with it. NOSCRIPT errors are also retried in there (should also work better for the cluster support).
edit: Ok. it looks like I can simplify the code there. An eval also automatically caches the script in redis.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO this is a serious statement, we have really high load on our services with a lot of lua scripts(we use redis with redisLabs as our main persistent DB), from time to time we loose connection so we have to reconnect. if it will cause sending the script string each time and run EVAL, it may be a disaster :) |
Yes. This PR now fixes this behavior. New commands coming in will first load the script again. And at the moment it always does an exists check: https://github.com/luin/ioredis/blob/f275bc24de3825f80415a69ff227a45251dd1a3b/lib/pipeline.ts#L282 With the update, we only have the issue that already queued messages will do an eval. Somewhat like #1497 (comment) would fix this. |
Hey @marcbachmann 👋 Thanks for the contribution. I didn't realize this issue so it's a great fix! The reason we don't retry on
Not sure if I understand this, why not resetting the scripts results in #1499 looks promising, as I still need some time reviewing it, for this PR, should we just delete the |
You're welcome. Yes, then let's remove the fallback logic again and simplify that PR. #1499 might be a better approach to handle the script caches.
Good to know, not sure this was mentioned anywhere.
Yes, but it will execute the
👍 |
f36a582
to
6439c20
Compare
This is now ready to merge |
Fair point. I 💯 agree with that. I think we should then just priority code maintainability over the behavior choice, though introducing breaking changes means we have to wait for the schedule of the next major version. |
## [4.28.5](v4.28.4...v4.28.5) (2022-02-06) ### Bug Fixes * Reset loaded script hashes to force a reload of scripts after reconnect of redis ([#1497](#1497)) ([f357a31](f357a31))
🎉 This PR is included in version 4.28.5 🎉 The release is available on: Your semantic-release bot 📦🚀 |
## [4.28.5](redis/ioredis@v4.28.4...v4.28.5) (2022-02-06) ### Bug Fixes * Reset loaded script hashes to force a reload of scripts after reconnect of redis ([#1497](redis/ioredis#1497)) ([f357a31](redis/ioredis@f357a31))
Lua scripts get loaded once and not retried unless a client side interval expires: https://github.com/luin/ioredis/blob/master/lib/pipeline.ts#L376
This causes issues with redis instances restarting or failing over.
There's no way of knowing whether a script is still present in the new instance.
We should reset the local state and just load them again when a reconnect happens and rely on the existing code that checks whether the scripts are present on the server.
I ran into that issue while testing the reliability of a service, but it might also fix #1405See #1499 for the cluster fixes.There's still a small time window where
NOSCRIPT
errors occur as messages are already queued for execution.Previously the script handler only executedEVAL
in the non-pipeline mode. This now changed and always gets executed.Changelog