|
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