redis 4.7.1 → 5.4.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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +93 -0
  3. data/README.md +125 -162
  4. data/lib/redis/client.rb +82 -625
  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 +75 -27
  12. data/lib/redis/commands/lists.rb +73 -23
  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 +47 -36
  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 -14
  21. data/lib/redis/distributed.rb +150 -75
  22. data/lib/redis/errors.rb +15 -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 +76 -182
  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 -66
  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
@@ -6,26 +6,11 @@ require "redis/commands"
6
6
 
7
7
  class Redis
8
8
  BASE_PATH = __dir__
9
- @exists_returns_integer = true
10
-
11
9
  Deprecated = Class.new(StandardError)
12
10
 
13
11
  class << self
14
- attr_reader :exists_returns_integer
15
12
  attr_accessor :silence_deprecations, :raise_deprecations
16
13
 
17
- def exists_returns_integer=(value)
18
- unless value
19
- deprecate!(
20
- "`Redis#exists(key)` will return an Integer by default in redis-rb 4.3. The option to explicitly " \
21
- "disable this behaviour via `Redis.exists_returns_integer` will be removed in 5.0. You should use " \
22
- "`exists?` instead."
23
- )
24
- end
25
-
26
- @exists_returns_integer = value
27
- end
28
-
29
14
  def deprecate!(message)
30
15
  unless silence_deprecations
31
16
  if raise_deprecations
@@ -35,20 +20,22 @@ class Redis
35
20
  end
36
21
  end
37
22
  end
23
+ end
38
24
 
39
- def current
40
- deprecate!("`Redis.current` is deprecated and will be removed in 5.0. (called from: #{caller(1, 1).first})")
41
- @current ||= Redis.new
42
- end
43
-
44
- def current=(redis)
45
- deprecate!("`Redis.current=` is deprecated and will be removed in 5.0. (called from: #{caller(1, 1).first})")
46
- @current = redis
25
+ # soft-deprecated
26
+ # We added this back for older sidekiq releases
27
+ module Connection
28
+ class << self
29
+ def drivers
30
+ [RedisClient.default_driver]
31
+ end
47
32
  end
48
33
  end
49
34
 
50
35
  include Commands
51
36
 
37
+ SERVER_URL_OPTIONS = %i(url host port path).freeze
38
+
52
39
  # Create a new client instance
53
40
  #
54
41
  # @param [Hash] options
@@ -58,56 +45,49 @@ class Redis
58
45
  # @option options [String] :host ("127.0.0.1") server hostname
59
46
  # @option options [Integer] :port (6379) server port
60
47
  # @option options [String] :path path to server socket (overrides host and port)
61
- # @option options [Float] :timeout (5.0) timeout in seconds
48
+ # @option options [Float] :timeout (1.0) timeout in seconds
62
49
  # @option options [Float] :connect_timeout (same as timeout) timeout for initial connect in seconds
63
50
  # @option options [String] :username Username to authenticate against server
64
51
  # @option options [String] :password Password to authenticate against server
65
- # @option options [Integer] :db (0) Database to select after initial connect
66
- # @option options [Symbol] :driver Driver to use, currently supported: `:ruby`, `:hiredis`, `:synchrony`
52
+ # @option options [Integer] :db (0) Database to select after connect and on reconnects
53
+ # @option options [Symbol] :driver Driver to use, currently supported: `:ruby`, `:hiredis`
67
54
  # @option options [String] :id ID for the client connection, assigns name to current connection by sending
68
55
  # `CLIENT SETNAME`
69
- # @option options [Hash, Integer] :tcp_keepalive Keepalive values, if Integer `intvl` and `probe` are calculated
70
- # based on the value, if Hash `time`, `intvl` and `probes` can be specified as a Integer
71
- # @option options [Integer] :reconnect_attempts Number of attempts trying to connect
56
+ # @option options [Integer, Array<Integer, Float>] :reconnect_attempts Number of attempts trying to connect,
57
+ # or a list of sleep duration between attempts.
72
58
  # @option options [Boolean] :inherit_socket (false) Whether to use socket in forked process or not
59
+ # @option options [String] :name The name of the server group to connect to.
73
60
  # @option options [Array] :sentinels List of sentinels to contact
74
- # @option options [Symbol] :role (:master) Role to fetch via Sentinel, either `:master` or `:slave`
75
- # @option options [Array<String, Hash{Symbol => String, Integer}>] :cluster List of cluster nodes to contact
76
- # @option options [Boolean] :replica Whether to use readonly replica nodes in Redis Cluster or not
77
- # @option options [String] :fixed_hostname Specify a FQDN if cluster mode enabled and
78
- # client has to connect nodes via single endpoint with SSL/TLS
79
- # @option options [Class] :connector Class of custom connector
80
61
  #
81
62
  # @return [Redis] a new client instance
82
63
  def initialize(options = {})
83
- @options = options.dup
84
- @cluster_mode = options.key?(:cluster)
85
- client = @cluster_mode ? Cluster : Client
86
- @original_client = @client = client.new(options)
87
- @queue = Hash.new { |h, k| h[k] = [] }
88
64
  @monitor = Monitor.new
89
- end
90
-
91
- # Run code with the client reconnecting
92
- def with_reconnect(val = true, &blk)
93
- synchronize do |client|
94
- client.with_reconnect(val, &blk)
65
+ @options = options.dup
66
+ @options[:reconnect_attempts] = 1 unless @options.key?(:reconnect_attempts)
67
+ if ENV["REDIS_URL"] && SERVER_URL_OPTIONS.none? { |o| @options.key?(o) }
68
+ @options[:url] = ENV["REDIS_URL"]
95
69
  end
70
+ inherit_socket = @options.delete(:inherit_socket)
71
+ @subscription_client = nil
72
+
73
+ @client = initialize_client(@options)
74
+ @client.inherit_socket! if inherit_socket
96
75
  end
97
76
 
98
77
  # Run code without the client reconnecting
99
- def without_reconnect(&blk)
100
- with_reconnect(false, &blk)
78
+ def without_reconnect(&block)
79
+ @client.disable_reconnection(&block)
101
80
  end
102
81
 
103
82
  # Test whether or not the client is connected
104
83
  def connected?
105
- @original_client.connected?
84
+ @client.connected? || @subscription_client&.connected?
106
85
  end
107
86
 
108
87
  # Disconnect the client as quickly and silently as possible.
109
88
  def close
110
- @original_client.disconnect
89
+ @client.close
90
+ @subscription_client&.close
111
91
  end
112
92
  alias disconnect! close
113
93
 
@@ -115,127 +95,20 @@ class Redis
115
95
  yield self
116
96
  end
117
97
 
118
- # @deprecated Queues a command for pipelining.
119
- #
120
- # Commands in the queue are executed with the Redis#commit method.
121
- #
122
- # See http://redis.io/topics/pipelining for more details.
123
- #
124
- def queue(*command)
125
- ::Redis.deprecate!(
126
- "Redis#queue is deprecated and will be removed in Redis 5.0.0. Use Redis#pipelined instead." \
127
- "(called from: #{caller(1, 1).first})"
128
- )
129
-
130
- synchronize do
131
- @queue[Thread.current.object_id] << command
132
- end
133
- end
134
-
135
- # @deprecated Sends all commands in the queue.
136
- #
137
- # See http://redis.io/topics/pipelining for more details.
138
- #
139
- def commit
140
- ::Redis.deprecate!(
141
- "Redis#commit is deprecated and will be removed in Redis 5.0.0. Use Redis#pipelined instead. " \
142
- "(called from: #{Kernel.caller(1, 1).first})"
143
- )
144
-
145
- synchronize do |client|
146
- begin
147
- pipeline = Pipeline.new(client)
148
- @queue[Thread.current.object_id].each do |command|
149
- pipeline.call(command)
150
- end
151
-
152
- client.call_pipelined(pipeline)
153
- ensure
154
- @queue.delete(Thread.current.object_id)
155
- end
156
- end
157
- end
158
-
159
98
  def _client
160
99
  @client
161
100
  end
162
101
 
163
- def pipelined(&block)
164
- deprecation_displayed = false
165
- if block&.arity == 0
166
- Pipeline.deprecation_warning("pipelined", Kernel.caller_locations(1, 5))
167
- deprecation_displayed = true
168
- end
169
-
170
- synchronize do |prior_client|
171
- begin
172
- pipeline = Pipeline.new(prior_client)
173
- @client = deprecation_displayed ? pipeline : DeprecatedPipeline.new(pipeline)
174
- pipelined_connection = PipelinedConnection.new(pipeline)
175
- yield pipelined_connection
176
- prior_client.call_pipeline(pipeline)
177
- ensure
178
- @client = prior_client
179
- end
180
- end
181
- end
182
-
183
- # Mark the start of a transaction block.
184
- #
185
- # Passing a block is optional.
186
- #
187
- # @example With a block
188
- # redis.multi do |multi|
189
- # multi.set("key", "value")
190
- # multi.incr("counter")
191
- # end # => ["OK", 6]
192
- #
193
- # @example Without a block
194
- # redis.multi
195
- # # => "OK"
196
- # redis.set("key", "value")
197
- # # => "QUEUED"
198
- # redis.incr("counter")
199
- # # => "QUEUED"
200
- # redis.exec
201
- # # => ["OK", 6]
202
- #
203
- # @yield [multi] the commands that are called inside this block are cached
204
- # and written to the server upon returning from it
205
- # @yieldparam [Redis] multi `self`
206
- #
207
- # @return [String, Array<...>]
208
- # - when a block is not given, `OK`
209
- # - when a block is given, an array with replies
210
- #
211
- # @see #watch
212
- # @see #unwatch
213
- def multi(&block)
214
- if block_given?
215
- deprecation_displayed = false
216
- if block&.arity == 0
217
- Pipeline.deprecation_warning("multi", Kernel.caller_locations(1, 5))
218
- deprecation_displayed = true
219
- end
220
-
221
- synchronize do |prior_client|
222
- begin
223
- pipeline = Pipeline::Multi.new(prior_client)
224
- @client = deprecation_displayed ? pipeline : DeprecatedMulti.new(pipeline)
225
- pipelined_connection = PipelinedConnection.new(pipeline)
226
- yield pipelined_connection
227
- prior_client.call_pipeline(pipeline)
228
- ensure
229
- @client = prior_client
230
- end
102
+ def pipelined(exception: true)
103
+ synchronize do |client|
104
+ client.pipelined(exception: exception) do |raw_pipeline|
105
+ yield PipelinedConnection.new(raw_pipeline, exception: exception)
231
106
  end
232
- else
233
- send_command([:multi])
234
107
  end
235
108
  end
236
109
 
237
110
  def id
238
- @original_client.id
111
+ @client.id || @client.server_url
239
112
  end
240
113
 
241
114
  def inspect
@@ -247,54 +120,75 @@ class Redis
247
120
  end
248
121
 
249
122
  def connection
250
- return @original_client.connection_info if @cluster_mode
251
-
252
123
  {
253
- host: @original_client.host,
254
- port: @original_client.port,
255
- db: @original_client.db,
256
- id: @original_client.id,
257
- location: @original_client.location
124
+ host: @client.host,
125
+ port: @client.port,
126
+ db: @client.db,
127
+ id: id,
128
+ location: "#{@client.host}:#{@client.port}"
258
129
  }
259
130
  end
260
131
 
261
132
  private
262
133
 
134
+ def initialize_client(options)
135
+ if options.key?(:cluster)
136
+ raise "Redis Cluster support was moved to the `redis-clustering` gem."
137
+ end
138
+
139
+ if options.key?(:sentinels)
140
+ Client.sentinel(**options).new_client
141
+ else
142
+ Client.config(**options).new_client
143
+ end
144
+ end
145
+
263
146
  def synchronize
264
147
  @monitor.synchronize { yield(@client) }
265
148
  end
266
149
 
267
150
  def send_command(command, &block)
268
151
  @monitor.synchronize do
269
- @client.call(command, &block)
152
+ @client.call_v(command, &block)
270
153
  end
154
+ rescue ::RedisClient::Error => error
155
+ Client.translate_error!(error)
271
156
  end
272
157
 
273
158
  def send_blocking_command(command, timeout, &block)
274
159
  @monitor.synchronize do
275
- @client.call_with_timeout(command, timeout, &block)
160
+ @client.blocking_call_v(timeout, command, &block)
276
161
  end
277
162
  end
278
163
 
279
164
  def _subscription(method, timeout, channels, block)
280
- return @client.call([method] + channels) if subscribed?
165
+ if block
166
+ if @subscription_client
167
+ raise SubscriptionError, "This client is already subscribed"
168
+ end
281
169
 
282
- begin
283
- original, @client = @client, SubscribedClient.new(@client)
284
- if timeout > 0
285
- @client.send(method, timeout, *channels, &block)
286
- else
287
- @client.send(method, *channels, &block)
170
+ begin
171
+ @subscription_client = SubscribedClient.new(@client.pubsub)
172
+ if timeout > 0
173
+ @subscription_client.send(method, timeout, *channels, &block)
174
+ else
175
+ @subscription_client.send(method, *channels, &block)
176
+ end
177
+ ensure
178
+ @subscription_client&.close
179
+ @subscription_client = nil
180
+ end
181
+ else
182
+ unless @subscription_client
183
+ raise SubscriptionError, "This client is not subscribed"
288
184
  end
289
- ensure
290
- @client = original
185
+
186
+ @subscription_client.call_v([method].concat(channels))
291
187
  end
292
188
  end
293
189
  end
294
190
 
295
191
  require "redis/version"
296
- require "redis/connection"
297
192
  require "redis/client"
298
- require "redis/cluster"
299
193
  require "redis/pipeline"
300
194
  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.7.1
4
+ version: 5.4.0
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: 2022-07-01 00:00:00.000000000 Z
18
+ date: 2025-02-20 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.7.1
77
+ documentation_uri: https://www.rubydoc.info/gems/redis/5.4.0
123
78
  homepage_uri: https://github.com/redis/redis-rb
124
- source_code_uri: https://github.com/redis/redis-rb/tree/v4.7.1
125
- post_install_message:
79
+ source_code_uri: https://github.com/redis/redis-rb/tree/v5.4.0
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