redis-cluster-client 0.11.5 → 0.12.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 +4 -4
- data/lib/redis_client/cluster/command.rb +2 -2
- data/lib/redis_client/cluster/errors.rb +27 -18
- data/lib/redis_client/cluster/node.rb +4 -4
- data/lib/redis_client/cluster/optimistic_locking.rb +15 -6
- data/lib/redis_client/cluster/pipeline.rb +4 -4
- data/lib/redis_client/cluster/router.rb +31 -13
- data/lib/redis_client/cluster/transaction.rb +5 -4
- data/lib/redis_client/cluster_config.rb +16 -2
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2465b865cd79c78cca3a1e7e3950989cce94f6675b0497763e03e261d3fa9c6f
|
4
|
+
data.tar.gz: df607279a58171aca75835b767f3bf5965256db0fcb7b540bfcb3a8f302eb638
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bfb9031660b642bb14768dc8edc66fb15073be9ca23ddc061d279a351c497a83ff8a3aad21903e2d0a37aa6824da35761188ba4dc1fe8ae0182b54584edaa5e5
|
7
|
+
data.tar.gz: 9fab63d0595bfb7dac85a07c255a41573ab8d44598605919fd4182e99cf3da585bf931abe63da9e2f470815c6ef0d34ab24a5b7f2de346ba29870fb894c556ee
|
@@ -25,7 +25,7 @@ class RedisClient
|
|
25
25
|
)
|
26
26
|
|
27
27
|
class << self
|
28
|
-
def load(nodes, slow_command_timeout: -1)
|
28
|
+
def load(nodes, slow_command_timeout: -1) # rubocop:disable Metrics/AbcSize
|
29
29
|
cmd = errors = nil
|
30
30
|
|
31
31
|
nodes&.each do |node|
|
@@ -43,7 +43,7 @@ class RedisClient
|
|
43
43
|
|
44
44
|
return cmd unless cmd.nil?
|
45
45
|
|
46
|
-
raise ::RedisClient::Cluster::InitialSetupError
|
46
|
+
raise ::RedisClient::Cluster::InitialSetupError.from_errors(errors)
|
47
47
|
end
|
48
48
|
|
49
49
|
private
|
@@ -4,51 +4,60 @@ require 'redis_client'
|
|
4
4
|
|
5
5
|
class RedisClient
|
6
6
|
class Cluster
|
7
|
+
class Error < ::RedisClient::Error
|
8
|
+
def with_config(config)
|
9
|
+
@config = config
|
10
|
+
self
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
7
14
|
ERR_ARG_NORMALIZATION = ->(arg) { Array[arg].flatten.reject { |e| e.nil? || (e.respond_to?(:empty?) && e.empty?) } }
|
8
15
|
|
9
16
|
private_constant :ERR_ARG_NORMALIZATION
|
10
17
|
|
11
|
-
class InitialSetupError <
|
12
|
-
def
|
18
|
+
class InitialSetupError < Error
|
19
|
+
def self.from_errors(errors)
|
13
20
|
msg = ERR_ARG_NORMALIZATION.call(errors).map(&:message).uniq.join(',')
|
14
|
-
|
21
|
+
new("Redis client could not fetch cluster information: #{msg}")
|
15
22
|
end
|
16
23
|
end
|
17
24
|
|
18
|
-
class OrchestrationCommandNotSupported <
|
19
|
-
def
|
25
|
+
class OrchestrationCommandNotSupported < Error
|
26
|
+
def self.from_command(command)
|
20
27
|
str = ERR_ARG_NORMALIZATION.call(command).map(&:to_s).join(' ').upcase
|
21
28
|
msg = "#{str} command should be used with care " \
|
22
29
|
'only by applications orchestrating Redis Cluster, like redis-cli, ' \
|
23
30
|
'and the command if used out of the right context can leave the cluster ' \
|
24
31
|
'in a wrong state or cause data loss.'
|
25
|
-
|
32
|
+
new(msg)
|
26
33
|
end
|
27
34
|
end
|
28
35
|
|
29
|
-
class ErrorCollection <
|
36
|
+
class ErrorCollection < Error
|
30
37
|
attr_reader :errors
|
31
38
|
|
32
|
-
def
|
33
|
-
@errors = {}
|
39
|
+
def self.with_errors(errors)
|
34
40
|
if !errors.is_a?(Hash) || errors.empty?
|
35
|
-
|
36
|
-
|
41
|
+
new(errors.to_s).with_errors({})
|
42
|
+
else
|
43
|
+
messages = errors.map { |node_key, error| "#{node_key}: (#{error.class}) #{error.message}" }
|
44
|
+
new(messages.join(', ')).with_errors(errors)
|
37
45
|
end
|
46
|
+
end
|
38
47
|
|
39
|
-
|
40
|
-
|
41
|
-
|
48
|
+
def with_errors(errors)
|
49
|
+
@errors = errors if @errors.nil?
|
50
|
+
self
|
42
51
|
end
|
43
52
|
end
|
44
53
|
|
45
|
-
class AmbiguousNodeError <
|
46
|
-
def
|
47
|
-
|
54
|
+
class AmbiguousNodeError < Error
|
55
|
+
def self.from_command(command)
|
56
|
+
new("Cluster client doesn't know which node the #{command} command should be sent to.")
|
48
57
|
end
|
49
58
|
end
|
50
59
|
|
51
|
-
class NodeMightBeDown <
|
60
|
+
class NodeMightBeDown < Error
|
52
61
|
def initialize(_ = '')
|
53
62
|
super(
|
54
63
|
'The client is trying to fetch the latest cluster state ' \
|
@@ -28,7 +28,7 @@ class RedisClient
|
|
28
28
|
private_constant :USE_CHAR_ARRAY_SLOT, :SLOT_SIZE, :MIN_SLOT, :MAX_SLOT,
|
29
29
|
:DEAD_FLAGS, :ROLE_FLAGS, :EMPTY_ARRAY, :EMPTY_HASH
|
30
30
|
|
31
|
-
ReloadNeeded = Class.new(::RedisClient::Error)
|
31
|
+
ReloadNeeded = Class.new(::RedisClient::Cluster::Error)
|
32
32
|
|
33
33
|
Info = Struct.new(
|
34
34
|
'RedisClusterNode',
|
@@ -148,7 +148,7 @@ class RedisClient
|
|
148
148
|
|
149
149
|
raise ReloadNeeded if errors.values.any?(::RedisClient::ConnectionError)
|
150
150
|
|
151
|
-
raise ::RedisClient::Cluster::ErrorCollection
|
151
|
+
raise ::RedisClient::Cluster::ErrorCollection.with_errors(errors)
|
152
152
|
end
|
153
153
|
|
154
154
|
def clients_for_scanning(seed: nil)
|
@@ -267,7 +267,7 @@ class RedisClient
|
|
267
267
|
result_values, errors = call_multiple_nodes(clients, method, command, args, &block)
|
268
268
|
return result_values if errors.nil? || errors.empty?
|
269
269
|
|
270
|
-
raise ::RedisClient::Cluster::ErrorCollection
|
270
|
+
raise ::RedisClient::Cluster::ErrorCollection.with_errors(errors)
|
271
271
|
end
|
272
272
|
|
273
273
|
def try_map(clients, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
@@ -334,7 +334,7 @@ class RedisClient
|
|
334
334
|
|
335
335
|
work_group.close
|
336
336
|
|
337
|
-
raise ::RedisClient::Cluster::InitialSetupError
|
337
|
+
raise ::RedisClient::Cluster::InitialSetupError.from_errors(errors) if node_info_list.nil?
|
338
338
|
|
339
339
|
grouped = node_info_list.compact.group_by do |info_list|
|
340
340
|
info_list.sort_by!(&:id)
|
@@ -15,10 +15,7 @@ class RedisClient
|
|
15
15
|
slot = find_slot(keys)
|
16
16
|
raise ::RedisClient::Cluster::Transaction::ConsistencyError, "unsafe watch: #{keys.join(' ')}" if slot.nil?
|
17
17
|
|
18
|
-
|
19
|
-
# redirections freely initially (i.e. for the first WATCH call)
|
20
|
-
node = @router.find_primary_node_by_slot(slot)
|
21
|
-
handle_redirection(node, retry_count: 1) do |nd|
|
18
|
+
handle_redirection(slot, retry_count: 1) do |nd|
|
22
19
|
nd.with do |c|
|
23
20
|
c.ensure_connected_cluster_scoped(retryable: false) do
|
24
21
|
c.call('ASKING') if @asking
|
@@ -45,10 +42,22 @@ class RedisClient
|
|
45
42
|
|
46
43
|
private
|
47
44
|
|
48
|
-
def handle_redirection(
|
49
|
-
|
45
|
+
def handle_redirection(slot, retry_count: 1, &blk)
|
46
|
+
# We have not yet selected a node for this transaction, initially, which means we can handle
|
47
|
+
# redirections freely initially (i.e. for the first WATCH call)
|
48
|
+
node = @router.find_primary_node_by_slot(slot)
|
49
|
+
times_block_executed = 0
|
50
|
+
@router.handle_redirection(node, nil, retry_count: retry_count) do |nd|
|
51
|
+
times_block_executed += 1
|
50
52
|
handle_asking_once(nd, &blk)
|
51
53
|
end
|
54
|
+
rescue ::RedisClient::ConnectionError
|
55
|
+
# Deduct the number of retries that happened _inside_ router#handle_redirection from our remaining
|
56
|
+
# _external_ retries. Always deduct at least one in case handle_redirection raises without trying the block.
|
57
|
+
retry_count -= [times_block_executed, 1].min
|
58
|
+
raise if retry_count < 0
|
59
|
+
|
60
|
+
retry
|
52
61
|
end
|
53
62
|
|
54
63
|
def handle_asking_once(node)
|
@@ -108,13 +108,13 @@ class RedisClient
|
|
108
108
|
end
|
109
109
|
end
|
110
110
|
|
111
|
-
ReplySizeError = Class.new(::RedisClient::Error)
|
111
|
+
ReplySizeError = Class.new(::RedisClient::Cluster::Error)
|
112
112
|
|
113
|
-
class StaleClusterState < ::RedisClient::Error
|
113
|
+
class StaleClusterState < ::RedisClient::Cluster::Error
|
114
114
|
attr_accessor :replies, :first_exception
|
115
115
|
end
|
116
116
|
|
117
|
-
class RedirectionNeeded < ::RedisClient::Error
|
117
|
+
class RedirectionNeeded < ::RedisClient::Cluster::Error
|
118
118
|
attr_accessor :replies, :indices, :first_exception
|
119
119
|
end
|
120
120
|
|
@@ -204,7 +204,7 @@ class RedisClient
|
|
204
204
|
|
205
205
|
work_group.close
|
206
206
|
@router.renew_cluster_state if cluster_state_errors
|
207
|
-
raise ::RedisClient::Cluster::ErrorCollection
|
207
|
+
raise ::RedisClient::Cluster::ErrorCollection.with_errors(errors).with_config(@router.config) unless errors.nil?
|
208
208
|
|
209
209
|
required_redirections&.each do |node_key, v|
|
210
210
|
raise v.first_exception if v.first_exception
|
@@ -21,10 +21,10 @@ class RedisClient
|
|
21
21
|
|
22
22
|
private_constant :ZERO_CURSOR_FOR_SCAN, :TSF
|
23
23
|
|
24
|
+
attr_reader :config
|
25
|
+
|
24
26
|
def initialize(config, concurrent_worker, pool: nil, **kwargs)
|
25
|
-
@config = config
|
26
|
-
@original_config = config.dup if config.connect_with_original_config
|
27
|
-
@connect_with_original_config = config.connect_with_original_config
|
27
|
+
@config = config
|
28
28
|
@concurrent_worker = concurrent_worker
|
29
29
|
@pool = pool
|
30
30
|
@client_kwargs = kwargs
|
@@ -32,6 +32,9 @@ class RedisClient
|
|
32
32
|
@node.reload!
|
33
33
|
@command = ::RedisClient::Cluster::Command.load(@node.replica_clients.shuffle, slow_command_timeout: config.slow_command_timeout)
|
34
34
|
@command_builder = @config.command_builder
|
35
|
+
rescue ::RedisClient::Cluster::InitialSetupError => e
|
36
|
+
e.with_config(config)
|
37
|
+
raise
|
35
38
|
end
|
36
39
|
|
37
40
|
def send_command(method, command, *args, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
@@ -58,9 +61,9 @@ class RedisClient
|
|
58
61
|
when 'flushall', 'flushdb'
|
59
62
|
@node.call_primaries(method, command, args).first.then(&TSF.call(block))
|
60
63
|
when 'readonly', 'readwrite', 'shutdown'
|
61
|
-
raise ::RedisClient::Cluster::OrchestrationCommandNotSupported
|
64
|
+
raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(cmd).with_config(@config)
|
62
65
|
when 'discard', 'exec', 'multi', 'unwatch'
|
63
|
-
raise ::RedisClient::Cluster::AmbiguousNodeError
|
66
|
+
raise ::RedisClient::Cluster::AmbiguousNodeError.from_command(cmd).with_config(@config)
|
64
67
|
else
|
65
68
|
node = assign_node(command)
|
66
69
|
try_send(node, method, command, args, &block)
|
@@ -69,7 +72,7 @@ class RedisClient
|
|
69
72
|
raise
|
70
73
|
rescue ::RedisClient::Cluster::Node::ReloadNeeded
|
71
74
|
renew_cluster_state
|
72
|
-
raise ::RedisClient::Cluster::NodeMightBeDown
|
75
|
+
raise ::RedisClient::Cluster::NodeMightBeDown.new.with_config(@config)
|
73
76
|
rescue ::RedisClient::ConnectionError
|
74
77
|
renew_cluster_state
|
75
78
|
raise
|
@@ -77,6 +80,7 @@ class RedisClient
|
|
77
80
|
renew_cluster_state if e.message.start_with?('CLUSTERDOWN')
|
78
81
|
raise
|
79
82
|
rescue ::RedisClient::Cluster::ErrorCollection => e
|
83
|
+
e.with_config(@config)
|
80
84
|
raise if e.errors.any?(::RedisClient::CircuitBreaker::OpenCircuitError)
|
81
85
|
|
82
86
|
renew_cluster_state if e.errors.values.any? do |err|
|
@@ -90,7 +94,7 @@ class RedisClient
|
|
90
94
|
|
91
95
|
# @see https://redis.io/docs/reference/cluster-spec/#redirection-and-resharding Redirection and resharding
|
92
96
|
def try_send(node, method, command, args, retry_count: 3, &block)
|
93
|
-
handle_redirection(node, retry_count: retry_count) do |on_node|
|
97
|
+
handle_redirection(node, command, retry_count: retry_count) do |on_node|
|
94
98
|
if args.empty?
|
95
99
|
# prevent memory allocation for variable-length args
|
96
100
|
on_node.public_send(method, command, &block)
|
@@ -101,12 +105,12 @@ class RedisClient
|
|
101
105
|
end
|
102
106
|
|
103
107
|
def try_delegate(node, method, *args, retry_count: 3, **kwargs, &block)
|
104
|
-
handle_redirection(node, retry_count: retry_count) do |on_node|
|
108
|
+
handle_redirection(node, nil, retry_count: retry_count) do |on_node|
|
105
109
|
on_node.public_send(method, *args, **kwargs, &block)
|
106
110
|
end
|
107
111
|
end
|
108
112
|
|
109
|
-
def handle_redirection(node, retry_count:) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
113
|
+
def handle_redirection(node, command, retry_count:) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
110
114
|
yield node
|
111
115
|
rescue ::RedisClient::CircuitBreaker::OpenCircuitError
|
112
116
|
raise
|
@@ -134,6 +138,17 @@ class RedisClient
|
|
134
138
|
|
135
139
|
retry_count -= 1
|
136
140
|
renew_cluster_state
|
141
|
+
|
142
|
+
if retry_count >= 0
|
143
|
+
# Find the node to use for this command - if this fails for some reason, though, re-use
|
144
|
+
# the old node.
|
145
|
+
begin
|
146
|
+
node = find_node(find_node_key(command)) if command
|
147
|
+
rescue StandardError # rubocop:disable Lint/SuppressedException
|
148
|
+
end
|
149
|
+
retry
|
150
|
+
end
|
151
|
+
|
137
152
|
retry if retry_count >= 0
|
138
153
|
raise
|
139
154
|
end
|
@@ -178,7 +193,7 @@ class RedisClient
|
|
178
193
|
node_key = primary ? @node.find_node_key_of_primary(slot) : @node.find_node_key_of_replica(slot)
|
179
194
|
if node_key.nil?
|
180
195
|
renew_cluster_state
|
181
|
-
raise ::RedisClient::Cluster::NodeMightBeDown
|
196
|
+
raise ::RedisClient::Cluster::NodeMightBeDown.new.with_config(@config)
|
182
197
|
end
|
183
198
|
node_key
|
184
199
|
else
|
@@ -292,7 +307,7 @@ class RedisClient
|
|
292
307
|
case subcommand = ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_subcommand(command)
|
293
308
|
when 'addslots', 'delslots', 'failover', 'forget', 'meet', 'replicate',
|
294
309
|
'reset', 'set-config-epoch', 'setslot'
|
295
|
-
raise ::RedisClient::Cluster::OrchestrationCommandNotSupported
|
310
|
+
raise ::RedisClient::Cluster::OrchestrationCommandNotSupported.from_command(['cluster', subcommand]).with_config(@config)
|
296
311
|
when 'saveconfig' then @node.call_all(method, command, args).first.then(&TSF.call(block))
|
297
312
|
when 'getkeysinslot'
|
298
313
|
raise ArgumentError, command.join(' ') if command.size != 4
|
@@ -336,7 +351,10 @@ class RedisClient
|
|
336
351
|
end
|
337
352
|
|
338
353
|
def send_watch_command(command)
|
339
|
-
|
354
|
+
unless block_given?
|
355
|
+
msg = 'A block required. And you need to use the block argument as a client for the transaction.'
|
356
|
+
raise ::RedisClient::Cluster::Transaction::ConsistencyError.new(msg).with_config(@config)
|
357
|
+
end
|
340
358
|
|
341
359
|
::RedisClient::Cluster::OptimisticLocking.new(self).watch(command[1..]) do |c, slot, asking|
|
342
360
|
transaction = ::RedisClient::Cluster::Transaction.new(
|
@@ -390,7 +408,7 @@ class RedisClient
|
|
390
408
|
def handle_node_reload_error(retry_count: 1)
|
391
409
|
yield
|
392
410
|
rescue ::RedisClient::Cluster::Node::ReloadNeeded
|
393
|
-
raise ::RedisClient::Cluster::NodeMightBeDown if retry_count <= 0
|
411
|
+
raise ::RedisClient::Cluster::NodeMightBeDown.new.with_config(@config) if retry_count <= 0
|
394
412
|
|
395
413
|
retry_count -= 1
|
396
414
|
renew_cluster_state
|
@@ -1,12 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'redis_client'
|
4
|
+
require 'redis_client/cluster/errors'
|
4
5
|
require 'redis_client/cluster/pipeline'
|
5
6
|
|
6
7
|
class RedisClient
|
7
8
|
class Cluster
|
8
9
|
class Transaction
|
9
|
-
ConsistencyError = Class.new(::RedisClient::Error)
|
10
|
+
ConsistencyError = Class.new(::RedisClient::Cluster::Error)
|
10
11
|
|
11
12
|
MAX_REDIRECTION = 2
|
12
13
|
EMPTY_ARRAY = [].freeze
|
@@ -67,7 +68,7 @@ class RedisClient
|
|
67
68
|
@pending_commands.each(&:call)
|
68
69
|
|
69
70
|
return EMPTY_ARRAY if @pipeline._empty?
|
70
|
-
raise ConsistencyError
|
71
|
+
raise ConsistencyError.new("couldn't determine the node: #{@pipeline._commands}").with_config(@router.config) if @node.nil?
|
71
72
|
|
72
73
|
commit
|
73
74
|
end
|
@@ -163,7 +164,7 @@ class RedisClient
|
|
163
164
|
|
164
165
|
def handle_command_error!(err, redirect:) # rubocop:disable Metrics/AbcSize
|
165
166
|
if err.message.start_with?('CROSSSLOT')
|
166
|
-
raise ConsistencyError
|
167
|
+
raise ConsistencyError.new("#{err.message}: #{err.command}").with_config(@router.config)
|
167
168
|
elsif err.message.start_with?('MOVED')
|
168
169
|
node = @router.assign_redirection_node(err.message)
|
169
170
|
send_transaction(node, redirect: redirect - 1)
|
@@ -183,7 +184,7 @@ class RedisClient
|
|
183
184
|
return if slots.size == 1 && @watching_slot.nil?
|
184
185
|
return if slots.size == 1 && @watching_slot == slots.first
|
185
186
|
|
186
|
-
raise(
|
187
|
+
raise ConsistencyError.new("the transaction should be executed to a slot in a node: #{commands}").with_config(@router.config)
|
187
188
|
end
|
188
189
|
|
189
190
|
def try_asking(node)
|
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'uri'
|
4
4
|
require 'redis_client'
|
5
5
|
require 'redis_client/cluster'
|
6
|
+
require 'redis_client/cluster/errors'
|
6
7
|
require 'redis_client/cluster/node_key'
|
7
8
|
require 'redis_client/command_builder'
|
8
9
|
|
@@ -27,10 +28,10 @@ class RedisClient
|
|
27
28
|
:VALID_SCHEMES, :VALID_NODES_KEYS, :MERGE_CONFIG_KEYS, :IGNORE_GENERIC_CONFIG_KEYS,
|
28
29
|
:MAX_WORKERS, :SLOW_COMMAND_TIMEOUT, :MAX_STARTUP_SAMPLE
|
29
30
|
|
30
|
-
InvalidClientConfigError = Class.new(::RedisClient::Error)
|
31
|
+
InvalidClientConfigError = Class.new(::RedisClient::Cluster::Error)
|
31
32
|
|
32
33
|
attr_reader :command_builder, :client_config, :replica_affinity, :slow_command_timeout,
|
33
|
-
:connect_with_original_config, :startup_nodes, :max_startup_sample
|
34
|
+
:connect_with_original_config, :startup_nodes, :max_startup_sample, :id
|
34
35
|
|
35
36
|
def initialize( # rubocop:disable Metrics/ParameterLists
|
36
37
|
nodes: DEFAULT_NODES,
|
@@ -59,6 +60,7 @@ class RedisClient
|
|
59
60
|
@client_implementation = client_implementation
|
60
61
|
@slow_command_timeout = slow_command_timeout
|
61
62
|
@max_startup_sample = max_startup_sample
|
63
|
+
@id = client_config[:id]
|
62
64
|
end
|
63
65
|
|
64
66
|
def inspect
|
@@ -92,6 +94,18 @@ class RedisClient
|
|
92
94
|
augment_client_config(config)
|
93
95
|
end
|
94
96
|
|
97
|
+
def resolved?
|
98
|
+
true
|
99
|
+
end
|
100
|
+
|
101
|
+
def sentinel?
|
102
|
+
false
|
103
|
+
end
|
104
|
+
|
105
|
+
def server_url
|
106
|
+
nil
|
107
|
+
end
|
108
|
+
|
95
109
|
private
|
96
110
|
|
97
111
|
def merge_concurrency_option(option)
|
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.12.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-11-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis-client
|
@@ -77,7 +77,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
77
77
|
- !ruby/object:Gem::Version
|
78
78
|
version: '0'
|
79
79
|
requirements: []
|
80
|
-
rubygems_version: 3.5.
|
80
|
+
rubygems_version: 3.5.22
|
81
81
|
signing_key:
|
82
82
|
specification_version: 4
|
83
83
|
summary: A Redis cluster client for Ruby
|