redis-cluster-client 0.0.11 → 0.2.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: d7c3e9a3b764d9e5fc52670d17c175ee9ccda2dcd56b4a977b0e9a1e0b71d972
4
- data.tar.gz: 191e1fe163f6f2fd111e4b2f69638aa28462d74e002f1ff0a11fc0f52cc740cd
3
+ metadata.gz: 5160c83d23cb638fc76cf079377981d6778560ba0eb476da03f58d6f4eded9af
4
+ data.tar.gz: dd0baec0d24de14e2cba10c4ea3f45af7f1bef2db30f609c9551ed2d13c0bf4a
5
5
  SHA512:
6
- metadata.gz: a42c6a871302686e33026ed570575d5b51637b707501609fdf9e574a5a976e06af388bc69bebd5bb22a58a30741c4b3e43d883d154782675446ce88c49ac2b37
7
- data.tar.gz: e2ae0a6dd3af75407cc4d795492afff75e5bc7c01fee84be2ef0761e67dda0d9f93cc103b99219bd1b5e8df9dc3ff1dc86b3406c3a23dc1d011b649c363ab163
6
+ metadata.gz: 1cf5c16776bb7ff4c53ffa66e3de3371c1130fed7a4916982e7fba9859ba9ae7505af0bc5e8e02b58f22d116ab74692a4572d835f05672457bac4ddd469e3162
7
+ data.tar.gz: f08215c00f2b12b00247604917a7f7abc228ddd0e4305c67af38f8e2087a8b3785a22bfbd09ac32f75789719a624be49663f5048f6187318477069e4ef9627a1
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'redis_cluster_client'
@@ -55,6 +55,10 @@ class RedisClient
55
55
  dig_details(command, :readonly)
56
56
  end
57
57
 
58
+ def exists?(name)
59
+ @details.key?(name.to_s.downcase)
60
+ end
61
+
58
62
  private
59
63
 
60
64
  def pick_details(details)
@@ -128,9 +128,9 @@ class RedisClient
128
128
  @clients.fetch(node_key)
129
129
  end
130
130
 
131
- def call_all(method, *args, **kwargs, &block)
131
+ def call_all(method, command, args, &block)
132
132
  results, errors = try_map do |_, client|
133
- client.send(method, *args, **kwargs, &block)
133
+ client.send(method, *args, command, &block)
134
134
  end
135
135
 
136
136
  return results.values if errors.empty?
@@ -138,11 +138,11 @@ class RedisClient
138
138
  raise ::RedisClient::Cluster::ErrorCollection, errors
139
139
  end
140
140
 
141
- def call_primaries(method, *args, **kwargs, &block)
141
+ def call_primaries(method, command, args, &block)
142
142
  results, errors = try_map do |node_key, client|
143
143
  next if replica?(node_key)
144
144
 
145
- client.send(method, *args, **kwargs, &block)
145
+ client.send(method, *args, command, &block)
146
146
  end
147
147
 
148
148
  return results.values if errors.empty?
@@ -150,14 +150,14 @@ class RedisClient
150
150
  raise ::RedisClient::Cluster::ErrorCollection, errors
151
151
  end
152
152
 
153
- def call_replicas(method, *args, **kwargs, &block)
154
- return call_primaries(method, *args, **kwargs, &block) if replica_disabled?
153
+ def call_replicas(method, command, args, &block)
154
+ return call_primaries(method, command, args, &block) if replica_disabled?
155
155
 
156
156
  replica_node_keys = @replications.values.map(&:sample)
157
157
  results, errors = try_map do |node_key, client|
158
158
  next if primary?(node_key) || !replica_node_keys.include?(node_key)
159
159
 
160
- client.send(method, *args, **kwargs, &block)
160
+ client.send(method, *args, command, &block)
161
161
  end
162
162
 
163
163
  return results.values if errors.empty?
@@ -165,9 +165,9 @@ class RedisClient
165
165
  raise ::RedisClient::Cluster::ErrorCollection, errors
166
166
  end
167
167
 
168
- def send_ping(method, *args, **kwargs, &block)
168
+ def send_ping(method, command, args, &block)
169
169
  results, errors = try_map do |_, client|
170
- client.send(method, *args, **kwargs, &block)
170
+ client.send(method, *args, command, &block)
171
171
  end
172
172
 
173
173
  return results.values if errors.empty?
@@ -8,27 +8,52 @@ class RedisClient
8
8
  class Pipeline
9
9
  ReplySizeError = Class.new(::RedisClient::Error)
10
10
 
11
- def initialize(router)
11
+ def initialize(router, command_builder)
12
12
  @router = router
13
+ @command_builder = command_builder
13
14
  @grouped = Hash.new([].freeze)
14
15
  @size = 0
15
16
  end
16
17
 
17
- def call(*command, **kwargs)
18
- node_key = @router.find_node_key(*command, primary_only: true)
19
- @grouped[node_key] += [[@size, :call, command, kwargs]]
18
+ def call(*args, **kwargs, &block)
19
+ command = @command_builder.generate(args, kwargs)
20
+ node_key = @router.find_node_key(command, primary_only: true)
21
+ @grouped[node_key] += [[@size, :call_v, command, block]]
20
22
  @size += 1
21
23
  end
22
24
 
23
- def call_once(*command, **kwargs)
24
- node_key = @router.find_node_key(*command, primary_only: true)
25
- @grouped[node_key] += [[@size, :call_once, command, kwargs]]
25
+ def call_v(args, &block)
26
+ command = @command_builder.generate(args)
27
+ node_key = @router.find_node_key(command, primary_only: true)
28
+ @grouped[node_key] += [[@size, :call_v, command, block]]
26
29
  @size += 1
27
30
  end
28
31
 
29
- def blocking_call(timeout, *command, **kwargs)
30
- node_key = @router.find_node_key(*command, primary_only: true)
31
- @grouped[node_key] += [[@size, :blocking_call, timeout, command, kwargs]]
32
+ def call_once(*args, **kwargs, &block)
33
+ command = @command_builder.generate(args, kwargs)
34
+ node_key = @router.find_node_key(command, primary_only: true)
35
+ @grouped[node_key] += [[@size, :call_once_v, command, block]]
36
+ @size += 1
37
+ end
38
+
39
+ def call_once_v(args, &block)
40
+ command = @command_builder.generate(args)
41
+ node_key = @router.find_node_key(command, primary_only: true)
42
+ @grouped[node_key] += [[@size, :call_once_v, command, block]]
43
+ @size += 1
44
+ end
45
+
46
+ def blocking_call(timeout, *args, **kwargs, &block)
47
+ command = @command_builder.generate(args, kwargs)
48
+ node_key = @router.find_node_key(command, primary_only: true)
49
+ @grouped[node_key] += [[@size, :blocking_call_v, timeout, command, block]]
50
+ @size += 1
51
+ end
52
+
53
+ def blocking_call_v(timeout, args, &block)
54
+ command = @command_builder.generate(args)
55
+ node_key = @router.find_node_key(command, primary_only: true)
56
+ @grouped[node_key] += [[@size, :blocking_call_v, timeout, command, block]]
32
57
  @size += 1
33
58
  end
34
59
 
@@ -37,20 +62,15 @@ class RedisClient
37
62
  end
38
63
 
39
64
  # TODO: https://github.com/redis-rb/redis-cluster-client/issues/37 handle redirections
40
- def execute # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
65
+ def execute # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
41
66
  all_replies = Array.new(@size)
42
67
  errors = {}
43
68
  threads = @grouped.map do |k, v|
44
69
  Thread.new(@router, k, v) do |router, node_key, rows|
45
70
  Thread.pass
46
71
  replies = router.find_node(node_key).pipelined do |pipeline|
47
- rows.each do |row|
48
- case row[1]
49
- when :call then pipeline.call(*row[2], **row[3])
50
- when :call_once then pipeline.call_once(*row[2], **row[3])
51
- when :blocking_call then pipeline.blocking_call(row[2], *row[3], **row[4])
52
- else raise NotImplementedError, row[1]
53
- end
72
+ rows.each do |(_size, *row, block)|
73
+ pipeline.send(*row, &block)
54
74
  end
55
75
  end
56
76
 
@@ -3,15 +3,24 @@
3
3
  class RedisClient
4
4
  class Cluster
5
5
  class PubSub
6
- def initialize(router)
6
+ def initialize(router, command_builder)
7
7
  @router = router
8
+ @command_builder = command_builder
8
9
  @pubsub = nil
9
10
  end
10
11
 
11
- def call(*command, **kwargs)
12
+ def call(*args, **kwargs)
12
13
  close
13
- @pubsub = @router.assign_node(*command).pubsub
14
- @pubsub.call(*command, **kwargs)
14
+ command = @command_builder.generate(args, kwargs)
15
+ @pubsub = @router.assign_node(command).pubsub
16
+ @pubsub.call_v(command)
17
+ end
18
+
19
+ def call_v(command)
20
+ close
21
+ command = @command_builder.generate(command)
22
+ @pubsub = @router.assign_node(command).pubsub
23
+ @pubsub.call_v(command)
15
24
  end
16
25
 
17
26
  def close
@@ -21,46 +21,80 @@ class RedisClient
21
21
  @node = fetch_cluster_info(@config, pool: @pool, **@client_kwargs)
22
22
  @command = ::RedisClient::Cluster::Command.load(@node)
23
23
  @mutex = Mutex.new
24
+ @command_builder = @config.command_builder
24
25
  end
25
26
 
26
- def send_command(method, *args, **kwargs, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
27
- command = method == :blocking_call && args.size > 1 ? args[1..] : args
28
-
27
+ def send_command(method, command, *args, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
29
28
  cmd = command.first.to_s.downcase
30
29
  case cmd
31
30
  when 'acl', 'auth', 'bgrewriteaof', 'bgsave', 'quit', 'save'
32
- @node.call_all(method, *args, **kwargs, &block).first
31
+ @node.call_all(method, command, args, &block).first
33
32
  when 'flushall', 'flushdb'
34
- @node.call_primaries(method, *args, **kwargs, &block).first
35
- when 'ping' then @node.send_ping(method, *args, **kwargs, &block).first
36
- when 'wait' then send_wait_command(method, *args, **kwargs, &block)
37
- when 'keys' then @node.call_replicas(method, *args, **kwargs, &block).flatten.sort
38
- when 'dbsize' then @node.call_replicas(method, *args, **kwargs, &block).sum
39
- when 'scan' then scan(*command, **kwargs)
40
- when 'lastsave' then @node.call_all(method, *args, **kwargs, &block).sort
41
- when 'role' then @node.call_all(method, *args, **kwargs, &block)
42
- when 'config' then send_config_command(method, *args, **kwargs, &block)
43
- when 'client' then send_client_command(method, *args, **kwargs, &block)
44
- when 'cluster' then send_cluster_command(method, *args, **kwargs, &block)
33
+ @node.call_primaries(method, command, args, &block).first
34
+ when 'ping' then @node.send_ping(method, command, args, &block).first
35
+ when 'wait' then send_wait_command(method, command, args, &block)
36
+ when 'keys' then @node.call_replicas(method, command, args, &block).flatten.sort_by(&:to_s)
37
+ when 'dbsize' then @node.call_replicas(method, command, args, &block).select { |e| e.is_a?(Integer) }.sum
38
+ when 'scan' then scan(command)
39
+ when 'lastsave' then @node.call_all(method, command, args, &block).sort_by(&:to_i)
40
+ when 'role' then @node.call_all(method, command, args, &block)
41
+ when 'config' then send_config_command(method, command, args, &block)
42
+ when 'client' then send_client_command(method, command, args, &block)
43
+ when 'cluster' then send_cluster_command(method, command, args, &block)
45
44
  when 'readonly', 'readwrite', 'shutdown'
46
45
  raise ::RedisClient::Cluster::OrchestrationCommandNotSupported, cmd
47
- when 'memory' then send_memory_command(method, *args, **kwargs, &block)
48
- when 'script' then send_script_command(method, *args, **kwargs, &block)
49
- when 'pubsub' then send_pubsub_command(method, *args, **kwargs, &block)
46
+ when 'memory' then send_memory_command(method, command, args, &block)
47
+ when 'script' then send_script_command(method, command, args, &block)
48
+ when 'pubsub' then send_pubsub_command(method, command, args, &block)
50
49
  when 'discard', 'exec', 'multi', 'unwatch'
51
50
  raise ::RedisClient::Cluster::AmbiguousNodeError, cmd
52
51
  else
53
- node = assign_node(*command)
54
- try_send(node, method, *args, **kwargs, &block)
52
+ node = assign_node(command)
53
+ try_send(node, method, command, args, &block)
55
54
  end
56
55
  rescue ::RedisClient::Cluster::Node::ReloadNeeded
57
56
  update_cluster_info!
58
57
  raise ::RedisClient::Cluster::NodeMightBeDown
58
+ rescue ::RedisClient::Cluster::ErrorCollection => e
59
+ update_cluster_info! if e.errors.values.any? do |err|
60
+ err.message.start_with?('CLUSTERDOWN Hash slot not served')
61
+ end
62
+ raise
59
63
  end
60
64
 
61
65
  # @see https://redis.io/topics/cluster-spec#redirection-and-resharding
62
66
  # Redirection and resharding
63
- def try_send(node, method, *args, retry_count: 3, **kwargs, &block) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
67
+ def try_send(node, method, command, args, retry_count: 3, &block) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
68
+ node.send(method, *args, command, &block)
69
+ rescue ::RedisClient::CommandError => e
70
+ raise if retry_count <= 0
71
+
72
+ if e.message.start_with?('MOVED')
73
+ node = assign_redirection_node(e.message)
74
+ retry_count -= 1
75
+ retry
76
+ elsif e.message.start_with?('ASK')
77
+ node = assign_asking_node(e.message)
78
+ node.call('ASKING')
79
+ retry_count -= 1
80
+ retry
81
+ elsif e.message.start_with?('CLUSTERDOWN Hash slot not served')
82
+ update_cluster_info!
83
+ retry_count -= 1
84
+ retry
85
+ else
86
+ raise
87
+ end
88
+ rescue ::RedisClient::ConnectionError => e
89
+ raise if method == :blocking_call_v || (method == :blocking_call && e.is_a?(RedisClient::ReadTimeoutError))
90
+ raise if retry_count <= 0
91
+
92
+ update_cluster_info!
93
+ retry_count -= 1
94
+ retry
95
+ end
96
+
97
+ def try_delegate(node, method, *args, retry_count: 3, **kwargs, &block) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
64
98
  node.send(method, *args, **kwargs, &block)
65
99
  rescue ::RedisClient::CommandError => e
66
100
  raise if retry_count <= 0
@@ -74,6 +108,10 @@ class RedisClient
74
108
  node.call('ASKING')
75
109
  retry_count -= 1
76
110
  retry
111
+ elsif e.message.start_with?('CLUSTERDOWN Hash slot not served')
112
+ update_cluster_info!
113
+ retry_count -= 1
114
+ retry
77
115
  else
78
116
  raise
79
117
  end
@@ -86,6 +124,8 @@ class RedisClient
86
124
  end
87
125
 
88
126
  def scan(*command, **kwargs) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
127
+ command = @command_builder.generate(command, kwargs)
128
+
89
129
  command[1] = ZERO_CURSOR_FOR_SCAN if command.size == 1
90
130
  input_cursor = Integer(command[1])
91
131
 
@@ -99,7 +139,7 @@ class RedisClient
99
139
 
100
140
  command[1] = raw_cursor.to_s
101
141
 
102
- result_cursor, result_keys = client.call(*command, **kwargs)
142
+ result_cursor, result_keys = client.call_v(command)
103
143
  result_cursor = Integer(result_cursor)
104
144
 
105
145
  client_index += 1 if result_cursor == 0
@@ -107,12 +147,12 @@ class RedisClient
107
147
  [((result_cursor << 8) + client_index).to_s, result_keys]
108
148
  end
109
149
 
110
- def assign_node(*command)
111
- node_key = find_node_key(*command)
150
+ def assign_node(command, primary_only: false)
151
+ node_key = find_node_key(command, primary_only: primary_only)
112
152
  find_node(node_key)
113
153
  end
114
154
 
115
- def find_node_key(*command, primary_only: false)
155
+ def find_node_key(command, primary_only: false)
116
156
  key = @command.extract_first_key(command)
117
157
  slot = key.empty? ? nil : ::RedisClient::Cluster::KeySlotConverter.convert(key)
118
158
 
@@ -133,10 +173,14 @@ class RedisClient
133
173
  retry
134
174
  end
135
175
 
176
+ def command_exists?(name)
177
+ @command.exists?(name)
178
+ end
179
+
136
180
  private
137
181
 
138
- def send_wait_command(method, *args, retry_count: 3, **kwargs, &block)
139
- @node.call_primaries(method, *args, **kwargs, &block).select { |r| r.is_a?(Integer) }.sum
182
+ def send_wait_command(method, command, args, retry_count: 3, &block)
183
+ @node.call_primaries(method, command, args, &block).select { |r| r.is_a?(Integer) }.sum
140
184
  rescue ::RedisClient::Cluster::ErrorCollection => e
141
185
  raise if retry_count <= 0
142
186
  raise if e.errors.values.none? do |err|
@@ -148,76 +192,65 @@ class RedisClient
148
192
  retry
149
193
  end
150
194
 
151
- def send_config_command(method, *args, **kwargs, &block)
152
- command = method == :blocking_call && args.size > 1 ? args[1..] : args
153
-
195
+ def send_config_command(method, command, args, &block)
154
196
  case command[1].to_s.downcase
155
197
  when 'resetstat', 'rewrite', 'set'
156
- @node.call_all(method, *args, **kwargs, &block).first
157
- else assign_node(*command).send(method, *args, **kwargs, &block)
198
+ @node.call_all(method, command, args, &block).first
199
+ else assign_node(command).send(method, *args, command, &block)
158
200
  end
159
201
  end
160
202
 
161
- def send_memory_command(method, *args, **kwargs, &block)
162
- command = method == :blocking_call && args.size > 1 ? args[1..] : args
163
-
203
+ def send_memory_command(method, command, args, &block)
164
204
  case command[1].to_s.downcase
165
- when 'stats' then @node.call_all(method, *args, **kwargs, &block)
166
- when 'purge' then @node.call_all(method, *args, **kwargs, &block).first
167
- else assign_node(*command).send(method, *args, **kwargs, &block)
205
+ when 'stats' then @node.call_all(method, command, args, &block)
206
+ when 'purge' then @node.call_all(method, command, args, &block).first
207
+ else assign_node(command).send(method, *args, command, &block)
168
208
  end
169
209
  end
170
210
 
171
- def send_client_command(method, *args, **kwargs, &block)
172
- command = method == :blocking_call && args.size > 1 ? args[1..] : args
173
-
211
+ def send_client_command(method, command, args, &block)
174
212
  case command[1].to_s.downcase
175
- when 'list' then @node.call_all(method, *args, **kwargs, &block).flatten
213
+ when 'list' then @node.call_all(method, command, args, &block).flatten
176
214
  when 'pause', 'reply', 'setname'
177
- @node.call_all(method, *args, **kwargs, &block).first
178
- else assign_node(*command).send(method, *args, **kwargs, &block)
215
+ @node.call_all(method, command, args, &block).first
216
+ else assign_node(command).send(method, *args, command, &block)
179
217
  end
180
218
  end
181
219
 
182
- def send_cluster_command(method, *args, **kwargs, &block) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
183
- command = method == :blocking_call && args.size > 1 ? args[1..] : args
220
+ def send_cluster_command(method, command, args, &block) # rubocop:disable Metrics/MethodLength
184
221
  subcommand = command[1].to_s.downcase
185
222
 
186
223
  case subcommand
187
224
  when 'addslots', 'delslots', 'failover', 'forget', 'meet', 'replicate',
188
225
  'reset', 'set-config-epoch', 'setslot'
189
226
  raise ::RedisClient::Cluster::OrchestrationCommandNotSupported, ['cluster', subcommand]
190
- when 'saveconfig' then @node.call_all(method, *args, **kwargs, &block).first
227
+ when 'saveconfig' then @node.call_all(method, command, args, &block).first
191
228
  when 'getkeysinslot'
192
229
  raise ArgumentError, command.join(' ') if command.size != 4
193
230
 
194
- find_node(@node.find_node_key_of_replica(command[2])).send(method, *args, **kwargs, &block)
195
- else assign_node(*command).send(method, *args, **kwargs, &block)
231
+ find_node(@node.find_node_key_of_replica(command[2])).send(method, *args, command, &block)
232
+ else assign_node(command).send(method, *args, command, &block)
196
233
  end
197
234
  end
198
235
 
199
- def send_script_command(method, *args, **kwargs, &block)
200
- command = method == :blocking_call && args.size > 1 ? args[1..] : args
201
-
236
+ def send_script_command(method, command, args, &block)
202
237
  case command[1].to_s.downcase
203
238
  when 'debug', 'kill'
204
- @node.call_all(method, *args, **kwargs, &block).first
239
+ @node.call_all(method, command, args, &block).first
205
240
  when 'flush', 'load'
206
- @node.call_primaries(method, *args, **kwargs, &block).first
207
- else assign_node(*command).send(method, *args, **kwargs, &block)
241
+ @node.call_primaries(method, command, args, &block).first
242
+ else assign_node(command).send(method, *args, command, &block)
208
243
  end
209
244
  end
210
245
 
211
- def send_pubsub_command(method, *args, **kwargs, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
212
- command = method == :blocking_call && args.size > 1 ? args[1..] : args
213
-
246
+ def send_pubsub_command(method, command, args, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
214
247
  case command[1].to_s.downcase
215
- when 'channels' then @node.call_all(method, *args, **kwargs, &block).flatten.uniq.sort
248
+ when 'channels' then @node.call_all(method, command, args, &block).flatten.uniq.sort_by(&:to_s)
216
249
  when 'numsub'
217
- @node.call_all(method, *args, **kwargs, &block).reject(&:empty?).map { |e| Hash[*e] }
250
+ @node.call_all(method, command, args, &block).reject(&:empty?).map { |e| Hash[*e] }
218
251
  .reduce({}) { |a, e| a.merge(e) { |_, v1, v2| v1 + v2 } }
219
- when 'numpat' then @node.call_all(method, *args, **kwargs, &block).sum
220
- else assign_node(*command).send(method, *args, **kwargs, &block)
252
+ when 'numpat' then @node.call_all(method, command, args, &block).select { |e| e.is_a?(Integer) }.sum
253
+ else assign_node(command).send(method, *args, command, &block)
221
254
  end
222
255
  end
223
256
 
@@ -244,7 +277,7 @@ class RedisClient
244
277
  def update_cluster_info!
245
278
  @mutex.synchronize do
246
279
  begin
247
- @node.call_all(:close)
280
+ @node.each(&:close)
248
281
  rescue ::RedisClient::Cluster::ErrorCollection
249
282
  # ignore
250
283
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'redis_client'
4
3
  require 'redis_client/cluster/pipeline'
5
4
  require 'redis_client/cluster/pub_sub'
6
5
  require 'redis_client/cluster/router'
@@ -9,24 +8,46 @@ class RedisClient
9
8
  class Cluster
10
9
  ZERO_CURSOR_FOR_SCAN = '0'
11
10
 
11
+ attr_reader :config
12
+
12
13
  def initialize(config, pool: nil, **kwargs)
14
+ @config = config
13
15
  @router = ::RedisClient::Cluster::Router.new(config, pool: pool, **kwargs)
16
+ @command_builder = config.command_builder
14
17
  end
15
18
 
16
19
  def inspect
17
20
  "#<#{self.class.name} #{@router.node.node_keys.join(', ')}>"
18
21
  end
19
22
 
20
- def call(*command, **kwargs)
21
- @router.send_command(:call, *command, **kwargs)
23
+ def call(*args, **kwargs, &block)
24
+ command = @command_builder.generate(args, kwargs)
25
+ @router.send_command(:call_v, command, &block)
26
+ end
27
+
28
+ def call_v(command, &block)
29
+ command = @command_builder.generate(command)
30
+ @router.send_command(:call_v, command, &block)
31
+ end
32
+
33
+ def call_once(*args, **kwargs, &block)
34
+ command = @command_builder.generate(args, kwargs)
35
+ @router.send_command(:call_once_v, command, &block)
36
+ end
37
+
38
+ def call_once_v(command, &block)
39
+ command = @command_builder.generate(command)
40
+ @router.send_command(:call_once_v, command, &block)
22
41
  end
23
42
 
24
- def call_once(*command, **kwargs)
25
- @router.send_command(:call_once, *command, **kwargs)
43
+ def blocking_call(timeout, *args, **kwargs, &block)
44
+ command = @command_builder.generate(args, kwargs)
45
+ @router.send_command(:blocking_call_v, command, timeout, &block)
26
46
  end
27
47
 
28
- def blocking_call(timeout, *command, **kwargs)
29
- @router.send_command(:blocking_call, timeout, *command, **kwargs)
48
+ def blocking_call_v(timeout, command, &block)
49
+ command = @command_builder.generate(command)
50
+ @router.send_command(:blocking_call_v, command, timeout, &block)
30
51
  end
31
52
 
32
53
  def scan(*args, **kwargs, &block)
@@ -41,22 +62,22 @@ class RedisClient
41
62
  end
42
63
 
43
64
  def sscan(key, *args, **kwargs, &block)
44
- node = @router.assign_node('SSCAN', key)
45
- @router.try_send(node, :sscan, key, *args, **kwargs, &block)
65
+ node = @router.assign_node(['SSCAN', key])
66
+ @router.try_delegate(node, :sscan, key, *args, **kwargs, &block)
46
67
  end
47
68
 
48
69
  def hscan(key, *args, **kwargs, &block)
49
- node = @router.assign_node('HSCAN', key)
50
- @router.try_send(node, :hscan, key, *args, **kwargs, &block)
70
+ node = @router.assign_node(['HSCAN', key])
71
+ @router.try_delegate(node, :hscan, key, *args, **kwargs, &block)
51
72
  end
52
73
 
53
74
  def zscan(key, *args, **kwargs, &block)
54
- node = @router.assign_node('ZSCAN', key)
55
- @router.try_send(node, :zscan, key, *args, **kwargs, &block)
75
+ node = @router.assign_node(['ZSCAN', key])
76
+ @router.try_delegate(node, :zscan, key, *args, **kwargs, &block)
56
77
  end
57
78
 
58
79
  def pipelined
59
- pipeline = ::RedisClient::Cluster::Pipeline.new(@router)
80
+ pipeline = ::RedisClient::Cluster::Pipeline.new(@router, @command_builder)
60
81
  yield pipeline
61
82
  return [] if pipeline.empty? == 0
62
83
 
@@ -64,12 +85,30 @@ class RedisClient
64
85
  end
65
86
 
66
87
  def pubsub
67
- ::RedisClient::Cluster::PubSub.new(@router)
88
+ ::RedisClient::Cluster::PubSub.new(@router, @command_builder)
68
89
  end
69
90
 
70
91
  def close
71
- @router.node.call_all(:close)
92
+ @router.node.each(&:close)
72
93
  nil
73
94
  end
95
+
96
+ private
97
+
98
+ def method_missing(name, *args, **kwargs, &block)
99
+ if @router.command_exists?(name)
100
+ args.unshift(name)
101
+ command = @command_builder.generate(args, kwargs)
102
+ return @router.send_command(:call_v, command, &block)
103
+ end
104
+
105
+ super
106
+ end
107
+
108
+ def respond_to_missing?(name, include_private = false)
109
+ return true if @router.command_exists?(name)
110
+
111
+ super
112
+ end
74
113
  end
75
114
  end
@@ -4,6 +4,7 @@ require 'uri'
4
4
  require 'redis_client'
5
5
  require 'redis_client/cluster'
6
6
  require 'redis_client/cluster/node_key'
7
+ require 'redis_client/command_builder'
7
8
 
8
9
  class RedisClient
9
10
  class ClusterConfig
@@ -19,12 +20,16 @@ class RedisClient
19
20
 
20
21
  InvalidClientConfigError = Class.new(::RedisClient::Error)
21
22
 
22
- def initialize(nodes: DEFAULT_NODES, replica: false, fixed_hostname: '', **client_config)
23
+ attr_reader :command_builder, :client_config
24
+
25
+ def initialize(nodes: DEFAULT_NODES, replica: false, client_implementation: Cluster, fixed_hostname: '', **client_config)
23
26
  @replica = true & replica
24
27
  @fixed_hostname = fixed_hostname.to_s
25
28
  @node_configs = build_node_configs(nodes.dup)
26
29
  client_config = client_config.reject { |k, _| IGNORE_GENERIC_CONFIG_KEYS.include?(k) }
30
+ @command_builder = client_config.fetch(:command_builder, ::RedisClient::CommandBuilder)
27
31
  @client_config = merge_generic_config(client_config, @node_configs)
32
+ @client_implementation = client_implementation
28
33
  @mutex = Mutex.new
29
34
  end
30
35
 
@@ -32,12 +37,16 @@ class RedisClient
32
37
  "#<#{self.class.name} #{per_node_key.values}>"
33
38
  end
34
39
 
40
+ def read_timeout
41
+ @client_config[:read_timeout] || @client_config[:timeout] || RedisClient::Config::DEFAULT_TIMEOUT
42
+ end
43
+
35
44
  def new_pool(size: 5, timeout: 5, **kwargs)
36
- ::RedisClient::Cluster.new(self, pool: { size: size, timeout: timeout }, **kwargs)
45
+ @client_implementation.new(self, pool: { size: size, timeout: timeout }, **kwargs)
37
46
  end
38
47
 
39
48
  def new_client(**kwargs)
40
- ::RedisClient::Cluster.new(self, **kwargs)
49
+ @client_implementation.new(self, **kwargs)
41
50
  end
42
51
 
43
52
  def per_node_key
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.0.11
4
+ version: 0.2.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: 2022-06-27 00:00:00.000000000 Z
11
+ date: 2022-08-17 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.5'
19
+ version: '0.6'
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.5'
26
+ version: '0.6'
27
27
  description:
28
28
  email:
29
29
  - proxy0721@gmail.com
@@ -31,6 +31,7 @@ executables: []
31
31
  extensions: []
32
32
  extra_rdoc_files: []
33
33
  files:
34
+ - lib/redis-cluster-client.rb
34
35
  - lib/redis_client/cluster.rb
35
36
  - lib/redis_client/cluster/command.rb
36
37
  - lib/redis_client/cluster/errors.rb