redis-cluster-client 0.0.11 → 0.2.0

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: 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