redis 4.8.0 → 5.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +64 -1
  3. data/README.md +101 -161
  4. data/lib/redis/client.rb +82 -625
  5. data/lib/redis/commands/bitmaps.rb +4 -1
  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 +9 -6
  10. data/lib/redis/commands/hyper_log_log.rb +1 -1
  11. data/lib/redis/commands/keys.rb +5 -23
  12. data/lib/redis/commands/lists.rb +74 -25
  13. data/lib/redis/commands/pubsub.rb +28 -25
  14. data/lib/redis/commands/server.rb +15 -15
  15. data/lib/redis/commands/sets.rb +31 -40
  16. data/lib/redis/commands/sorted_sets.rb +84 -12
  17. data/lib/redis/commands/streams.rb +39 -19
  18. data/lib/redis/commands/strings.rb +18 -17
  19. data/lib/redis/commands/transactions.rb +7 -31
  20. data/lib/redis/commands.rb +4 -7
  21. data/lib/redis/distributed.rb +114 -64
  22. data/lib/redis/errors.rb +15 -50
  23. data/lib/redis/hash_ring.rb +26 -26
  24. data/lib/redis/pipeline.rb +43 -222
  25. data/lib/redis/subscribe.rb +50 -14
  26. data/lib/redis/version.rb +1 -1
  27. data/lib/redis.rb +76 -184
  28. metadata +10 -54
  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
@@ -6,26 +6,10 @@ require "redis/commands"
6
6
 
7
7
  class Redis
8
8
  BASE_PATH = __dir__
9
- @exists_returns_integer = true
10
- @sadd_returns_boolean = true
11
-
12
9
  Deprecated = Class.new(StandardError)
13
10
 
14
11
  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
12
+ attr_accessor :silence_deprecations, :raise_deprecations
29
13
 
30
14
  def deprecate!(message)
31
15
  unless silence_deprecations
@@ -36,20 +20,22 @@ class Redis
36
20
  end
37
21
  end
38
22
  end
23
+ end
39
24
 
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
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
48
32
  end
49
33
  end
50
34
 
51
35
  include Commands
52
36
 
37
+ SERVER_URL_OPTIONS = %i(url host port path).freeze
38
+
53
39
  # Create a new client instance
54
40
  #
55
41
  # @param [Hash] options
@@ -59,56 +45,49 @@ class Redis
59
45
  # @option options [String] :host ("127.0.0.1") server hostname
60
46
  # @option options [Integer] :port (6379) server port
61
47
  # @option options [String] :path path to server socket (overrides host and port)
62
- # @option options [Float] :timeout (5.0) timeout in seconds
48
+ # @option options [Float] :timeout (1.0) timeout in seconds
63
49
  # @option options [Float] :connect_timeout (same as timeout) timeout for initial connect in seconds
64
50
  # @option options [String] :username Username to authenticate against server
65
51
  # @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`
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`
68
54
  # @option options [String] :id ID for the client connection, assigns name to current connection by sending
69
55
  # `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
56
+ # @option options [Integer, Array<Integer, Float>] :reconnect_attempts Number of attempts trying to connect,
57
+ # or a list of sleep duration between attempts.
73
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.
74
60
  # @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
61
  #
82
62
  # @return [Redis] a new client instance
83
63
  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
64
  @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)
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"]
96
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
97
75
  end
98
76
 
99
77
  # Run code without the client reconnecting
100
- def without_reconnect(&blk)
101
- with_reconnect(false, &blk)
78
+ def without_reconnect(&block)
79
+ @client.disable_reconnection(&block)
102
80
  end
103
81
 
104
82
  # Test whether or not the client is connected
105
83
  def connected?
106
- @original_client.connected?
84
+ @client.connected? || @subscription_client&.connected?
107
85
  end
108
86
 
109
87
  # Disconnect the client as quickly and silently as possible.
110
88
  def close
111
- @original_client.disconnect
89
+ @client.close
90
+ @subscription_client&.close
112
91
  end
113
92
  alias disconnect! close
114
93
 
@@ -116,127 +95,20 @@ class Redis
116
95
  yield self
117
96
  end
118
97
 
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
98
  def _client
161
99
  @client
162
100
  end
163
101
 
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
102
+ def pipelined
103
+ synchronize do |client|
104
+ client.pipelined do |raw_pipeline|
105
+ yield PipelinedConnection.new(raw_pipeline)
232
106
  end
233
- else
234
- send_command([:multi])
235
107
  end
236
108
  end
237
109
 
238
110
  def id
239
- @original_client.id
111
+ @client.id || @client.server_url
240
112
  end
241
113
 
242
114
  def inspect
@@ -248,54 +120,74 @@ class Redis
248
120
  end
249
121
 
250
122
  def connection
251
- return @original_client.connection_info if @cluster_mode
252
-
253
123
  {
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
124
+ host: @client.host,
125
+ port: @client.port,
126
+ db: @client.db,
127
+ id: id,
128
+ location: "#{@client.host}:#{@client.port}"
259
129
  }
260
130
  end
261
131
 
262
132
  private
263
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
+
264
146
  def synchronize
265
147
  @monitor.synchronize { yield(@client) }
266
148
  end
267
149
 
268
150
  def send_command(command, &block)
269
151
  @monitor.synchronize do
270
- @client.call(command, &block)
152
+ @client.call_v(command, &block)
271
153
  end
154
+ rescue ::RedisClient::Error => error
155
+ Client.translate_error!(error)
272
156
  end
273
157
 
274
158
  def send_blocking_command(command, timeout, &block)
275
159
  @monitor.synchronize do
276
- @client.call_with_timeout(command, timeout, &block)
160
+ @client.blocking_call_v(timeout, command, &block)
277
161
  end
278
162
  end
279
163
 
280
164
  def _subscription(method, timeout, channels, block)
281
- return @client.call([method] + channels) if subscribed?
165
+ if block
166
+ if @subscription_client
167
+ raise SubscriptionError, "This client is already subscribed"
168
+ end
282
169
 
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)
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 = nil
179
+ end
180
+ else
181
+ unless @subscription_client
182
+ raise SubscriptionError, "This client is not subscribed"
289
183
  end
290
- ensure
291
- @client = original
184
+
185
+ @subscription_client.call_v([method].concat(channels))
292
186
  end
293
187
  end
294
188
  end
295
189
 
296
190
  require "redis/version"
297
- require "redis/connection"
298
191
  require "redis/client"
299
- require "redis/cluster"
300
192
  require "redis/pipeline"
301
193
  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.0
4
+ version: 5.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ezra Zygmuntowicz
@@ -16,50 +16,22 @@ authors:
16
16
  autorequire:
17
17
  bindir: bin
18
18
  cert_chain: []
19
- date: 2022-08-22 00:00:00.000000000 Z
19
+ date: 2023-10-23 00:00:00.000000000 Z
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
- name: em-synchrony
22
+ name: redis-client
23
23
  requirement: !ruby/object:Gem::Requirement
24
24
  requirements:
25
25
  - - ">="
26
26
  - !ruby/object:Gem::Version
27
- version: '0'
28
- type: :development
27
+ version: 0.17.0
28
+ type: :runtime
29
29
  prerelease: false
30
30
  version_requirements: !ruby/object:Gem::Requirement
31
31
  requirements:
32
32
  - - ">="
33
33
  - !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'
34
+ version: 0.17.0
63
35
  description: |2
64
36
  A Ruby client that tries to match Redis' API one-to-one, while still
65
37
  providing an idiomatic interface.
@@ -74,16 +46,6 @@ files:
74
46
  - README.md
75
47
  - lib/redis.rb
76
48
  - 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
49
  - lib/redis/commands.rb
88
50
  - lib/redis/commands/bitmaps.rb
89
51
  - lib/redis/commands/cluster.rb
@@ -101,12 +63,6 @@ files:
101
63
  - lib/redis/commands/streams.rb
102
64
  - lib/redis/commands/strings.rb
103
65
  - 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
66
  - lib/redis/distributed.rb
111
67
  - lib/redis/errors.rb
112
68
  - lib/redis/hash_ring.rb
@@ -119,9 +75,9 @@ licenses:
119
75
  metadata:
120
76
  bug_tracker_uri: https://github.com/redis/redis-rb/issues
121
77
  changelog_uri: https://github.com/redis/redis-rb/blob/master/CHANGELOG.md
122
- documentation_uri: https://www.rubydoc.info/gems/redis/4.8.0
78
+ documentation_uri: https://www.rubydoc.info/gems/redis/5.0.8
123
79
  homepage_uri: https://github.com/redis/redis-rb
124
- source_code_uri: https://github.com/redis/redis-rb/tree/v4.8.0
80
+ source_code_uri: https://github.com/redis/redis-rb/tree/v5.0.8
125
81
  post_install_message:
126
82
  rdoc_options: []
127
83
  require_paths:
@@ -130,14 +86,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
130
86
  requirements:
131
87
  - - ">="
132
88
  - !ruby/object:Gem::Version
133
- version: 2.4.0
89
+ version: 2.5.0
134
90
  required_rubygems_version: !ruby/object:Gem::Requirement
135
91
  requirements:
136
92
  - - ">="
137
93
  - !ruby/object:Gem::Version
138
94
  version: '0'
139
95
  requirements: []
140
- rubygems_version: 3.1.2
96
+ rubygems_version: 3.3.7
141
97
  signing_key:
142
98
  specification_version: 4
143
99
  summary: A Ruby client library for Redis
@@ -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