-
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(cluster): fix autopipeline with keyPrefix or arg array #1391
Conversation
ec6e154
to
941d5e5
Compare
Keys will hash to different nodes and, therefore, wouldnt allow them to be used inside the same pipeline or am I missing something? |
I'm not quite sure what you mean. If there are 16385 different keys and 16384 different nodes, at least two of the keys will go to the same node because of https://en.wikipedia.org/wiki/Pigeonhole_principle The issue is that when a cluster was used, we need to use the prefixed key (prefixed + args[0]) to compute the destination node, not the unprefixed key (args[0]) |
https://github.com/invertase/cluster-key-slot/blob/62a7bce30c668f83c89f331943e882d2c92899c0/lib/index.js#L147 in this case, there's 16384 possible values from 0 to 0x3FFF and there would be collisions if there were more than 16384 keys, and even with a few hundred keys there are likely to be false positive hash collisions due to https://en.wikipedia.org/wiki/Birthday_problem EDIT: |
An example of the failure seen running this without the patch. With this patch, it succeeds const IORedis = require('./built');
const process = require('process');
async function main() {
// Using instructions from https://redis.io/topics/cluster-tutorial
// redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 --cluster-replicas 0
const redis = new IORedis.Cluster([{ host: '127.0.0.1', port:7000 }], { keyPrefix: 'test:', enableAutoPipelining: true });
const requests = [];
for (let i = 0; i < 10; i++) {
requests.push(redis.get(`k${i}`));
}
const results = await Promise.all(requests);
console.log(results);
process.exit(0);
}
main();
|
lib/autoPipelining.ts
Outdated
const slotKey = client.isCluster | ||
? client.slots[ | ||
calculateSlot( | ||
client.options.keyPrefix | ||
? `${client.options.keyPrefix}${args[0]}` | ||
: args[0] | ||
) | ||
].join(",") | ||
: "main"; |
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.
const slotKey = client.isCluster | |
? client.slots[ | |
calculateSlot( | |
client.options.keyPrefix | |
? `${client.options.keyPrefix}${args[0]}` | |
: args[0] | |
) | |
].join(",") | |
: "main"; | |
const prefix = client.options.keyPrefix || ''; | |
const slotKey = client.isCluster | |
? client.slots[ | |
calculateSlot(`${prefix}${args[0]}`) | |
].join(",") | |
: "main"; |
this should likely be cheaper. we can also avoid assigning empty string to keyPrefix if we set it in the constructor
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.
redis.get([], [], ['key'])
is technically allowed by ioredis because all of the args get passed to lodash flatten. I added getFirstValueInFlattenedArray and some unit tests to work as a fallback for more common cases.
Less common cases (_.isArguments) may benefit from https://github.com/lodash/lodash/blob/master/.internal/isFlattenable.js but that seems brittle, and I don't know ioredis's policy on using internal implementation details like that
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.
Did something similar, but accounting for the fact that args[0]
can be an array
finally realized this is only related to auto pipelining, change makes sense to me |
941d5e5
to
5e067d6
Compare
2e10f8a
to
e1cab95
Compare
This is ready for another look |
ca23dc5
to
36f6387
Compare
@luin this LGTM, what do you think? |
Previously, the building of pipelines ignored the key prefix. It was possible that two keys, foo and bar, might be set into the same pipeline. However, after being prefixed by a configured "keyPrefix" value, they may no longer belong to the same pipeline. This led to the error: "All keys in the pipeline should belong to the same slots allocation group" Similarly, `args[0]` can be a (possibly empty) array which the Command constructor would flatten. Properly compute the first flattened key when autopipelining for a Redis.Cluster instance. Amended version of https://github.com/luin/ioredis/pull/1335/files - see comments on that PR This may fix redis#1264 and redis#1248. Fixes redis#1392
c5b077e
to
f116d48
Compare
My changes since the last review are minor:
|
LGTM! Thanks @TysonAndre |
🎉 This PR is included in version 4.27.7 🎉 The release is available on: Your semantic-release bot 📦🚀 |
## [4.27.7](redis/ioredis@v4.27.6...v4.27.7) (2021-08-01) ### Bug Fixes * **cluster:** fix autopipeline with keyPrefix or arg array ([#1391](redis/ioredis#1391)) ([d7477aa](redis/ioredis@d7477aa)), closes [#1264](redis/ioredis#1264) [#1248](redis/ioredis#1248) [#1392](redis/ioredis#1392)
Previously the building of pipelines ignored the key prefix.
It was possible that two keys, foo and bar, might be set into
the same pipeline. However, after being prefixed by a configured
"keyPrefix" value, they may no longer belong to the same
pipeline.
This led to the error:
"All keys in the pipeline should belong to the same slots
allocation group"
Similarly,
args[0]
can be a (possibly empty) array which the Commandconstructor would flatten. Properly compute the first flattened key when
autopipelining for a Redis.Cluster instance.
Amended version of https://github.com/luin/ioredis/pull/1335/files - see comments on that PR
This may fix #1264 and #1248.
Fixes #1392