redis 4.8.1 → 5.4.1

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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +86 -0
  3. data/README.md +125 -162
  4. data/lib/redis/client.rb +81 -608
  5. data/lib/redis/commands/bitmaps.rb +14 -4
  6. data/lib/redis/commands/cluster.rb +1 -18
  7. data/lib/redis/commands/connection.rb +5 -10
  8. data/lib/redis/commands/geo.rb +3 -3
  9. data/lib/redis/commands/hashes.rb +13 -6
  10. data/lib/redis/commands/hyper_log_log.rb +1 -1
  11. data/lib/redis/commands/keys.rb +27 -23
  12. data/lib/redis/commands/lists.rb +74 -25
  13. data/lib/redis/commands/pubsub.rb +34 -25
  14. data/lib/redis/commands/server.rb +15 -15
  15. data/lib/redis/commands/sets.rb +35 -40
  16. data/lib/redis/commands/sorted_sets.rb +128 -18
  17. data/lib/redis/commands/streams.rb +48 -21
  18. data/lib/redis/commands/strings.rb +18 -17
  19. data/lib/redis/commands/transactions.rb +7 -31
  20. data/lib/redis/commands.rb +11 -12
  21. data/lib/redis/distributed.rb +136 -72
  22. data/lib/redis/errors.rb +20 -50
  23. data/lib/redis/hash_ring.rb +26 -26
  24. data/lib/redis/pipeline.rb +47 -222
  25. data/lib/redis/subscribe.rb +50 -14
  26. data/lib/redis/version.rb +1 -1
  27. data/lib/redis.rb +79 -184
  28. metadata +10 -57
  29. data/lib/redis/cluster/command.rb +0 -79
  30. data/lib/redis/cluster/command_loader.rb +0 -33
  31. data/lib/redis/cluster/key_slot_converter.rb +0 -72
  32. data/lib/redis/cluster/node.rb +0 -120
  33. data/lib/redis/cluster/node_key.rb +0 -31
  34. data/lib/redis/cluster/node_loader.rb +0 -34
  35. data/lib/redis/cluster/option.rb +0 -100
  36. data/lib/redis/cluster/slot.rb +0 -86
  37. data/lib/redis/cluster/slot_loader.rb +0 -46
  38. data/lib/redis/cluster.rb +0 -315
  39. data/lib/redis/connection/command_helper.rb +0 -41
  40. data/lib/redis/connection/hiredis.rb +0 -68
  41. data/lib/redis/connection/registry.rb +0 -13
  42. data/lib/redis/connection/ruby.rb +0 -437
  43. data/lib/redis/connection/synchrony.rb +0 -148
  44. data/lib/redis/connection.rb +0 -11
data/lib/redis.rb CHANGED
@@ -1,31 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "redis-client"
4
+
3
5
  require "monitor"
4
6
  require "redis/errors"
5
7
  require "redis/commands"
6
8
 
7
9
  class Redis
8
10
  BASE_PATH = __dir__
9
- @exists_returns_integer = true
10
- @sadd_returns_boolean = true
11
-
12
11
  Deprecated = Class.new(StandardError)
13
12
 
14
13
  class << self
15
- attr_reader :exists_returns_integer
16
- attr_accessor :silence_deprecations, :raise_deprecations, :sadd_returns_boolean
17
-
18
- def exists_returns_integer=(value)
19
- unless value
20
- deprecate!(
21
- "`Redis#exists(key)` will return an Integer by default in redis-rb 4.3. The option to explicitly " \
22
- "disable this behaviour via `Redis.exists_returns_integer` will be removed in 5.0. You should use " \
23
- "`exists?` instead."
24
- )
25
- end
26
-
27
- @exists_returns_integer = value
28
- end
14
+ attr_accessor :silence_deprecations, :raise_deprecations
29
15
 
30
16
  def deprecate!(message)
31
17
  unless silence_deprecations
@@ -36,20 +22,22 @@ class Redis
36
22
  end
37
23
  end
38
24
  end
25
+ end
39
26
 
40
- def current
41
- deprecate!("`Redis.current` is deprecated and will be removed in 5.0. (called from: #{caller(1, 1).first})")
42
- @current ||= Redis.new
43
- end
44
-
45
- def current=(redis)
46
- deprecate!("`Redis.current=` is deprecated and will be removed in 5.0. (called from: #{caller(1, 1).first})")
47
- @current = redis
27
+ # soft-deprecated
28
+ # We added this back for older sidekiq releases
29
+ module Connection
30
+ class << self
31
+ def drivers
32
+ [RedisClient.default_driver]
33
+ end
48
34
  end
49
35
  end
50
36
 
51
37
  include Commands
52
38
 
39
+ SERVER_URL_OPTIONS = %i(url host port path).freeze
40
+
53
41
  # Create a new client instance
54
42
  #
55
43
  # @param [Hash] options
@@ -59,56 +47,49 @@ class Redis
59
47
  # @option options [String] :host ("127.0.0.1") server hostname
60
48
  # @option options [Integer] :port (6379) server port
61
49
  # @option options [String] :path path to server socket (overrides host and port)
62
- # @option options [Float] :timeout (5.0) timeout in seconds
50
+ # @option options [Float] :timeout (1.0) timeout in seconds
63
51
  # @option options [Float] :connect_timeout (same as timeout) timeout for initial connect in seconds
64
52
  # @option options [String] :username Username to authenticate against server
65
53
  # @option options [String] :password Password to authenticate against server
66
- # @option options [Integer] :db (0) Database to select after initial connect
67
- # @option options [Symbol] :driver Driver to use, currently supported: `:ruby`, `:hiredis`, `:synchrony`
54
+ # @option options [Integer] :db (0) Database to select after connect and on reconnects
55
+ # @option options [Symbol] :driver Driver to use, currently supported: `:ruby`, `:hiredis`
68
56
  # @option options [String] :id ID for the client connection, assigns name to current connection by sending
69
57
  # `CLIENT SETNAME`
70
- # @option options [Hash, Integer] :tcp_keepalive Keepalive values, if Integer `intvl` and `probe` are calculated
71
- # based on the value, if Hash `time`, `intvl` and `probes` can be specified as a Integer
72
- # @option options [Integer] :reconnect_attempts Number of attempts trying to connect
58
+ # @option options [Integer, Array<Integer, Float>] :reconnect_attempts Number of attempts trying to connect,
59
+ # or a list of sleep duration between attempts.
73
60
  # @option options [Boolean] :inherit_socket (false) Whether to use socket in forked process or not
61
+ # @option options [String] :name The name of the server group to connect to.
74
62
  # @option options [Array] :sentinels List of sentinels to contact
75
- # @option options [Symbol] :role (:master) Role to fetch via Sentinel, either `:master` or `:slave`
76
- # @option options [Array<String, Hash{Symbol => String, Integer}>] :cluster List of cluster nodes to contact
77
- # @option options [Boolean] :replica Whether to use readonly replica nodes in Redis Cluster or not
78
- # @option options [String] :fixed_hostname Specify a FQDN if cluster mode enabled and
79
- # client has to connect nodes via single endpoint with SSL/TLS
80
- # @option options [Class] :connector Class of custom connector
81
63
  #
82
64
  # @return [Redis] a new client instance
83
65
  def initialize(options = {})
84
- @options = options.dup
85
- @cluster_mode = options.key?(:cluster)
86
- client = @cluster_mode ? Cluster : Client
87
- @original_client = @client = client.new(options)
88
- @queue = Hash.new { |h, k| h[k] = [] }
89
66
  @monitor = Monitor.new
90
- end
91
-
92
- # Run code with the client reconnecting
93
- def with_reconnect(val = true, &blk)
94
- synchronize do |client|
95
- client.with_reconnect(val, &blk)
67
+ @options = options.dup
68
+ @options[:reconnect_attempts] = 1 unless @options.key?(:reconnect_attempts)
69
+ if ENV["REDIS_URL"] && SERVER_URL_OPTIONS.none? { |o| @options.key?(o) }
70
+ @options[:url] = ENV["REDIS_URL"]
96
71
  end
72
+ inherit_socket = @options.delete(:inherit_socket)
73
+ @subscription_client = nil
74
+
75
+ @client = initialize_client(@options)
76
+ @client.inherit_socket! if inherit_socket
97
77
  end
98
78
 
99
79
  # Run code without the client reconnecting
100
- def without_reconnect(&blk)
101
- with_reconnect(false, &blk)
80
+ def without_reconnect(&block)
81
+ @client.disable_reconnection(&block)
102
82
  end
103
83
 
104
84
  # Test whether or not the client is connected
105
85
  def connected?
106
- @original_client.connected?
86
+ @client.connected? || @subscription_client&.connected?
107
87
  end
108
88
 
109
89
  # Disconnect the client as quickly and silently as possible.
110
90
  def close
111
- @original_client.disconnect
91
+ @client.close
92
+ @subscription_client&.close
112
93
  end
113
94
  alias disconnect! close
114
95
 
@@ -116,127 +97,20 @@ class Redis
116
97
  yield self
117
98
  end
118
99
 
119
- # @deprecated Queues a command for pipelining.
120
- #
121
- # Commands in the queue are executed with the Redis#commit method.
122
- #
123
- # See http://redis.io/topics/pipelining for more details.
124
- #
125
- def queue(*command)
126
- ::Redis.deprecate!(
127
- "Redis#queue is deprecated and will be removed in Redis 5.0.0. Use Redis#pipelined instead." \
128
- "(called from: #{caller(1, 1).first})"
129
- )
130
-
131
- synchronize do
132
- @queue[Thread.current.object_id] << command
133
- end
134
- end
135
-
136
- # @deprecated Sends all commands in the queue.
137
- #
138
- # See http://redis.io/topics/pipelining for more details.
139
- #
140
- def commit
141
- ::Redis.deprecate!(
142
- "Redis#commit is deprecated and will be removed in Redis 5.0.0. Use Redis#pipelined instead. " \
143
- "(called from: #{Kernel.caller(1, 1).first})"
144
- )
145
-
146
- synchronize do |client|
147
- begin
148
- pipeline = Pipeline.new(client)
149
- @queue[Thread.current.object_id].each do |command|
150
- pipeline.call(command)
151
- end
152
-
153
- client.call_pipelined(pipeline)
154
- ensure
155
- @queue.delete(Thread.current.object_id)
156
- end
157
- end
158
- end
159
-
160
100
  def _client
161
101
  @client
162
102
  end
163
103
 
164
- def pipelined(&block)
165
- deprecation_displayed = false
166
- if block&.arity == 0
167
- Pipeline.deprecation_warning("pipelined", Kernel.caller_locations(1, 5))
168
- deprecation_displayed = true
169
- end
170
-
171
- synchronize do |prior_client|
172
- begin
173
- pipeline = Pipeline.new(prior_client)
174
- @client = deprecation_displayed ? pipeline : DeprecatedPipeline.new(pipeline)
175
- pipelined_connection = PipelinedConnection.new(pipeline)
176
- yield pipelined_connection
177
- prior_client.call_pipeline(pipeline)
178
- ensure
179
- @client = prior_client
180
- end
181
- end
182
- end
183
-
184
- # Mark the start of a transaction block.
185
- #
186
- # Passing a block is optional.
187
- #
188
- # @example With a block
189
- # redis.multi do |multi|
190
- # multi.set("key", "value")
191
- # multi.incr("counter")
192
- # end # => ["OK", 6]
193
- #
194
- # @example Without a block
195
- # redis.multi
196
- # # => "OK"
197
- # redis.set("key", "value")
198
- # # => "QUEUED"
199
- # redis.incr("counter")
200
- # # => "QUEUED"
201
- # redis.exec
202
- # # => ["OK", 6]
203
- #
204
- # @yield [multi] the commands that are called inside this block are cached
205
- # and written to the server upon returning from it
206
- # @yieldparam [Redis] multi `self`
207
- #
208
- # @return [String, Array<...>]
209
- # - when a block is not given, `OK`
210
- # - when a block is given, an array with replies
211
- #
212
- # @see #watch
213
- # @see #unwatch
214
- def multi(&block)
215
- if block_given?
216
- deprecation_displayed = false
217
- if block&.arity == 0
218
- Pipeline.deprecation_warning("multi", Kernel.caller_locations(1, 5))
219
- deprecation_displayed = true
220
- end
221
-
222
- synchronize do |prior_client|
223
- begin
224
- pipeline = Pipeline::Multi.new(prior_client)
225
- @client = deprecation_displayed ? pipeline : DeprecatedMulti.new(pipeline)
226
- pipelined_connection = PipelinedConnection.new(pipeline)
227
- yield pipelined_connection
228
- prior_client.call_pipeline(pipeline)
229
- ensure
230
- @client = prior_client
231
- end
104
+ def pipelined(exception: true)
105
+ synchronize do |client|
106
+ client.pipelined(exception: exception) do |raw_pipeline|
107
+ yield PipelinedConnection.new(raw_pipeline, exception: exception)
232
108
  end
233
- else
234
- send_command([:multi])
235
109
  end
236
110
  end
237
111
 
238
112
  def id
239
- @original_client.id
113
+ @client.id || @client.server_url
240
114
  end
241
115
 
242
116
  def inspect
@@ -248,54 +122,75 @@ class Redis
248
122
  end
249
123
 
250
124
  def connection
251
- return @original_client.connection_info if @cluster_mode
252
-
253
125
  {
254
- host: @original_client.host,
255
- port: @original_client.port,
256
- db: @original_client.db,
257
- id: @original_client.id,
258
- location: @original_client.location
126
+ host: @client.host,
127
+ port: @client.port,
128
+ db: @client.db,
129
+ id: id,
130
+ location: "#{@client.host}:#{@client.port}"
259
131
  }
260
132
  end
261
133
 
262
134
  private
263
135
 
136
+ def initialize_client(options)
137
+ if options.key?(:cluster)
138
+ raise "Redis Cluster support was moved to the `redis-clustering` gem."
139
+ end
140
+
141
+ if options.key?(:sentinels)
142
+ Client.sentinel(**options).new_client
143
+ else
144
+ Client.config(**options).new_client
145
+ end
146
+ end
147
+
264
148
  def synchronize
265
149
  @monitor.synchronize { yield(@client) }
266
150
  end
267
151
 
268
152
  def send_command(command, &block)
269
153
  @monitor.synchronize do
270
- @client.call(command, &block)
154
+ @client.call_v(command, &block)
271
155
  end
156
+ rescue ::RedisClient::Error => error
157
+ Client.translate_error!(error)
272
158
  end
273
159
 
274
160
  def send_blocking_command(command, timeout, &block)
275
161
  @monitor.synchronize do
276
- @client.call_with_timeout(command, timeout, &block)
162
+ @client.blocking_call_v(timeout, command, &block)
277
163
  end
278
164
  end
279
165
 
280
166
  def _subscription(method, timeout, channels, block)
281
- return @client.call([method] + channels) if subscribed?
167
+ if block
168
+ if @subscription_client
169
+ raise SubscriptionError, "This client is already subscribed"
170
+ end
282
171
 
283
- begin
284
- original, @client = @client, SubscribedClient.new(@client)
285
- if timeout > 0
286
- @client.send(method, timeout, *channels, &block)
287
- else
288
- @client.send(method, *channels, &block)
172
+ begin
173
+ @subscription_client = SubscribedClient.new(@client.pubsub)
174
+ if timeout > 0
175
+ @subscription_client.send(method, timeout, *channels, &block)
176
+ else
177
+ @subscription_client.send(method, *channels, &block)
178
+ end
179
+ ensure
180
+ @subscription_client&.close
181
+ @subscription_client = nil
182
+ end
183
+ else
184
+ unless @subscription_client
185
+ raise SubscriptionError, "This client is not subscribed"
289
186
  end
290
- ensure
291
- @client = original
187
+
188
+ @subscription_client.call_v([method].concat(channels))
292
189
  end
293
190
  end
294
191
  end
295
192
 
296
193
  require "redis/version"
297
- require "redis/connection"
298
194
  require "redis/client"
299
- require "redis/cluster"
300
195
  require "redis/pipeline"
301
196
  require "redis/subscribe"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.8.1
4
+ version: 5.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ezra Zygmuntowicz
@@ -13,53 +13,24 @@ authors:
13
13
  - Michel Martens
14
14
  - Damian Janowski
15
15
  - Pieter Noordhuis
16
- autorequire:
17
16
  bindir: bin
18
17
  cert_chain: []
19
- date: 2023-02-10 00:00:00.000000000 Z
18
+ date: 2025-07-17 00:00:00.000000000 Z
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
22
- name: em-synchrony
21
+ name: redis-client
23
22
  requirement: !ruby/object:Gem::Requirement
24
23
  requirements:
25
24
  - - ">="
26
25
  - !ruby/object:Gem::Version
27
- version: '0'
28
- type: :development
26
+ version: 0.22.0
27
+ type: :runtime
29
28
  prerelease: false
30
29
  version_requirements: !ruby/object:Gem::Requirement
31
30
  requirements:
32
31
  - - ">="
33
32
  - !ruby/object:Gem::Version
34
- version: '0'
35
- - !ruby/object:Gem::Dependency
36
- name: hiredis
37
- requirement: !ruby/object:Gem::Requirement
38
- requirements:
39
- - - ">="
40
- - !ruby/object:Gem::Version
41
- version: '0'
42
- type: :development
43
- prerelease: false
44
- version_requirements: !ruby/object:Gem::Requirement
45
- requirements:
46
- - - ">="
47
- - !ruby/object:Gem::Version
48
- version: '0'
49
- - !ruby/object:Gem::Dependency
50
- name: mocha
51
- requirement: !ruby/object:Gem::Requirement
52
- requirements:
53
- - - ">="
54
- - !ruby/object:Gem::Version
55
- version: '0'
56
- type: :development
57
- prerelease: false
58
- version_requirements: !ruby/object:Gem::Requirement
59
- requirements:
60
- - - ">="
61
- - !ruby/object:Gem::Version
62
- version: '0'
33
+ version: 0.22.0
63
34
  description: |2
64
35
  A Ruby client that tries to match Redis' API one-to-one, while still
65
36
  providing an idiomatic interface.
@@ -74,16 +45,6 @@ files:
74
45
  - README.md
75
46
  - lib/redis.rb
76
47
  - lib/redis/client.rb
77
- - lib/redis/cluster.rb
78
- - lib/redis/cluster/command.rb
79
- - lib/redis/cluster/command_loader.rb
80
- - lib/redis/cluster/key_slot_converter.rb
81
- - lib/redis/cluster/node.rb
82
- - lib/redis/cluster/node_key.rb
83
- - lib/redis/cluster/node_loader.rb
84
- - lib/redis/cluster/option.rb
85
- - lib/redis/cluster/slot.rb
86
- - lib/redis/cluster/slot_loader.rb
87
48
  - lib/redis/commands.rb
88
49
  - lib/redis/commands/bitmaps.rb
89
50
  - lib/redis/commands/cluster.rb
@@ -101,12 +62,6 @@ files:
101
62
  - lib/redis/commands/streams.rb
102
63
  - lib/redis/commands/strings.rb
103
64
  - lib/redis/commands/transactions.rb
104
- - lib/redis/connection.rb
105
- - lib/redis/connection/command_helper.rb
106
- - lib/redis/connection/hiredis.rb
107
- - lib/redis/connection/registry.rb
108
- - lib/redis/connection/ruby.rb
109
- - lib/redis/connection/synchrony.rb
110
65
  - lib/redis/distributed.rb
111
66
  - lib/redis/errors.rb
112
67
  - lib/redis/hash_ring.rb
@@ -119,10 +74,9 @@ licenses:
119
74
  metadata:
120
75
  bug_tracker_uri: https://github.com/redis/redis-rb/issues
121
76
  changelog_uri: https://github.com/redis/redis-rb/blob/master/CHANGELOG.md
122
- documentation_uri: https://www.rubydoc.info/gems/redis/4.8.1
77
+ documentation_uri: https://www.rubydoc.info/gems/redis/5.4.1
123
78
  homepage_uri: https://github.com/redis/redis-rb
124
- source_code_uri: https://github.com/redis/redis-rb/tree/v4.8.1
125
- post_install_message:
79
+ source_code_uri: https://github.com/redis/redis-rb/tree/v5.4.1
126
80
  rdoc_options: []
127
81
  require_paths:
128
82
  - lib
@@ -130,15 +84,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
130
84
  requirements:
131
85
  - - ">="
132
86
  - !ruby/object:Gem::Version
133
- version: 2.4.0
87
+ version: 2.6.0
134
88
  required_rubygems_version: !ruby/object:Gem::Requirement
135
89
  requirements:
136
90
  - - ">="
137
91
  - !ruby/object:Gem::Version
138
92
  version: '0'
139
93
  requirements: []
140
- rubygems_version: 3.1.2
141
- signing_key:
94
+ rubygems_version: 3.6.2
142
95
  specification_version: 4
143
96
  summary: A Ruby client library for Redis
144
97
  test_files: []
@@ -1,79 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../errors'
4
-
5
- class Redis
6
- class Cluster
7
- # Keep details about Redis commands for Redis Cluster Client.
8
- # @see https://redis.io/commands/command
9
- class Command
10
- def initialize(details)
11
- @details = pick_details(details)
12
- end
13
-
14
- def extract_first_key(command)
15
- i = determine_first_key_position(command)
16
- return '' if i == 0
17
-
18
- key = command[i].to_s
19
- hash_tag = extract_hash_tag(key)
20
- hash_tag.empty? ? key : hash_tag
21
- end
22
-
23
- def should_send_to_master?(command)
24
- dig_details(command, :write)
25
- end
26
-
27
- def should_send_to_slave?(command)
28
- dig_details(command, :readonly)
29
- end
30
-
31
- private
32
-
33
- def pick_details(details)
34
- details.transform_values do |detail|
35
- {
36
- first_key_position: detail[:first],
37
- write: detail[:flags].include?('write'),
38
- readonly: detail[:flags].include?('readonly')
39
- }
40
- end
41
- end
42
-
43
- def dig_details(command, key)
44
- name = command.first.to_s
45
- return unless @details.key?(name)
46
-
47
- @details.fetch(name).fetch(key)
48
- end
49
-
50
- def determine_first_key_position(command)
51
- case command.first.to_s.downcase
52
- when 'eval', 'evalsha', 'migrate', 'zinterstore', 'zunionstore' then 3
53
- when 'object' then 2
54
- when 'memory'
55
- command[1].to_s.casecmp('usage').zero? ? 2 : 0
56
- when 'xread', 'xreadgroup'
57
- determine_optional_key_position(command, 'streams')
58
- else
59
- dig_details(command, :first_key_position).to_i
60
- end
61
- end
62
-
63
- def determine_optional_key_position(command, option_name)
64
- idx = command.map(&:to_s).map(&:downcase).index(option_name)
65
- idx.nil? ? 0 : idx + 1
66
- end
67
-
68
- # @see https://redis.io/topics/cluster-spec#keys-hash-tags Keys hash tags
69
- def extract_hash_tag(key)
70
- s = key.index('{')
71
- e = key.index('}', s.to_i + 1)
72
-
73
- return '' if s.nil? || e.nil?
74
-
75
- key[s + 1..e - 1]
76
- end
77
- end
78
- end
79
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'redis/errors'
4
-
5
- class Redis
6
- class Cluster
7
- # Load details about Redis commands for Redis Cluster Client
8
- # @see https://redis.io/commands/command
9
- module CommandLoader
10
- module_function
11
-
12
- def load(nodes)
13
- errors = nodes.map do |node|
14
- begin
15
- return fetch_command_details(node)
16
- rescue CannotConnectError, ConnectionError, CommandError => error
17
- error
18
- end
19
- end
20
-
21
- raise InitialSetupError, errors
22
- end
23
-
24
- def fetch_command_details(node)
25
- node.call(%i[command]).map do |reply|
26
- [reply[0], { arity: reply[1], flags: reply[2], first: reply[3], last: reply[4], step: reply[5] }]
27
- end.to_h
28
- end
29
-
30
- private_class_method :fetch_command_details
31
- end
32
- end
33
- end
@@ -1,72 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Redis
4
- class Cluster
5
- # Key to slot converter for Redis Cluster Client
6
- #
7
- # We can test it by `CLUSTER KEYSLOT` command.
8
- #
9
- # @see https://github.com/antirez/redis-rb-cluster
10
- # Reference implementation in Ruby
11
- # @see https://redis.io/topics/cluster-spec#appendix
12
- # Reference implementation in ANSI C
13
- # @see https://redis.io/commands/cluster-keyslot
14
- # CLUSTER KEYSLOT command reference
15
- #
16
- # Copyright (C) 2013 Salvatore Sanfilippo <antirez@gmail.com>
17
- module KeySlotConverter
18
- XMODEM_CRC16_LOOKUP = [
19
- 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
20
- 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
21
- 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
22
- 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
23
- 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
24
- 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
25
- 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
26
- 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
27
- 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
28
- 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
29
- 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
30
- 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
31
- 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
32
- 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
33
- 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
34
- 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
35
- 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
36
- 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
37
- 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
38
- 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
39
- 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
40
- 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
41
- 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
42
- 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
43
- 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
44
- 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
45
- 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
46
- 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
47
- 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
48
- 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
49
- 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
50
- 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
51
- ].freeze
52
-
53
- HASH_SLOTS = 16_384
54
-
55
- module_function
56
-
57
- # Convert key into slot.
58
- #
59
- # @param key [String] the key of the redis command
60
- #
61
- # @return [Integer] slot number
62
- def convert(key)
63
- crc = 0
64
- key.each_byte do |b|
65
- crc = ((crc << 8) & 0xffff) ^ XMODEM_CRC16_LOOKUP[((crc >> 8) ^ b) & 0xff]
66
- end
67
-
68
- crc % HASH_SLOTS
69
- end
70
- end
71
- end
72
- end