redis-cluster-client 0.8.1 → 0.9.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: 53d1591b89859ddc92dc6ca349ff211d0a6ac14c68ad72e920cc9725d82587b4
4
- data.tar.gz: fcfeceffddc9201f8b8c5d6999d3e7aa8581c9f24f0d823b029a5561ef82887d
3
+ metadata.gz: 97383b0daf1dcc2e5fcd8e50640e9ecbd903562a10cca0f4996ada1807469f80
4
+ data.tar.gz: 675ae25f2fbf2d9d67234b4e1ad81894656f11a32fe4cd1ebf8ae73c90c67509
5
5
  SHA512:
6
- metadata.gz: d51aefc48e64ef0cdbded8b5c8e4b005dcfd103d9fa4ecfa0dff1d9aa4fed5d6f6aefd4dca4ac53e89867b5276812bc6c7f8cf3cb41c1184bd9d8f19ea601471
7
- data.tar.gz: 548da631726b4881f0a7712186f656733cb6941aef9ca3415498a6f048f59170fb74a59c30da68d655cee5e5773732d9d781c489f6cda2f0c92a341f1b28b892
6
+ metadata.gz: 851565ace738fb9bf51d1211ece97767520c94a7b02e49b053732de351d77a30e7104ae1f8143a0ee3903a2b99de39b298b3eec80c2fb94b08fb4747180bb03d
7
+ data.tar.gz: 0a0b6ada72bccfcdb7cecda533f8f31761f15f991e359bc211a4d54ba973b2c68983efd3a9a594e7baab11b614fecb058de98b71f1890f7e52150f40e2c34d1b
@@ -97,6 +97,12 @@ class RedisClient
97
97
  @commands.key?(::RedisClient::Cluster::NormalizedCmdName.instance.get_by_name(name))
98
98
  end
99
99
 
100
+ def determine_key_step(command)
101
+ name = ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_command(command)
102
+ # Some commands like EVALSHA have zero as the step in COMMANDS somehow.
103
+ @commands[name].key_step == 0 ? 1 : @commands[name].key_step
104
+ end
105
+
100
106
  private
101
107
 
102
108
  def determine_first_key_position(command) # rubocop:disable Metrics/CyclomaticComplexity
@@ -143,12 +149,6 @@ class RedisClient
143
149
  end
144
150
  end
145
151
 
146
- def determine_key_step(command)
147
- name = ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_command(command)
148
- # Some commands like EVALSHA have zero as the step in COMMANDS somehow.
149
- @commands[name].key_step == 0 ? 1 : @commands[name].key_step
150
- end
151
-
152
152
  def determine_optional_key_position(command, option_name) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
153
153
  idx = command&.flatten&.map(&:to_s)&.map(&:downcase)&.index(option_name&.downcase)
154
154
  idx.nil? ? 0 : idx + 1
@@ -5,6 +5,9 @@ class RedisClient
5
5
  class Node
6
6
  class BaseTopology
7
7
  IGNORE_GENERIC_CONFIG_KEYS = %i[url host port path].freeze
8
+ EMPTY_HASH = {}.freeze
9
+ EMPTY_ARRAY = [].freeze
10
+
8
11
  attr_reader :clients, :primary_clients, :replica_clients
9
12
 
10
13
  def initialize(pool, concurrent_worker, **kwargs)
@@ -92,13 +92,7 @@ class RedisClient
92
92
  end
93
93
  end
94
94
 
95
- def initialize(
96
- concurrent_worker,
97
- config:,
98
- pool: nil,
99
- **kwargs
100
- )
101
-
95
+ def initialize(concurrent_worker, config:, pool: nil, **kwargs)
102
96
  @concurrent_worker = concurrent_worker
103
97
  @slots = build_slot_node_mappings(EMPTY_ARRAY)
104
98
  @replications = build_replication_mappings(EMPTY_ARRAY)
@@ -106,6 +100,7 @@ class RedisClient
106
100
  @topology = klass.new(pool, @concurrent_worker, **kwargs)
107
101
  @config = config
108
102
  @mutex = Mutex.new
103
+ @last_reloaded_at = nil
109
104
  end
110
105
 
111
106
  def inspect
@@ -10,6 +10,7 @@ require 'redis_client/cluster/node_key'
10
10
  require 'redis_client/cluster/normalized_cmd_name'
11
11
  require 'redis_client/cluster/transaction'
12
12
  require 'redis_client/cluster/optimistic_locking'
13
+ require 'redis_client/cluster/pipeline'
13
14
  require 'redis_client/cluster/error_identification'
14
15
 
15
16
  class RedisClient
@@ -17,6 +18,11 @@ class RedisClient
17
18
  class Router
18
19
  ZERO_CURSOR_FOR_SCAN = '0'
19
20
  TSF = ->(f, x) { f.nil? ? x : f.call(x) }.curry
21
+ MULTIPLE_KEYS_COMMAND_TO_PIPELINE = {
22
+ 'mset' => 'set',
23
+ 'mget' => 'get',
24
+ 'del' => 'del'
25
+ }.freeze
20
26
 
21
27
  def initialize(config, concurrent_worker, pool: nil, **kwargs)
22
28
  @config = config.dup
@@ -48,6 +54,8 @@ class RedisClient
48
54
  when 'script' then send_script_command(method, command, args, &block)
49
55
  when 'pubsub' then send_pubsub_command(method, command, args, &block)
50
56
  when 'watch' then send_watch_command(command, &block)
57
+ when 'mset', 'mget', 'del'
58
+ send_multiple_keys_command(cmd, method, command, args, &block)
51
59
  when 'acl', 'auth', 'bgrewriteaof', 'bgsave', 'quit', 'save'
52
60
  @node.call_all(method, command, args).first.then(&TSF.call(block))
53
61
  when 'flushall', 'flushdb'
@@ -314,7 +322,6 @@ class RedisClient
314
322
  end
315
323
  end
316
324
 
317
- # for redis-rb
318
325
  def send_watch_command(command)
319
326
  raise ::RedisClient::Cluster::Transaction::ConsistencyError, 'A block required. And you need to use the block argument as a client for the transaction.' unless block_given?
320
327
 
@@ -327,6 +334,40 @@ class RedisClient
327
334
  end
328
335
  end
329
336
 
337
+ def send_multiple_keys_command(cmd, method, command, args, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
338
+ key_step = @command.determine_key_step(cmd)
339
+ if command.size <= key_step + 1 || !::RedisClient::Cluster::KeySlotConverter.extract_hash_tag(command[1]).empty? # rubocop:disable Style/IfUnlessModifier
340
+ return try_send(assign_node(command), method, command, args, &block)
341
+ end
342
+
343
+ seed = @config.use_replica? && @config.replica_affinity == :random ? nil : Random.new_seed
344
+ pipeline = ::RedisClient::Cluster::Pipeline.new(self, @command_builder, @concurrent_worker, exception: true, seed: seed)
345
+
346
+ # This implementation is prioritized to lessen memory consumption rather than readability.
347
+ single_key_cmd = MULTIPLE_KEYS_COMMAND_TO_PIPELINE[cmd]
348
+ single_command = Array.new(key_step + 1)
349
+ single_command[0] = single_key_cmd
350
+ if key_step == 1
351
+ command[1..].each do |key|
352
+ single_command[1] = key
353
+ pipeline.call_v(single_command)
354
+ end
355
+ else
356
+ command[1..].each_slice(key_step) do |v|
357
+ key_step.times { |i| single_command[i + 1] = v[i] }
358
+ pipeline.call_v(single_command)
359
+ end
360
+ end
361
+
362
+ replies = pipeline.execute
363
+ result = case cmd
364
+ when 'mset' then replies.first
365
+ when 'del' then replies.sum
366
+ else replies
367
+ end
368
+ block_given? ? yield(result) : result
369
+ end
370
+
330
371
  def update_cluster_info!
331
372
  @node.reload!
332
373
  end
@@ -8,6 +8,7 @@ class RedisClient
8
8
  class Transaction
9
9
  ConsistencyError = Class.new(::RedisClient::Error)
10
10
  MAX_REDIRECTION = 2
11
+ EMPTY_ARRAY = [].freeze
11
12
 
12
13
  def initialize(router, command_builder, node: nil, slot: nil, asking: false)
13
14
  @router = router
@@ -62,10 +63,10 @@ class RedisClient
62
63
  def execute
63
64
  @pending_commands.each(&:call)
64
65
 
65
- raise ArgumentError, 'empty transaction' if @pipeline._empty?
66
+ return EMPTY_ARRAY if @pipeline._empty?
66
67
  raise ConsistencyError, "couldn't determine the node: #{@pipeline._commands}" if @node.nil?
67
68
 
68
- settle
69
+ commit
69
70
  end
70
71
 
71
72
  private
@@ -92,8 +93,17 @@ class RedisClient
92
93
  @pending_commands.clear
93
94
  end
94
95
 
95
- def settle
96
+ def commit
96
97
  @pipeline.call('EXEC')
98
+ settle
99
+ end
100
+
101
+ def cancel
102
+ @pipeline.call('DISCARD')
103
+ settle
104
+ end
105
+
106
+ def settle
97
107
  # If we needed ASKING on the watch, we need ASKING on the multi as well.
98
108
  @node.call('ASKING') if @asking
99
109
  # Don't handle redirections at this level if we're in a watch (the watcher handles redirections
@@ -5,7 +5,6 @@ require 'redis_client/cluster/pipeline'
5
5
  require 'redis_client/cluster/pub_sub'
6
6
  require 'redis_client/cluster/router'
7
7
  require 'redis_client/cluster/transaction'
8
- require 'redis_client/cluster/pinning_node'
9
8
  require 'redis_client/cluster/optimistic_locking'
10
9
 
11
10
  class RedisClient
@@ -118,13 +117,8 @@ class RedisClient
118
117
  ::RedisClient::Cluster::PubSub.new(@router, @command_builder)
119
118
  end
120
119
 
121
- # TODO: This isn't an official public interface yet. Don't use in your production environment.
122
- # @see https://github.com/redis-rb/redis-cluster-client/issues/299
123
- def with(key: nil, hashtag: nil, write: true)
124
- key = process_with_arguments(key, hashtag)
125
- node_key = @router.find_node_key_by_key(key, primary: write)
126
- node = @router.find_node(node_key)
127
- node.with { |c| yield ::RedisClient::Cluster::PinningNode.new(c) }
120
+ def with(...)
121
+ raise NotImplementedError, 'No way to use'
128
122
  end
129
123
 
130
124
  def close
@@ -135,19 +129,6 @@ class RedisClient
135
129
 
136
130
  private
137
131
 
138
- def process_with_arguments(key, hashtag) # rubocop:disable Metrics/CyclomaticComplexity
139
- raise ArgumentError, 'Only one of key or hashtag may be provided' if key && hashtag
140
-
141
- if hashtag
142
- # The documentation says not to wrap your hashtag in {}, but people will probably
143
- # do it anyway and it's easy for us to fix here.
144
- key = hashtag&.match?(/^{.*}$/) ? hashtag : "{#{hashtag}}"
145
- end
146
- raise ArgumentError, 'One of key or hashtag must be provided' if key.nil? || key.empty?
147
-
148
- key
149
- end
150
-
151
132
  def method_missing(name, *args, **kwargs, &block)
152
133
  if @router.command_exists?(name)
153
134
  args.unshift(name)
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.8.1
4
+ version: 0.9.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-04-13 00:00:00.000000000 Z
11
+ date: 2024-05-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis-client
@@ -50,7 +50,6 @@ files:
50
50
  - lib/redis_client/cluster/node_key.rb
51
51
  - lib/redis_client/cluster/normalized_cmd_name.rb
52
52
  - lib/redis_client/cluster/optimistic_locking.rb
53
- - lib/redis_client/cluster/pinning_node.rb
54
53
  - lib/redis_client/cluster/pipeline.rb
55
54
  - lib/redis_client/cluster/pub_sub.rb
56
55
  - lib/redis_client/cluster/router.rb
@@ -78,7 +77,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
78
77
  - !ruby/object:Gem::Version
79
78
  version: '0'
80
79
  requirements: []
81
- rubygems_version: 3.5.3
80
+ rubygems_version: 3.5.9
82
81
  signing_key:
83
82
  specification_version: 4
84
83
  summary: A Redis cluster client for Ruby
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class RedisClient
4
- class Cluster
5
- class PinningNode
6
- def initialize(client)
7
- @client = client
8
- end
9
-
10
- def call(*args, **kwargs, &block)
11
- @client.call(*args, **kwargs, &block)
12
- end
13
-
14
- def call_v(args, &block)
15
- @client.call_v(args, &block)
16
- end
17
-
18
- def call_once(*args, **kwargs, &block)
19
- @client.call_once(*args, **kwargs, &block)
20
- end
21
-
22
- def call_once_v(args, &block)
23
- @client.call_once_v(args, &block)
24
- end
25
-
26
- def blocking_call(timeout, *args, **kwargs, &block)
27
- @client.blocking_call(timeout, *args, **kwargs, &block)
28
- end
29
-
30
- def blocking_call_v(timeout, args, &block)
31
- @client.blocking_call_v(timeout, args, &block)
32
- end
33
- end
34
- end
35
- end