Skip to content

Commit 25715d5

Browse files
committed
Fix a bug for cross-slot validation for pipelining
1 parent baf5706 commit 25715d5

File tree

2 files changed

+30
-12
lines changed

2 files changed

+30
-12
lines changed

lib/redis/cluster.rb

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,13 @@ def call_loop(command, timeout = 0, &block)
7878
end
7979

8080
def call_pipeline(pipeline)
81-
node_keys, command_keys = extract_keys_in_pipeline(pipeline)
82-
raise CrossSlotPipeliningError, command_keys if node_keys.size > 1
81+
node_keys = pipeline.commands.map { |cmd| find_node_key(cmd, primary_only: true) }.compact.uniq
82+
if node_keys.size > 1
83+
raise(CrossSlotPipeliningError,
84+
pipeline.commands.map { |cmd| @command.extract_first_key(cmd) }.reject(&:empty?).uniq)
85+
end
8386

84-
node = find_node(node_keys.first)
85-
try_send(node, :call_pipeline, pipeline)
87+
try_send(find_node(node_keys.first), :call_pipeline, pipeline)
8688
end
8789

8890
def call_with_timeout(command, timeout, &block)
@@ -253,14 +255,14 @@ def assign_node(command)
253255
find_node(node_key)
254256
end
255257

256-
def find_node_key(command)
258+
def find_node_key(command, primary_only: false)
257259
key = @command.extract_first_key(command)
258260
return if key.empty?
259261

260262
slot = KeySlotConverter.convert(key)
261263
return unless @slot.exists?(slot)
262264

263-
if @command.should_send_to_master?(command)
265+
if @command.should_send_to_master?(command) || primary_only
264266
@slot.find_node_key_of_master(slot)
265267
else
266268
@slot.find_node_key_of_slave(slot)
@@ -285,11 +287,5 @@ def update_cluster_info!(node_key = nil)
285287
@node.map(&:disconnect)
286288
@node, @slot = fetch_cluster_info!(@option)
287289
end
288-
289-
def extract_keys_in_pipeline(pipeline)
290-
node_keys = pipeline.commands.map { |cmd| find_node_key(cmd) }.compact.uniq
291-
command_keys = pipeline.commands.map { |cmd| @command.extract_first_key(cmd) }.reject(&:empty?)
292-
[node_keys, command_keys]
293-
end
294290
end
295291
end

test/cluster_client_pipelining_test.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,26 @@ def test_pipelining_without_hash_tags
5656
end
5757
end
5858
end
59+
60+
def test_pipelining_with_multiple_replicas
61+
rc = build_another_client(replica: true)
62+
rc.instance_variable_get(:@client).instance_variable_get(:@slot).instance_variable_get(:@map).each do |_, v|
63+
v[:slaves] << v[:master] if v[:slaves].size < 2 # reproducing multiple replicas
64+
end
65+
66+
rc.pipelined do |r|
67+
r.get('key1')
68+
r.get('key1')
69+
r.get('key1')
70+
r.get('key1')
71+
r.get('key1')
72+
r.get('key1')
73+
r.get('key1')
74+
r.get('key1')
75+
r.get('key1')
76+
r.get('key1')
77+
end
78+
79+
rc.close
80+
end
5981
end

0 commit comments

Comments
 (0)