redis-cluster-client 0.7.7 → 0.7.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 158cfc3693b4057ae42249cbddcca2e5bd53bb19bb62416e44f1d1358b55c711
4
- data.tar.gz: 59917a893698b9d0fec7235821e4f9680b9e3ab4ebfd74554c48c50455a87186
3
+ metadata.gz: 5aa02a0e3934e8246e948260f2a4457b324de2377e7ec2e00fe9bf03a52f5ee4
4
+ data.tar.gz: 9f97a22af58eb64c64a6b73a79c2947bad06318e17bca235a5aa59fae35aad1d
5
5
  SHA512:
6
- metadata.gz: 116c7f397e557fc0b6a336e3481cc3e5458bac590afe454ecbfbfcafddc67ea3641f9cd020ab7b9f2cb41f3fe54188a13d4324b9496e43a27b1f58f97163363d
7
- data.tar.gz: e7ada90bdb11f0d113093792b627853a429d182c1e531d6c968fff530bbd34ed8df76655fd40223b91bc3c5d6524316302fa435438e731a8de1dcb0bc5c4ad24
6
+ metadata.gz: 2e8fcf1be08ba5c2582ad112455b02815410a81834b55bfc0035e0971556ff7b443e60a48a4cc2f5c94fdd81d09a5d03d3c261b4e0afb13ac9f38da5d4f0ab64
7
+ data.tar.gz: 77f17d9e003384f34e5719f0e84f70c8a04f7de196974fc5af830e4825827b01d7df94b4eb887d4a1a4a77f43caa989cfdcbb1e874d807503bf9ef254ea95a0d
@@ -1,47 +1,40 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'redis_client'
4
- require 'redis_client/cluster/key_slot_converter'
5
4
  require 'redis_client/cluster/transaction'
6
5
 
7
6
  class RedisClient
8
7
  class Cluster
9
8
  class OptimisticLocking
10
- def initialize(keys, router)
11
- @node = find_node!(keys, router)
12
- @keys = keys
9
+ def initialize(router)
10
+ @router = router
13
11
  end
14
12
 
15
- def watch
16
- @node.with do |c|
17
- c.call('WATCH', *@keys)
18
- reply = yield(c)
19
- c.call('UNWATCH')
20
- reply
13
+ def watch(keys)
14
+ slot = find_slot(keys)
15
+ raise ::RedisClient::Cluster::Transaction::ConsistencyError, "unsafe watch: #{keys.join(' ')}" if slot.nil?
16
+
17
+ node = @router.find_primary_node_by_slot(slot)
18
+ @router.handle_redirection(node, retry_count: 1) do |nd|
19
+ nd.with do |c|
20
+ c.call('WATCH', *keys)
21
+ reply = yield(c, slot)
22
+ c.call('UNWATCH')
23
+ reply
24
+ end
21
25
  end
22
26
  end
23
27
 
24
28
  private
25
29
 
26
- def find_node!(keys, router)
27
- raise ::RedisClient::Cluster::Transaction::ConsistencyError, "unsafe watch: #{keys.join(' ')}" unless safe?(keys)
30
+ def find_slot(keys)
31
+ return if keys.empty?
32
+ return if keys.any? { |k| k.nil? || k.empty? }
28
33
 
29
- node_key = router.find_primary_node_key(['WATCH', *keys])
30
- raise ::RedisClient::Cluster::Transaction::ConsistencyError, "couldn't determine the node" if node_key.nil?
34
+ slots = keys.map { |k| @router.find_slot_by_key(k) }
35
+ return if slots.uniq.size != 1
31
36
 
32
- router.find_node(node_key)
33
- end
34
-
35
- def safe?(keys)
36
- return false if keys.empty?
37
-
38
- slots = keys.map do |k|
39
- return false if k.nil? || k.empty?
40
-
41
- ::RedisClient::Cluster::KeySlotConverter.convert(k)
42
- end
43
-
44
- slots.uniq.size == 1
37
+ slots.first
45
38
  end
46
39
  end
47
40
  end
@@ -179,6 +179,16 @@ class RedisClient
179
179
  find_node_key_by_key(key, primary: true)
180
180
  end
181
181
 
182
+ def find_slot(command)
183
+ find_slot_by_key(@command.extract_first_key(command))
184
+ end
185
+
186
+ def find_slot_by_key(key)
187
+ return if key.empty?
188
+
189
+ ::RedisClient::Cluster::KeySlotConverter.convert(key)
190
+ end
191
+
182
192
  def find_node(node_key, retry_count: 3)
183
193
  @node.find_by(node_key)
184
194
  rescue ::RedisClient::Cluster::Node::ReloadNeeded
@@ -2,14 +2,14 @@
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)
10
+ MAX_REDIRECTION = 2
11
11
 
12
- def initialize(router, command_builder, node = nil)
12
+ def initialize(router, command_builder, node: nil, slot: nil)
13
13
  @router = router
14
14
  @command_builder = command_builder
15
15
  @retryable = true
@@ -17,6 +17,7 @@ class RedisClient
17
17
  @pending_commands = []
18
18
  @node = node
19
19
  prepare_tx unless @node.nil?
20
+ @watching_slot = slot
20
21
  end
21
22
 
22
23
  def call(*command, **kwargs, &block)
@@ -92,7 +93,7 @@ class RedisClient
92
93
 
93
94
  def settle
94
95
  @pipeline.call('EXEC')
95
- send_transaction(@node, redirect: true)
96
+ send_transaction(@node, redirect: MAX_REDIRECTION)
96
97
  end
97
98
 
98
99
  def send_transaction(client, redirect:)
@@ -109,7 +110,7 @@ class RedisClient
109
110
  client.middlewares.call_pipelined(commands, client.config) do
110
111
  connection.call_pipelined(commands, nil)
111
112
  rescue ::RedisClient::CommandError => e
112
- return handle_command_error!(commands, e) if redirect
113
+ return handle_command_error!(commands, e, redirect: redirect) unless redirect.zero?
113
114
 
114
115
  raise
115
116
  end
@@ -138,41 +139,30 @@ class RedisClient
138
139
  results
139
140
  end
140
141
 
141
- def handle_command_error!(commands, err)
142
+ def handle_command_error!(commands, err, redirect:) # rubocop:disable Metrics/AbcSize
142
143
  if err.message.start_with?('CROSSSLOT')
143
144
  raise ConsistencyError, "#{err.message}: #{err.command}"
144
- elsif err.message.start_with?('MOVED', 'ASK')
145
- ensure_the_same_node!(commands)
146
- handle_redirection(err)
147
- else
148
- raise err
149
- end
150
- end
151
-
152
- def ensure_the_same_node!(commands)
153
- expected_node_key = NodeKey.build_from_client(@node)
154
-
155
- commands.each do |command|
156
- node_key = @router.find_primary_node_key(command)
157
- next if node_key.nil?
158
- next if node_key == expected_node_key
159
-
160
- raise ConsistencyError, "the transaction should be executed to a slot in a node: #{commands}"
161
- end
162
- end
163
-
164
- def handle_redirection(err)
165
- if err.message.start_with?('MOVED')
145
+ elsif err.message.start_with?('MOVED')
146
+ ensure_the_same_slot!(commands)
166
147
  node = @router.assign_redirection_node(err.message)
167
- send_transaction(node, redirect: false)
148
+ send_transaction(node, redirect: redirect - 1)
168
149
  elsif err.message.start_with?('ASK')
150
+ ensure_the_same_slot!(commands)
169
151
  node = @router.assign_asking_node(err.message)
170
- try_asking(node) ? send_transaction(node, redirect: false) : err
152
+ try_asking(node) ? send_transaction(node, redirect: redirect - 1) : err
171
153
  else
172
154
  raise err
173
155
  end
174
156
  end
175
157
 
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
162
+
163
+ raise(ConsistencyError, "the transaction should be executed to a slot in a node: #{commands}")
164
+ end
165
+
176
166
  def try_asking(node)
177
167
  node.call('ASKING') == 'OK'
178
168
  rescue StandardError
@@ -95,14 +95,15 @@ class RedisClient
95
95
  if watch.nil? || watch.empty?
96
96
  transaction = ::RedisClient::Cluster::Transaction.new(@router, @command_builder)
97
97
  yield transaction
98
+ return transaction.execute
99
+ end
100
+
101
+ ::RedisClient::Cluster::OptimisticLocking.new(@router).watch(watch) do |c, slot|
102
+ transaction = ::RedisClient::Cluster::Transaction.new(
103
+ @router, @command_builder, node: c, slot: slot
104
+ )
105
+ yield transaction
98
106
  transaction.execute
99
- else
100
- locking = ::RedisClient::Cluster::OptimisticLocking.new(watch, @router)
101
- locking.watch do |c|
102
- transaction = ::RedisClient::Cluster::Transaction.new(@router, @command_builder, c)
103
- yield transaction
104
- transaction.execute
105
- end
106
107
  end
107
108
  end
108
109
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis-cluster-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.7
4
+ version: 0.7.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Taishi Kasuga