redis-cluster-client 0.3.5 → 0.3.6

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: a697c2bd33f615cb435ee48c11c301bba56cad4eac139f30db70a183d888d891
4
- data.tar.gz: 52200e9400bd7bd7fde2019c52b6cf1e26c6c019cbf1ed12a25c2167cd7ac129
3
+ metadata.gz: 24e153973525e8cd3905e92e851194a5c1f4349cecd71ca4a71db34fa678d1d8
4
+ data.tar.gz: 963ac53058e861d50c55160f55f9b064e853c49b29811075ece80ce34fa7b0f8
5
5
  SHA512:
6
- metadata.gz: 05dc86fd1aa3cbf3cd3f0fa43d9d1c8463fcbe9e0ce1bcfe094f6095fd92d878a4bb21e6d6fa62203e91f3dc642e5869552f0b83d579cfa158b791501e1cb600
7
- data.tar.gz: 8f80c5072d4d1bdbeb3e2cb4c9f92493537a6d9002edd71426598844ba28a497171c055135bb19a1d89a325c194026195100f9a0aea89c0beac3d8e9424f7f58
6
+ metadata.gz: 841bdd5aa580dcd7c3169fe88f36b8ed888ffffd3cf50b9f75449365c430afb41d79d88f2265183119f0ed012ace418868679cad468bcf39b25aed30b6207da1
7
+ data.tar.gz: 5f36751289ba73d5471cfe44d9e3d10cb70fd517249ce48ad7ea4254de0595c869fb900fecd3b0abd690fe977a732650ebf8da80a5d95ed1464a3345b79243fa
@@ -10,7 +10,7 @@ class RedisClient
10
10
  EMPTY_STRING = ''
11
11
 
12
12
  class << self
13
- def load(nodes) # rubocop:disable Metrics/MethodLength
13
+ def load(nodes)
14
14
  errors = []
15
15
  cmd = nil
16
16
  nodes&.each do |node|
@@ -32,10 +32,7 @@ class RedisClient
32
32
 
33
33
  def parse_command_details(rows)
34
34
  rows&.reject { |row| row[0].nil? }.to_h do |row|
35
- [
36
- ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_name(row[0]),
37
- { arity: row[1], flags: row[2], first: row[3], last: row[4], step: row[5] }
38
- ]
35
+ [row[0].downcase, { arity: row[1], flags: row[2], first: row[3], last: row[4], step: row[5] }]
39
36
  end
40
37
  end
41
38
  end
@@ -85,7 +82,7 @@ class RedisClient
85
82
  @details.fetch(name).fetch(key)
86
83
  end
87
84
 
88
- def determine_first_key_position(command) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
85
+ def determine_first_key_position(command) # rubocop:disable Metrics/CyclomaticComplexity
89
86
  case ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_command(command)
90
87
  when 'eval', 'evalsha', 'zinterstore', 'zunionstore' then 3
91
88
  when 'object' then 2
@@ -34,14 +34,13 @@ class RedisClient
34
34
 
35
35
  def any_replica_node_key(seed: nil)
36
36
  random = seed.nil? ? Random : Random.new(seed)
37
- @existed_replicas.sample(random: random).first
37
+ @existed_replicas.sample(random: random)&.first
38
38
  end
39
39
 
40
40
  private
41
41
 
42
- def measure_latencies(clients) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
43
- latencies = nil
44
- clients.each_slice(::RedisClient::Cluster::Node::MAX_THREADS) do |chuncked_clients|
42
+ def measure_latencies(clients) # rubocop:disable Metrics/AbcSize
43
+ clients.each_slice(::RedisClient::Cluster::Node::MAX_THREADS).each_with_object({}) do |chuncked_clients, acc|
45
44
  threads = chuncked_clients.map do |k, v|
46
45
  Thread.new(k, v) do |node_key, client|
47
46
  Thread.pass
@@ -63,12 +62,9 @@ class RedisClient
63
62
 
64
63
  threads.each do |t|
65
64
  t.join
66
- latencies ||= {}
67
- latencies[t.thread_variable_get(:node_key)] = t.thread_variable_get(:latency)
65
+ acc[t.thread_variable_get(:node_key)] = t.thread_variable_get(:latency)
68
66
  end
69
67
  end
70
-
71
- latencies
72
68
  end
73
69
 
74
70
  def select_replica_clients(replications, clients)
@@ -37,7 +37,7 @@ class RedisClient
37
37
  end
38
38
 
39
39
  class << self
40
- def load_info(options, **kwargs) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
40
+ def load_info(options, **kwargs) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
41
41
  startup_size = options.size > MAX_STARTUP_SAMPLE ? MAX_STARTUP_SAMPLE : options.size
42
42
  node_info_list = errors = nil
43
43
  startup_options = options.to_a.sample(MAX_STARTUP_SAMPLE).to_h
@@ -83,7 +83,7 @@ class RedisClient
83
83
 
84
84
  # @see https://redis.io/commands/cluster-nodes/
85
85
  # @see https://github.com/redis/redis/blob/78960ad57b8a5e6af743d789ed8fd767e37d42b8/src/cluster.c#L4660-L4683
86
- def parse_node_info(info) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
86
+ def parse_node_info(info) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
87
87
  rows = info.split("\n").map(&:split)
88
88
  rows.each { |arr| arr[2] = arr[2].split(',') }
89
89
  rows.select! { |arr| arr[7] == 'connected' && (arr[2] & %w[fail? fail handshake noaddr noflags]).empty? }
@@ -242,7 +242,7 @@ class RedisClient
242
242
  raise ::RedisClient::Cluster::ErrorCollection, errors
243
243
  end
244
244
 
245
- def try_map(clients) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
245
+ def try_map(clients) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
246
246
  results = errors = nil
247
247
  clients.each_slice(MAX_THREADS) do |chuncked_clients|
248
248
  threads = chuncked_clients.map do |k, v|
@@ -28,6 +28,7 @@ class RedisClient
28
28
 
29
29
  def clear
30
30
  @mutex.synchronize { @cache.clear }
31
+ true
31
32
  end
32
33
 
33
34
  private
@@ -2,84 +2,173 @@
2
2
 
3
3
  require 'redis_client'
4
4
  require 'redis_client/cluster/errors'
5
+ require 'redis_client/connection_mixin'
6
+ require 'redis_client/middlewares'
7
+ require 'redis_client/pooled'
5
8
 
6
9
  class RedisClient
7
10
  class Cluster
8
11
  class Pipeline
12
+ class Extended < ::RedisClient::Pipeline
13
+ attr_reader :outer_indices
14
+
15
+ def initialize(command_builder)
16
+ super
17
+ @outer_indices = nil
18
+ end
19
+
20
+ def add_outer_index(index)
21
+ @outer_indices ||= []
22
+ @outer_indices << index
23
+ end
24
+
25
+ def get_inner_index(outer_index)
26
+ @outer_indices&.find_index(outer_index)
27
+ end
28
+
29
+ def get_callee_method(inner_index)
30
+ if @timeouts.is_a?(Array) && !@timeouts[inner_index].nil?
31
+ :blocking_call_v
32
+ elsif _retryable?
33
+ :call_once_v
34
+ else
35
+ :call_v
36
+ end
37
+ end
38
+
39
+ def get_command(inner_index)
40
+ @commands.is_a?(Array) ? @commands[inner_index] : nil
41
+ end
42
+
43
+ def get_timeout(inner_index)
44
+ @timeouts.is_a?(Array) ? @timeouts[inner_index] : nil
45
+ end
46
+
47
+ def get_block(inner_index)
48
+ @blocks.is_a?(Array) ? @blocks[inner_index] : nil
49
+ end
50
+ end
51
+
52
+ ::RedisClient::ConnectionMixin.module_eval do
53
+ def call_pipelined_aware_of_redirection(commands, timeouts) # rubocop:disable Metrics/AbcSize
54
+ size = commands.size
55
+ results = Array.new(commands.size)
56
+ @pending_reads += size
57
+ write_multi(commands)
58
+
59
+ redirection_indices = nil
60
+ size.times do |index|
61
+ timeout = timeouts && timeouts[index]
62
+ result = read(timeout)
63
+ @pending_reads -= 1
64
+ if result.is_a?(CommandError)
65
+ result._set_command(commands[index])
66
+ if result.message.start_with?('MOVED', 'ASK')
67
+ redirection_indices ||= []
68
+ redirection_indices << index
69
+ end
70
+ end
71
+
72
+ results[index] = result
73
+ end
74
+
75
+ return results if redirection_indices.nil?
76
+
77
+ err = ::RedisClient::Cluster::Pipeline::RedirectionNeeded.new
78
+ err.replies = results
79
+ err.indices = redirection_indices
80
+ raise err
81
+ end
82
+ end
83
+
9
84
  ReplySizeError = Class.new(::RedisClient::Error)
85
+
86
+ class RedirectionNeeded < ::RedisClient::Error
87
+ attr_accessor :replies, :indices
88
+ end
89
+
10
90
  MAX_THREADS = Integer(ENV.fetch('REDIS_CLIENT_MAX_THREADS', 5))
11
91
 
12
92
  def initialize(router, command_builder, seed: Random.new_seed)
13
93
  @router = router
14
94
  @command_builder = command_builder
15
- @grouped = {}
16
- @size = 0
17
95
  @seed = seed
96
+ @pipelines = nil
97
+ @size = 0
18
98
  end
19
99
 
20
100
  def call(*args, **kwargs, &block)
21
101
  command = @command_builder.generate(args, kwargs)
22
102
  node_key = @router.find_node_key(command, seed: @seed)
23
- add_row(node_key, [@size, :call_v, command, block])
103
+ append_pipeline(node_key).call_v(command, &block)
24
104
  end
25
105
 
26
106
  def call_v(args, &block)
27
107
  command = @command_builder.generate(args)
28
108
  node_key = @router.find_node_key(command, seed: @seed)
29
- add_row(node_key, [@size, :call_v, command, block])
109
+ append_pipeline(node_key).call_v(command, &block)
30
110
  end
31
111
 
32
112
  def call_once(*args, **kwargs, &block)
33
113
  command = @command_builder.generate(args, kwargs)
34
114
  node_key = @router.find_node_key(command, seed: @seed)
35
- add_row(node_key, [@size, :call_once_v, command, block])
115
+ append_pipeline(node_key).call_once_v(command, &block)
36
116
  end
37
117
 
38
118
  def call_once_v(args, &block)
39
119
  command = @command_builder.generate(args)
40
120
  node_key = @router.find_node_key(command, seed: @seed)
41
- add_row(node_key, [@size, :call_once_v, command, block])
121
+ append_pipeline(node_key).call_once_v(command, &block)
42
122
  end
43
123
 
44
124
  def blocking_call(timeout, *args, **kwargs, &block)
45
125
  command = @command_builder.generate(args, kwargs)
46
126
  node_key = @router.find_node_key(command, seed: @seed)
47
- add_row(node_key, [@size, :blocking_call_v, timeout, command, block])
127
+ append_pipeline(node_key).blocking_call_v(timeout, command, &block)
48
128
  end
49
129
 
50
130
  def blocking_call_v(timeout, args, &block)
51
131
  command = @command_builder.generate(args)
52
132
  node_key = @router.find_node_key(command, seed: @seed)
53
- add_row(node_key, [@size, :blocking_call_v, timeout, command, block])
133
+ append_pipeline(node_key).blocking_call_v(timeout, command, &block)
54
134
  end
55
135
 
56
136
  def empty?
57
137
  @size.zero?
58
138
  end
59
139
 
60
- # TODO: https://github.com/redis-rb/redis-cluster-client/issues/37 handle redirections
61
- def execute # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
140
+ def execute # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
62
141
  all_replies = errors = nil
63
- @grouped.each_slice(MAX_THREADS) do |chuncked_grouped|
64
- threads = chuncked_grouped.map do |k, v|
65
- Thread.new(@router, k, v) do |router, node_key, rows|
142
+ @pipelines&.each_slice(MAX_THREADS) do |chuncked_pipelines|
143
+ threads = chuncked_pipelines.map do |node_key, pipeline|
144
+ Thread.new(node_key, pipeline) do |nk, pl|
66
145
  Thread.pass
67
- replies = do_pipelining(router.find_node(node_key), rows)
68
- raise ReplySizeError, "commands: #{rows.size}, replies: #{replies.size}" if rows.size != replies.size
146
+ Thread.current.thread_variable_set(:node_key, nk)
147
+ replies = do_pipelining(@router.find_node(nk), pl)
148
+ raise ReplySizeError, "commands: #{pl._size}, replies: #{replies.size}" if pl._size != replies.size
69
149
 
70
- Thread.current.thread_variable_set(:rows, rows)
71
150
  Thread.current.thread_variable_set(:replies, replies)
151
+ rescue ::RedisClient::Cluster::Pipeline::RedirectionNeeded => e
152
+ Thread.current.thread_variable_set(:redirection_needed, e)
72
153
  rescue StandardError => e
73
- Thread.current.thread_variable_set(:node_key, node_key)
74
154
  Thread.current.thread_variable_set(:error, e)
75
155
  end
76
156
  end
77
157
 
78
158
  threads.each do |t|
79
159
  t.join
160
+
80
161
  if t.thread_variable?(:replies)
81
162
  all_replies ||= Array.new(@size)
82
- t.thread_variable_get(:rows).each_with_index { |r, i| all_replies[r.first] = t.thread_variable_get(:replies)[i] }
163
+ @pipelines[t.thread_variable_get(:node_key)]
164
+ .outer_indices
165
+ .each_with_index { |outer, inner| all_replies[outer] = t.thread_variable_get(:replies)[inner] }
166
+ elsif t.thread_variable?(:redirection_needed)
167
+ all_replies ||= Array.new(@size)
168
+ pipeline = @pipelines[t.thread_variable_get(:node_key)]
169
+ err = t.thread_variable_get(:redirection_needed)
170
+ err.indices.each { |i| err.replies[i] = handle_redirection(err.replies[i], pipeline, i) }
171
+ pipeline.outer_indices.each_with_index { |outer, inner| all_replies[outer] = err.replies[inner] }
83
172
  elsif t.thread_variable?(:error)
84
173
  errors ||= {}
85
174
  errors[t.thread_variable_get(:node_key)] = t.thread_variable_get(:error)
@@ -87,28 +176,78 @@ class RedisClient
87
176
  end
88
177
  end
89
178
 
90
- return all_replies if errors.nil?
179
+ raise ::RedisClient::Cluster::ErrorCollection, errors unless errors.nil?
91
180
 
92
- raise ::RedisClient::Cluster::ErrorCollection, errors
181
+ all_replies
93
182
  end
94
183
 
95
184
  private
96
185
 
97
- def add_row(node_key, row)
98
- @grouped[node_key] = [] unless @grouped.key?(node_key)
99
- @grouped[node_key] << row
186
+ def append_pipeline(node_key)
187
+ @pipelines ||= {}
188
+ @pipelines[node_key] ||= ::RedisClient::Cluster::Pipeline::Extended.new(@command_builder)
189
+ @pipelines[node_key].add_outer_index(@size)
100
190
  @size += 1
191
+ @pipelines[node_key]
101
192
  end
102
193
 
103
- def do_pipelining(node, rows)
104
- node.pipelined do |pipeline|
105
- rows.each do |row|
106
- case row.size
107
- when 4 then pipeline.send(row[1], row[2], &row[3])
108
- when 5 then pipeline.send(row[1], row[2], row[3], &row[4])
109
- end
194
+ def do_pipelining(client, pipeline)
195
+ case client
196
+ when ::RedisClient then send_pipeline(client, pipeline)
197
+ when ::RedisClient::Pooled then client.with { |cli| send_pipeline(cli, pipeline) }
198
+ else raise NotImplementedError, "#{client.class.name}#pipelined for cluster client"
199
+ end
200
+ end
201
+
202
+ def send_pipeline(client, pipeline)
203
+ results = client.send(:ensure_connected, retryable: pipeline._retryable?) do |connection|
204
+ commands = pipeline._commands
205
+ ::RedisClient::Middlewares.call_pipelined(commands, client.config) do
206
+ connection.call_pipelined_aware_of_redirection(commands, pipeline._timeouts)
110
207
  end
111
208
  end
209
+
210
+ pipeline._coerce!(results)
211
+ end
212
+
213
+ def handle_redirection(err, pipeline, inner_index)
214
+ return err unless err.is_a?(::RedisClient::CommandError)
215
+
216
+ if err.message.start_with?('MOVED')
217
+ node = @router.assign_redirection_node(err.message)
218
+ try_redirection(node, pipeline, inner_index)
219
+ elsif err.message.start_with?('ASK')
220
+ node = @router.assign_asking_node(err.message)
221
+ try_asking(node) ? try_redirection(node, pipeline, inner_index) : err
222
+ else
223
+ err
224
+ end
225
+ end
226
+
227
+ def try_redirection(node, pipeline, inner_index)
228
+ redirect_command(node, pipeline, inner_index)
229
+ rescue StandardError => e
230
+ e
231
+ end
232
+
233
+ def redirect_command(node, pipeline, inner_index)
234
+ method = pipeline.get_callee_method(inner_index)
235
+ command = pipeline.get_command(inner_index)
236
+ timeout = pipeline.get_timeout(inner_index)
237
+ block = pipeline.get_block(inner_index)
238
+ args = timeout.nil? ? [] : [timeout]
239
+
240
+ if block.nil?
241
+ @router.try_send(node, method, command, args)
242
+ else
243
+ @router.try_send(node, method, command, args, &block)
244
+ end
245
+ end
246
+
247
+ def try_asking(node)
248
+ node.call('ASKING') == 'OK'
249
+ rescue StandardError
250
+ false
112
251
  end
113
252
  end
114
253
  end
@@ -25,7 +25,7 @@ class RedisClient
25
25
  @command_builder = @config.command_builder
26
26
  end
27
27
 
28
- def send_command(method, command, *args, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
28
+ def send_command(method, command, *args, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
29
29
  cmd = ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_command(command)
30
30
  case cmd
31
31
  when 'acl', 'auth', 'bgrewriteaof', 'bgsave', 'quit', 'save'
@@ -65,7 +65,7 @@ class RedisClient
65
65
 
66
66
  # @see https://redis.io/topics/cluster-spec#redirection-and-resharding
67
67
  # Redirection and resharding
68
- def try_send(node, method, command, args, retry_count: 3, &block) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
68
+ def try_send(node, method, command, args, retry_count: 3, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
69
69
  if args.empty?
70
70
  # prevent memory allocation for variable-length args
71
71
  node.send(method, command, &block)
@@ -100,7 +100,7 @@ class RedisClient
100
100
  retry
101
101
  end
102
102
 
103
- def try_delegate(node, method, *args, retry_count: 3, **kwargs, &block) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
103
+ def try_delegate(node, method, *args, retry_count: 3, **kwargs, &block) # rubocop:disable Metrics/AbcSize
104
104
  node.send(method, *args, **kwargs, &block)
105
105
  rescue ::RedisClient::CommandError => e
106
106
  raise if retry_count <= 0
@@ -129,7 +129,7 @@ class RedisClient
129
129
  retry
130
130
  end
131
131
 
132
- def scan(*command, seed: nil, **kwargs) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
132
+ def scan(*command, seed: nil, **kwargs) # rubocop:disable Metrics/AbcSize
133
133
  command = @command_builder.generate(command, kwargs)
134
134
 
135
135
  command[1] = ZERO_CURSOR_FOR_SCAN if command.size == 1
@@ -172,7 +172,7 @@ class RedisClient
172
172
  def find_node(node_key, retry_count: 3)
173
173
  @node.find_by(node_key)
174
174
  rescue ::RedisClient::Cluster::Node::ReloadNeeded
175
- raise ::RedieClient::Cluster::NodeMightBeDown if retry_count <= 0
175
+ raise ::RedisClient::Cluster::NodeMightBeDown if retry_count <= 0
176
176
 
177
177
  update_cluster_info!
178
178
  retry_count -= 1
@@ -183,6 +183,18 @@ class RedisClient
183
183
  @command.exists?(name)
184
184
  end
185
185
 
186
+ def assign_redirection_node(err_msg)
187
+ _, slot, node_key = err_msg.split
188
+ slot = slot.to_i
189
+ @node.update_slot(slot, node_key)
190
+ find_node(node_key)
191
+ end
192
+
193
+ def assign_asking_node(err_msg)
194
+ _, _, node_key = err_msg.split
195
+ find_node(node_key)
196
+ end
197
+
186
198
  private
187
199
 
188
200
  def send_wait_command(method, command, args, retry_count: 3, &block)
@@ -258,19 +270,7 @@ class RedisClient
258
270
  end
259
271
  end
260
272
 
261
- def assign_redirection_node(err_msg)
262
- _, slot, node_key = err_msg.split
263
- slot = slot.to_i
264
- @node.update_slot(slot, node_key)
265
- find_node(node_key)
266
- end
267
-
268
- def assign_asking_node(err_msg)
269
- _, _, node_key = err_msg.split
270
- find_node(node_key)
271
- end
272
-
273
- def fetch_cluster_info(config, pool: nil, **kwargs) # rubocop:disable Metrics/MethodLength
273
+ def fetch_cluster_info(config, pool: nil, **kwargs)
274
274
  node_info = ::RedisClient::Cluster::Node.load_info(config.per_node_key, **kwargs)
275
275
  node_addrs = node_info.map { |info| ::RedisClient::Cluster::NodeKey.hashify(info[:node_key]) }
276
276
  config.update_node(node_addrs)
@@ -81,7 +81,7 @@ class RedisClient
81
81
  seed = @config.use_replica? && @config.replica_affinity == :random ? nil : Random.new_seed
82
82
  pipeline = ::RedisClient::Cluster::Pipeline.new(@router, @command_builder, seed: seed)
83
83
  yield pipeline
84
- return [] if pipeline.empty? == 0
84
+ return [] if pipeline.empty?
85
85
 
86
86
  pipeline.execute
87
87
  end
@@ -114,7 +114,7 @@ class RedisClient
114
114
  end
115
115
  end
116
116
 
117
- def parse_node_url(addr) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
117
+ def parse_node_url(addr) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
118
118
  return if addr.empty?
119
119
 
120
120
  uri = URI(addr)
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.3.5
4
+ version: 0.3.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Taishi Kasuga
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-09-21 00:00:00.000000000 Z
11
+ date: 2022-09-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis-client