redis / ioredis

šŸš€ A robust, performance-focused, and full-featured Redis client for Node.js.
MIT License
14.42k stars 1.2k forks source link

Error: All keys in the pipeline should belong to the same slots allocation group #1602

Open ArtemHoruzhenko opened 2 years ago

ArtemHoruzhenko commented 2 years ago

There is an error when trying to execute pipeline on IORedis.Cluster instance. I'm sure that data belongs to a single shard. When I get some particular node using .getNodes and execute pipeline on that particular node (IORedis.Redis instance) it works fine

Environment:

Redis Cloud Cluster with 6 shards

Data example:

set "\xac\xed\x00\x05t\x0a4100000252" bar
set "\xac\xed\x00\x05t\x0a4100000255" bar

These 2 keys should be allocated to the same shard.

<IORedis.Cluster>.pipeline([
  ['type', <Buffer for \xac\xed\x00\x05t\x0a4100000252>],
  ['type', <Buffer for \xac\xed\x00\x05t\x0a4100000255>],
]).exec()

Will throw an error: All keys in the pipeline should belong to the same slots allocation group

But if you pick any node from the target shard and execute the same pipeline on it (<IORedis.Redis>.pipelien(...).exec()) it will work fine (just to prove that keys belongs to the same shard)

Also interesting thing:

<IORedis.Cluster>.pipeline([
  ['memory usage', <Buffer for \xac\xed\x00\x05t\x0a4100000252>, 'samples', '0'],
  ['memory usage', <Buffer for \xac\xed\x00\x05t\x0a4100000255>, 'samples', '0'],
]).exec()

Will fail with the same error when:

<IORedis.Cluster>.pipeline([
  ['type', <Buffer for \xac\xed\x00\x05t\x0a4100000252>],
  ['memory usage', <Buffer for \xac\xed\x00\x05t\x0a4100000252>, 'samples', '0'],
  ['memory usage', <Buffer for \xac\xed\x00\x05t\x0a4100000255>, 'samples', '0'],
]).exec()

will not

luin commented 2 years ago

Hey @arthosofteq šŸ‘‹,

These 2 keys should be allocated to the same shard.

The 2 keys belong to two different slots, although the two slots may be severed by the same shard at the moment. Ioredis doesn't know that before it reaches to server, so it throws early without sending them to the server to ensure the semantic of a pipeline.

I think it's risky to assume that two slots always belong to the same shard as it could be false after a failover or cluster re-setup. Generally, if you want to use pipeline in cluster, you need to ensure that they are not only severed by the same shard, but also they belong to the same slot.

Hope that makes sense.

Regarding the second issue, I'm not able to reproduce it as 'memory usage' is not a valid command (I assume you mean ['memory', 'usage', ...] but it still doesn't report the same error). Can you provide a reproducible example so I can test on my side?

undeadcat commented 1 year ago

Stumbled onto this error message.

@luin, I'm curious, what do you mean by 'ensure the semantic of a pipeline'? Is it a choice to not support multi-slot pipelines or an incomplete feature?

A pipeline is not a transaction, some operations may fail. I have not studied the topic too deeply, but initially seems like it might be reasonable for a Redis client library to provide 'best-effort' (up to a number of redirects) handling of multi-slot pipelines and ultimately (if still rebalancing), report that some operations failed.

The standard CLI client handles multi-slot pipelines:

% echo "SET foo 1\r\nSET foo2 1\r\nSET foo3 1\r\nSET foo4 1" | redis-cli -h <cluster-address> -p 6379 -c
-> Redirected to slot [12182] located at 172.28.224.50:6379
OK
-> Redirected to slot [1044] located at 172.28.199.77:6379
OK
OK
-> Redirected to slot [9426] located at 172.28.210.21:6379
OK
% echo "GET foo\r\nGET foo2\r\nGET foo3\r\nGET foo4" | redis-cli -h <cluster-address> -p 6379 -c        
-> Redirected to slot [12182] located at 172.28.224.50:6379
"1"
-> Redirected to slot [1044] located at 172.28.199.77:6379
"1"
"1"
-> Redirected to slot [9426] located at 172.28.210.21:6379
"1"

Jedis seems to handle multi-slot pipelines as well: https://github.com/redis/jedis https://gist.github.com/undeadcat/b1b22acc324a1e61a9b0ac96daeca863

LRagji commented 1 year ago

Same issue, @luin I have keys which are belonging to different slots/nodes and i am expecting it to work just as @undeadcat cli request.. since the whole intention of pipeline is to send all commands together to cluster, if there are redirects they can be handled by the ioredis for best possible resolution.. else you are forcing consumers in this scenario to co-locate keys on single node which defeats the purpose of cluster.... Another workaround would be to provide a resolver function which can distribute the commands into different sets according to slots and then let consumer call pipeline for each individual set...

enchorb commented 1 month ago

Any updates here? enableAutoPipelining only works if scaleReads is set to master