|
2 | 2 |
|
3 | 3 | require 'redis_client' |
4 | 4 | require 'redis_client/cluster/pipeline' |
5 | | -require 'redis_client/cluster/node_key' |
6 | 5 |
|
7 | 6 | class RedisClient |
8 | 7 | class Cluster |
9 | 8 | class Transaction |
10 | 9 | ConsistencyError = Class.new(::RedisClient::Error) |
11 | 10 | MAX_REDIRECTION = 2 |
12 | 11 |
|
13 | | - def initialize(router, command_builder, node: nil, resharding: false) |
| 12 | + def initialize(router, command_builder, node: nil, slot: nil) |
14 | 13 | @router = router |
15 | 14 | @command_builder = command_builder |
16 | 15 | @retryable = true |
17 | 16 | @pipeline = ::RedisClient::Pipeline.new(@command_builder) |
18 | 17 | @pending_commands = [] |
19 | 18 | @node = node |
20 | 19 | prepare_tx unless @node.nil? |
21 | | - @resharding_state = resharding |
| 20 | + @watching_slot = slot |
22 | 21 | end |
23 | 22 |
|
24 | 23 | def call(*command, **kwargs, &block) |
@@ -111,7 +110,7 @@ def send_pipeline(client, redirect:) |
111 | 110 | client.middlewares.call_pipelined(commands, client.config) do |
112 | 111 | connection.call_pipelined(commands, nil) |
113 | 112 | rescue ::RedisClient::CommandError => e |
114 | | - return handle_command_error!(client, commands, e, redirect: redirect) unless redirect.zero? |
| 113 | + return handle_command_error!(commands, e, redirect: redirect) unless redirect.zero? |
115 | 114 |
|
116 | 115 | raise |
117 | 116 | end |
@@ -140,28 +139,26 @@ def coerce_results!(results, offset: 1) |
140 | 139 | results |
141 | 140 | end |
142 | 141 |
|
143 | | - def handle_command_error!(client, commands, err, redirect:) # rubocop:disable Metrics/AbcSize |
| 142 | + def handle_command_error!(commands, err, redirect:) # rubocop:disable Metrics/AbcSize |
144 | 143 | if err.message.start_with?('CROSSSLOT') |
145 | 144 | raise ConsistencyError, "#{err.message}: #{err.command}" |
146 | 145 | elsif err.message.start_with?('MOVED') |
147 | | - ensure_the_same_node!(client, commands) |
| 146 | + ensure_the_same_slot!(commands) |
148 | 147 | node = @router.assign_redirection_node(err.message) |
149 | 148 | send_transaction(node, redirect: redirect - 1) |
150 | 149 | elsif err.message.start_with?('ASK') |
151 | | - ensure_the_same_node!(client, commands) |
| 150 | + ensure_the_same_slot!(commands) |
152 | 151 | node = @router.assign_asking_node(err.message) |
153 | 152 | try_asking(node) ? send_transaction(node, redirect: redirect - 1) : err |
154 | 153 | else |
155 | 154 | raise err |
156 | 155 | end |
157 | 156 | end |
158 | 157 |
|
159 | | - def ensure_the_same_node!(client, commands) |
160 | | - node_keys = commands.map { |command| @router.find_primary_node_key(command) }.compact.uniq |
161 | | - expected_node_key = ::RedisClient::Cluster::NodeKey.build_from_client(client) |
162 | | - |
163 | | - return if !@resharding_state && node_keys.size == 1 && node_keys.first == expected_node_key |
164 | | - return if @resharding_state && node_keys.size == 1 |
| 158 | + def ensure_the_same_slot!(commands) |
| 159 | + slots = commands.map { |command| @router.find_slot(command) }.compact.uniq |
| 160 | + return if slots.size == 1 && @watching_slot.nil? |
| 161 | + return if slots.size == 1 && @watching_slot == slots.first |
165 | 162 |
|
166 | 163 | raise(ConsistencyError, "the transaction should be executed to a slot in a node: #{commands}") |
167 | 164 | end |
|
0 commit comments