redis-cluster-client 0.7.11 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b27b124d30b25712726c2d6471fffe4fd879de2dabb2002d0654e40b1e24b90e
|
4
|
+
data.tar.gz: eae0cd4ff3d6ab6317f7fff7493df1da9fb4386b4fbd187619d41e0bdf891d56
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a353a42631431794e3d1d9616810ef5378930765683013cb96c8e49c010434e2f3f08995b0e48601492791b577612a9bf688dd0b186b72b0bba54e0330f24e8f
|
7
|
+
data.tar.gz: f87fed71fda0b9cf9ab3dde1b402f8c4b0c921c85efe8d794ef9fee7a48d143f43b829e6f2cd6ca01532f0c8c6ca8f682195ea3673aefc9ba5d342a9871a6c75
|
@@ -8,26 +8,56 @@ class RedisClient
|
|
8
8
|
class OptimisticLocking
|
9
9
|
def initialize(router)
|
10
10
|
@router = router
|
11
|
+
@asking = false
|
11
12
|
end
|
12
13
|
|
13
14
|
def watch(keys)
|
14
15
|
slot = find_slot(keys)
|
15
16
|
raise ::RedisClient::Cluster::Transaction::ConsistencyError, "unsafe watch: #{keys.join(' ')}" if slot.nil?
|
16
17
|
|
18
|
+
# We have not yet selected a node for this transaction, initially, which means we can handle
|
19
|
+
# redirections freely initially (i.e. for the first WATCH call)
|
17
20
|
node = @router.find_primary_node_by_slot(slot)
|
18
|
-
|
21
|
+
handle_redirection(node, retry_count: 1) do |nd|
|
19
22
|
nd.with do |c|
|
20
|
-
c.
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
23
|
+
c.ensure_connected_cluster_scoped(retryable: false) do
|
24
|
+
c.call('ASKING') if @asking
|
25
|
+
c.call('WATCH', *keys)
|
26
|
+
begin
|
27
|
+
yield(c, slot, @asking)
|
28
|
+
rescue ::RedisClient::ConnectionError
|
29
|
+
# No need to unwatch on a connection error.
|
30
|
+
raise
|
31
|
+
rescue StandardError
|
32
|
+
c.call('UNWATCH')
|
33
|
+
raise
|
34
|
+
end
|
35
|
+
end
|
25
36
|
end
|
26
37
|
end
|
27
38
|
end
|
28
39
|
|
29
40
|
private
|
30
41
|
|
42
|
+
def handle_redirection(node, retry_count: 1, &blk)
|
43
|
+
@router.handle_redirection(node, retry_count: retry_count) do |nd|
|
44
|
+
handle_asking_once(nd, &blk)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def handle_asking_once(node)
|
49
|
+
yield node
|
50
|
+
rescue ::RedisClient::CommandError => e
|
51
|
+
raise unless ErrorIdentification.client_owns_error?(e, node)
|
52
|
+
raise unless e.message.start_with?('ASK')
|
53
|
+
|
54
|
+
node = @router.assign_asking_node(e.message)
|
55
|
+
@asking = true
|
56
|
+
yield node
|
57
|
+
ensure
|
58
|
+
@asking = false
|
59
|
+
end
|
60
|
+
|
31
61
|
def find_slot(keys)
|
32
62
|
return if keys.empty?
|
33
63
|
return if keys.any? { |k| k.nil? || k.empty? }
|
@@ -12,7 +12,7 @@ class RedisClient
|
|
12
12
|
class Extended < ::RedisClient::Pipeline
|
13
13
|
attr_reader :outer_indices
|
14
14
|
|
15
|
-
def initialize(
|
15
|
+
def initialize(...)
|
16
16
|
super
|
17
17
|
@outer_indices = nil
|
18
18
|
end
|
@@ -50,14 +50,14 @@ class RedisClient
|
|
50
50
|
end
|
51
51
|
|
52
52
|
::RedisClient::ConnectionMixin.module_eval do
|
53
|
-
def call_pipelined_aware_of_redirection(commands, timeouts) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
53
|
+
def call_pipelined_aware_of_redirection(commands, timeouts, exception:) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
54
54
|
size = commands.size
|
55
55
|
results = Array.new(commands.size)
|
56
56
|
@pending_reads += size
|
57
57
|
write_multi(commands)
|
58
58
|
|
59
59
|
redirection_indices = nil
|
60
|
-
|
60
|
+
first_exception = nil
|
61
61
|
size.times do |index|
|
62
62
|
timeout = timeouts && timeouts[index]
|
63
63
|
result = read(timeout)
|
@@ -67,14 +67,14 @@ class RedisClient
|
|
67
67
|
if result.is_a?(::RedisClient::CommandError) && result.message.start_with?('MOVED', 'ASK')
|
68
68
|
redirection_indices ||= []
|
69
69
|
redirection_indices << index
|
70
|
-
|
71
|
-
|
70
|
+
elsif exception
|
71
|
+
first_exception ||= result
|
72
72
|
end
|
73
73
|
end
|
74
74
|
results[index] = result
|
75
75
|
end
|
76
76
|
|
77
|
-
raise
|
77
|
+
raise first_exception if exception && first_exception
|
78
78
|
return results if redirection_indices.nil?
|
79
79
|
|
80
80
|
err = ::RedisClient::Cluster::Pipeline::RedirectionNeeded.new
|
@@ -98,10 +98,11 @@ class RedisClient
|
|
98
98
|
attr_accessor :replies, :indices
|
99
99
|
end
|
100
100
|
|
101
|
-
def initialize(router, command_builder, concurrent_worker, seed: Random.new_seed)
|
101
|
+
def initialize(router, command_builder, concurrent_worker, exception:, seed: Random.new_seed)
|
102
102
|
@router = router
|
103
103
|
@command_builder = command_builder
|
104
104
|
@concurrent_worker = concurrent_worker
|
105
|
+
@exception = exception
|
105
106
|
@seed = seed
|
106
107
|
@pipelines = nil
|
107
108
|
@size = 0
|
@@ -212,7 +213,7 @@ class RedisClient
|
|
212
213
|
results = client.ensure_connected_cluster_scoped(retryable: pipeline._retryable?) do |connection|
|
213
214
|
commands = pipeline._commands
|
214
215
|
client.middlewares.call_pipelined(commands, client.config) do
|
215
|
-
connection.call_pipelined_aware_of_redirection(commands, pipeline._timeouts)
|
216
|
+
connection.call_pipelined_aware_of_redirection(commands, pipeline._timeouts, exception: @exception)
|
216
217
|
end
|
217
218
|
end
|
218
219
|
|
@@ -224,15 +225,21 @@ class RedisClient
|
|
224
225
|
|
225
226
|
if err.message.start_with?('MOVED')
|
226
227
|
node = @router.assign_redirection_node(err.message)
|
227
|
-
|
228
|
+
try_redirection(node, pipeline, inner_index)
|
228
229
|
elsif err.message.start_with?('ASK')
|
229
230
|
node = @router.assign_asking_node(err.message)
|
230
|
-
try_asking(node) ?
|
231
|
+
try_asking(node) ? try_redirection(node, pipeline, inner_index) : err
|
231
232
|
else
|
232
233
|
err
|
233
234
|
end
|
234
235
|
end
|
235
236
|
|
237
|
+
def try_redirection(node, pipeline, inner_index)
|
238
|
+
redirect_command(node, pipeline, inner_index)
|
239
|
+
rescue StandardError => e
|
240
|
+
@exception ? raise : e
|
241
|
+
end
|
242
|
+
|
236
243
|
def redirect_command(node, pipeline, inner_index)
|
237
244
|
method = pipeline.get_callee_method(inner_index)
|
238
245
|
command = pipeline.get_command(inner_index)
|
@@ -315,9 +315,9 @@ class RedisClient
|
|
315
315
|
def send_watch_command(command)
|
316
316
|
raise ::RedisClient::Cluster::Transaction::ConsistencyError, 'A block required. And you need to use the block argument as a client for the transaction.' unless block_given?
|
317
317
|
|
318
|
-
::RedisClient::Cluster::OptimisticLocking.new(self).watch(command[1..]) do |c, slot|
|
318
|
+
::RedisClient::Cluster::OptimisticLocking.new(self).watch(command[1..]) do |c, slot, asking|
|
319
319
|
transaction = ::RedisClient::Cluster::Transaction.new(
|
320
|
-
self, @command_builder, node: c, slot: slot
|
320
|
+
self, @command_builder, node: c, slot: slot, asking: asking
|
321
321
|
)
|
322
322
|
yield transaction
|
323
323
|
transaction.execute
|
@@ -9,7 +9,7 @@ class RedisClient
|
|
9
9
|
ConsistencyError = Class.new(::RedisClient::Error)
|
10
10
|
MAX_REDIRECTION = 2
|
11
11
|
|
12
|
-
def initialize(router, command_builder, node: nil, slot: nil)
|
12
|
+
def initialize(router, command_builder, node: nil, slot: nil, asking: false)
|
13
13
|
@router = router
|
14
14
|
@command_builder = command_builder
|
15
15
|
@retryable = true
|
@@ -18,6 +18,7 @@ class RedisClient
|
|
18
18
|
@node = node
|
19
19
|
prepare_tx unless @node.nil?
|
20
20
|
@watching_slot = slot
|
21
|
+
@asking = asking
|
21
22
|
end
|
22
23
|
|
23
24
|
def call(*command, **kwargs, &block)
|
@@ -93,7 +94,11 @@ class RedisClient
|
|
93
94
|
|
94
95
|
def settle
|
95
96
|
@pipeline.call('EXEC')
|
96
|
-
|
97
|
+
# If we needed ASKING on the watch, we need ASKING on the multi as well.
|
98
|
+
@node.call('ASKING') if @asking
|
99
|
+
# Don't handle redirections at this level if we're in a watch (the watcher handles redirections
|
100
|
+
# at the whole-transaction level.)
|
101
|
+
send_transaction(@node, redirect: !!@watching_slot ? 0 : MAX_REDIRECTION)
|
97
102
|
end
|
98
103
|
|
99
104
|
def send_transaction(client, redirect:)
|
@@ -110,7 +115,8 @@ class RedisClient
|
|
110
115
|
client.middlewares.call_pipelined(commands, client.config) do
|
111
116
|
connection.call_pipelined(commands, nil)
|
112
117
|
rescue ::RedisClient::CommandError => e
|
113
|
-
|
118
|
+
ensure_the_same_slot!(commands)
|
119
|
+
return handle_command_error!(e, redirect: redirect) unless redirect.zero?
|
114
120
|
|
115
121
|
raise
|
116
122
|
end
|
@@ -139,15 +145,13 @@ class RedisClient
|
|
139
145
|
results
|
140
146
|
end
|
141
147
|
|
142
|
-
def handle_command_error!(
|
148
|
+
def handle_command_error!(err, redirect:) # rubocop:disable Metrics/AbcSize
|
143
149
|
if err.message.start_with?('CROSSSLOT')
|
144
150
|
raise ConsistencyError, "#{err.message}: #{err.command}"
|
145
151
|
elsif err.message.start_with?('MOVED')
|
146
|
-
ensure_the_same_slot!(commands)
|
147
152
|
node = @router.assign_redirection_node(err.message)
|
148
153
|
send_transaction(node, redirect: redirect - 1)
|
149
154
|
elsif err.message.start_with?('ASK')
|
150
|
-
ensure_the_same_slot!(commands)
|
151
155
|
node = @router.assign_asking_node(err.message)
|
152
156
|
try_asking(node) ? send_transaction(node, redirect: redirect - 1) : err
|
153
157
|
else
|
data/lib/redis_client/cluster.rb
CHANGED
@@ -82,9 +82,16 @@ class RedisClient
|
|
82
82
|
@router.try_delegate(node, :zscan, key, *args, **kwargs, &block)
|
83
83
|
end
|
84
84
|
|
85
|
-
def pipelined
|
85
|
+
def pipelined(exception: true)
|
86
86
|
seed = @config.use_replica? && @config.replica_affinity == :random ? nil : Random.new_seed
|
87
|
-
pipeline = ::RedisClient::Cluster::Pipeline.new(
|
87
|
+
pipeline = ::RedisClient::Cluster::Pipeline.new(
|
88
|
+
@router,
|
89
|
+
@command_builder,
|
90
|
+
@concurrent_worker,
|
91
|
+
exception: exception,
|
92
|
+
seed: seed
|
93
|
+
)
|
94
|
+
|
88
95
|
yield pipeline
|
89
96
|
return [] if pipeline.empty?
|
90
97
|
|
@@ -98,9 +105,9 @@ class RedisClient
|
|
98
105
|
return transaction.execute
|
99
106
|
end
|
100
107
|
|
101
|
-
::RedisClient::Cluster::OptimisticLocking.new(@router).watch(watch) do |c, slot|
|
108
|
+
::RedisClient::Cluster::OptimisticLocking.new(@router).watch(watch) do |c, slot, asking|
|
102
109
|
transaction = ::RedisClient::Cluster::Transaction.new(
|
103
|
-
@router, @command_builder, node: c, slot: slot
|
110
|
+
@router, @command_builder, node: c, slot: slot, asking: asking
|
104
111
|
)
|
105
112
|
yield transaction
|
106
113
|
transaction.execute
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis-cluster-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Taishi Kasuga
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-04-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis-client
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0.
|
19
|
+
version: '0.22'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0.
|
26
|
+
version: '0.22'
|
27
27
|
description:
|
28
28
|
email:
|
29
29
|
- proxy0721@gmail.com
|