redis 4.4.0 → 5.0.7

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +150 -0
  3. data/README.md +95 -160
  4. data/lib/redis/client.rb +84 -608
  5. data/lib/redis/commands/bitmaps.rb +66 -0
  6. data/lib/redis/commands/cluster.rb +28 -0
  7. data/lib/redis/commands/connection.rb +53 -0
  8. data/lib/redis/commands/geo.rb +84 -0
  9. data/lib/redis/commands/hashes.rb +254 -0
  10. data/lib/redis/commands/hyper_log_log.rb +37 -0
  11. data/lib/redis/commands/keys.rb +437 -0
  12. data/lib/redis/commands/lists.rb +339 -0
  13. data/lib/redis/commands/pubsub.rb +54 -0
  14. data/lib/redis/commands/scripting.rb +114 -0
  15. data/lib/redis/commands/server.rb +188 -0
  16. data/lib/redis/commands/sets.rb +214 -0
  17. data/lib/redis/commands/sorted_sets.rb +884 -0
  18. data/lib/redis/commands/streams.rb +402 -0
  19. data/lib/redis/commands/strings.rb +314 -0
  20. data/lib/redis/commands/transactions.rb +115 -0
  21. data/lib/redis/commands.rb +237 -0
  22. data/lib/redis/distributed.rb +208 -70
  23. data/lib/redis/errors.rb +15 -41
  24. data/lib/redis/hash_ring.rb +26 -26
  25. data/lib/redis/pipeline.rb +66 -120
  26. data/lib/redis/subscribe.rb +23 -15
  27. data/lib/redis/version.rb +1 -1
  28. data/lib/redis.rb +109 -3546
  29. metadata +27 -54
  30. data/lib/redis/cluster/command.rb +0 -81
  31. data/lib/redis/cluster/command_loader.rb +0 -34
  32. data/lib/redis/cluster/key_slot_converter.rb +0 -72
  33. data/lib/redis/cluster/node.rb +0 -108
  34. data/lib/redis/cluster/node_key.rb +0 -31
  35. data/lib/redis/cluster/node_loader.rb +0 -37
  36. data/lib/redis/cluster/option.rb +0 -93
  37. data/lib/redis/cluster/slot.rb +0 -86
  38. data/lib/redis/cluster/slot_loader.rb +0 -49
  39. data/lib/redis/cluster.rb +0 -291
  40. data/lib/redis/connection/command_helper.rb +0 -39
  41. data/lib/redis/connection/hiredis.rb +0 -67
  42. data/lib/redis/connection/registry.rb +0 -13
  43. data/lib/redis/connection/ruby.rb +0 -427
  44. data/lib/redis/connection/synchrony.rb +0 -146
  45. data/lib/redis/connection.rb +0 -11
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.4.0
4
+ version: 5.0.7
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: 2021-07-28 00:00:00.000000000 Z
19
+ date: 2023-08-09 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.9.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.9.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,22 +46,23 @@ 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
- - lib/redis/connection.rb
88
- - lib/redis/connection/command_helper.rb
89
- - lib/redis/connection/hiredis.rb
90
- - lib/redis/connection/registry.rb
91
- - lib/redis/connection/ruby.rb
92
- - lib/redis/connection/synchrony.rb
49
+ - lib/redis/commands.rb
50
+ - lib/redis/commands/bitmaps.rb
51
+ - lib/redis/commands/cluster.rb
52
+ - lib/redis/commands/connection.rb
53
+ - lib/redis/commands/geo.rb
54
+ - lib/redis/commands/hashes.rb
55
+ - lib/redis/commands/hyper_log_log.rb
56
+ - lib/redis/commands/keys.rb
57
+ - lib/redis/commands/lists.rb
58
+ - lib/redis/commands/pubsub.rb
59
+ - lib/redis/commands/scripting.rb
60
+ - lib/redis/commands/server.rb
61
+ - lib/redis/commands/sets.rb
62
+ - lib/redis/commands/sorted_sets.rb
63
+ - lib/redis/commands/streams.rb
64
+ - lib/redis/commands/strings.rb
65
+ - lib/redis/commands/transactions.rb
93
66
  - lib/redis/distributed.rb
94
67
  - lib/redis/errors.rb
95
68
  - lib/redis/hash_ring.rb
@@ -102,9 +75,9 @@ licenses:
102
75
  metadata:
103
76
  bug_tracker_uri: https://github.com/redis/redis-rb/issues
104
77
  changelog_uri: https://github.com/redis/redis-rb/blob/master/CHANGELOG.md
105
- documentation_uri: https://www.rubydoc.info/gems/redis/4.4.0
78
+ documentation_uri: https://www.rubydoc.info/gems/redis/5.0.7
106
79
  homepage_uri: https://github.com/redis/redis-rb
107
- source_code_uri: https://github.com/redis/redis-rb/tree/v4.4.0
80
+ source_code_uri: https://github.com/redis/redis-rb/tree/v5.0.7
108
81
  post_install_message:
109
82
  rdoc_options: []
110
83
  require_paths:
@@ -113,14 +86,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
113
86
  requirements:
114
87
  - - ">="
115
88
  - !ruby/object:Gem::Version
116
- version: 2.3.0
89
+ version: 2.5.0
117
90
  required_rubygems_version: !ruby/object:Gem::Requirement
118
91
  requirements:
119
92
  - - ">="
120
93
  - !ruby/object:Gem::Version
121
94
  version: '0'
122
95
  requirements: []
123
- rubygems_version: 3.1.2
96
+ rubygems_version: 3.3.7
124
97
  signing_key:
125
98
  specification_version: 4
126
99
  summary: A Ruby client library for Redis
@@ -1,81 +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.map do |command, detail|
35
- [command, {
36
- first_key_position: detail[:first],
37
- write: detail[:flags].include?('write'),
38
- readonly: detail[:flags].include?('readonly')
39
- }]
40
- end.to_h
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 'scan', 'sscan', 'hscan', 'zscan'
57
- determine_optional_key_position(command, 'match')
58
- when 'xread', 'xreadgroup'
59
- determine_optional_key_position(command, 'streams')
60
- else
61
- dig_details(command, :first_key_position).to_i
62
- end
63
- end
64
-
65
- def determine_optional_key_position(command, option_name)
66
- idx = command.map(&:to_s).map(&:downcase).index(option_name)
67
- idx.nil? ? 0 : idx + 1
68
- end
69
-
70
- # @see https://redis.io/topics/cluster-spec#keys-hash-tags Keys hash tags
71
- def extract_hash_tag(key)
72
- s = key.index('{')
73
- e = key.index('}', s.to_i + 1)
74
-
75
- return '' if s.nil? || e.nil?
76
-
77
- key[s + 1..e - 1]
78
- end
79
- end
80
- end
81
- end
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../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
- details = {}
14
-
15
- nodes.each do |node|
16
- details = fetch_command_details(node)
17
- details.empty? ? next : break
18
- end
19
-
20
- details
21
- end
22
-
23
- def fetch_command_details(node)
24
- node.call(%i[command]).map do |reply|
25
- [reply[0], { arity: reply[1], flags: reply[2], first: reply[3], last: reply[4], step: reply[5] }]
26
- end.to_h
27
- rescue CannotConnectError, ConnectionError, CommandError
28
- {} # can retry on another node
29
- end
30
-
31
- private_class_method :fetch_command_details
32
- end
33
- end
34
- 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
@@ -1,108 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../errors'
4
-
5
- class Redis
6
- class Cluster
7
- # Keep client list of node for Redis Cluster Client
8
- class Node
9
- include Enumerable
10
-
11
- ReloadNeeded = Class.new(StandardError)
12
-
13
- ROLE_SLAVE = 'slave'
14
-
15
- def initialize(options, node_flags = {}, with_replica = false)
16
- @with_replica = with_replica
17
- @node_flags = node_flags
18
- @clients = build_clients(options)
19
- end
20
-
21
- def each(&block)
22
- @clients.values.each(&block)
23
- end
24
-
25
- def sample
26
- @clients.values.sample
27
- end
28
-
29
- def find_by(node_key)
30
- @clients.fetch(node_key)
31
- rescue KeyError
32
- raise ReloadNeeded
33
- end
34
-
35
- def call_all(command, &block)
36
- try_map { |_, client| client.call(command, &block) }.values
37
- end
38
-
39
- def call_master(command, &block)
40
- try_map do |node_key, client|
41
- next if slave?(node_key)
42
-
43
- client.call(command, &block)
44
- end.values
45
- end
46
-
47
- def call_slave(command, &block)
48
- return call_master(command, &block) if replica_disabled?
49
-
50
- try_map do |node_key, client|
51
- next if master?(node_key)
52
-
53
- client.call(command, &block)
54
- end.values
55
- end
56
-
57
- def process_all(commands, &block)
58
- try_map { |_, client| client.process(commands, &block) }.values
59
- end
60
-
61
- private
62
-
63
- def replica_disabled?
64
- !@with_replica
65
- end
66
-
67
- def master?(node_key)
68
- !slave?(node_key)
69
- end
70
-
71
- def slave?(node_key)
72
- @node_flags[node_key] == ROLE_SLAVE
73
- end
74
-
75
- def build_clients(options)
76
- clients = options.map do |node_key, option|
77
- next if replica_disabled? && slave?(node_key)
78
-
79
- option = option.merge(readonly: true) if slave?(node_key)
80
-
81
- client = Client.new(option)
82
- [node_key, client]
83
- end
84
-
85
- clients.compact.to_h
86
- end
87
-
88
- def try_map
89
- errors = {}
90
- results = {}
91
-
92
- @clients.each do |node_key, client|
93
- begin
94
- reply = yield(node_key, client)
95
- results[node_key] = reply unless reply.nil?
96
- rescue CommandError => err
97
- errors[node_key] = err
98
- next
99
- end
100
- end
101
-
102
- return results if errors.empty?
103
-
104
- raise CommandErrorCollection, errors
105
- end
106
- end
107
- end
108
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Redis
4
- class Cluster
5
- # Node key's format is `<ip>:<port>`.
6
- # It is different from node id.
7
- # Node id is internal identifying code in Redis Cluster.
8
- module NodeKey
9
- DELIMITER = ':'
10
-
11
- module_function
12
-
13
- def optionize(node_key)
14
- host, port = split(node_key)
15
- { host: host, port: port }
16
- end
17
-
18
- def split(node_key)
19
- node_key.split(DELIMITER)
20
- end
21
-
22
- def build_from_uri(uri)
23
- "#{uri.host}#{DELIMITER}#{uri.port}"
24
- end
25
-
26
- def build_from_host_port(host, port)
27
- "#{host}#{DELIMITER}#{port}"
28
- end
29
- end
30
- end
31
- end
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../errors'
4
-
5
- class Redis
6
- class Cluster
7
- # Load and hashify node info for Redis Cluster Client
8
- module NodeLoader
9
- module_function
10
-
11
- def load_flags(nodes)
12
- info = {}
13
-
14
- nodes.each do |node|
15
- info = fetch_node_info(node)
16
- info.empty? ? next : break
17
- end
18
-
19
- return info unless info.empty?
20
-
21
- raise CannotConnectError, 'Redis client could not connect to any cluster nodes'
22
- end
23
-
24
- def fetch_node_info(node)
25
- node.call(%i[cluster nodes])
26
- .split("\n")
27
- .map { |str| str.split(' ') }
28
- .map { |arr| [arr[1].split('@').first, (arr[2].split(',') & %w[master slave]).first] }
29
- .to_h
30
- rescue CannotConnectError, ConnectionError, CommandError
31
- {} # can retry on another node
32
- end
33
-
34
- private_class_method :fetch_node_info
35
- end
36
- end
37
- end
@@ -1,93 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../errors'
4
- require_relative 'node_key'
5
- require 'uri'
6
-
7
- class Redis
8
- class Cluster
9
- # Keep options for Redis Cluster Client
10
- class Option
11
- DEFAULT_SCHEME = 'redis'
12
- SECURE_SCHEME = 'rediss'
13
- VALID_SCHEMES = [DEFAULT_SCHEME, SECURE_SCHEME].freeze
14
-
15
- def initialize(options)
16
- options = options.dup
17
- node_addrs = options.delete(:cluster)
18
- @node_opts = build_node_options(node_addrs)
19
- @replica = options.delete(:replica) == true
20
- add_common_node_option_if_needed(options, @node_opts, :scheme)
21
- add_common_node_option_if_needed(options, @node_opts, :username)
22
- add_common_node_option_if_needed(options, @node_opts, :password)
23
- @options = options
24
- end
25
-
26
- def per_node_key
27
- @node_opts.map { |opt| [NodeKey.build_from_host_port(opt[:host], opt[:port]), @options.merge(opt)] }
28
- .to_h
29
- end
30
-
31
- def use_replica?
32
- @replica
33
- end
34
-
35
- def update_node(addrs)
36
- @node_opts = build_node_options(addrs)
37
- end
38
-
39
- def add_node(host, port)
40
- @node_opts << { host: host, port: port }
41
- end
42
-
43
- private
44
-
45
- def build_node_options(addrs)
46
- raise InvalidClientOptionError, 'Redis option of `cluster` must be an Array' unless addrs.is_a?(Array)
47
-
48
- addrs.map { |addr| parse_node_addr(addr) }
49
- end
50
-
51
- def parse_node_addr(addr)
52
- case addr
53
- when String
54
- parse_node_url(addr)
55
- when Hash
56
- parse_node_option(addr)
57
- else
58
- raise InvalidClientOptionError, 'Redis option of `cluster` must includes String or Hash'
59
- end
60
- end
61
-
62
- def parse_node_url(addr)
63
- uri = URI(addr)
64
- raise InvalidClientOptionError, "Invalid uri scheme #{addr}" unless VALID_SCHEMES.include?(uri.scheme)
65
-
66
- db = uri.path.split('/')[1]&.to_i
67
-
68
- { scheme: uri.scheme, username: uri.user, password: uri.password, host: uri.host, port: uri.port, db: db }
69
- .reject { |_, v| v.nil? || v == '' }
70
- rescue URI::InvalidURIError => err
71
- raise InvalidClientOptionError, err.message
72
- end
73
-
74
- def parse_node_option(addr)
75
- addr = addr.map { |k, v| [k.to_sym, v] }.to_h
76
- if addr.values_at(:host, :port).any?(&:nil?)
77
- raise InvalidClientOptionError, 'Redis option of `cluster` must includes `:host` and `:port` keys'
78
- end
79
-
80
- addr
81
- end
82
-
83
- # Redis cluster node returns only host and port information.
84
- # So we should complement additional information such as:
85
- # scheme, username, password and so on.
86
- def add_common_node_option_if_needed(options, node_opts, key)
87
- return options if options[key].nil? && node_opts.first[key].nil?
88
-
89
- options[key] ||= node_opts.first[key]
90
- end
91
- end
92
- end
93
- end
@@ -1,86 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Redis
4
- class Cluster
5
- # Keep slot and node key map for Redis Cluster Client
6
- class Slot
7
- ROLE_SLAVE = 'slave'
8
-
9
- def initialize(available_slots, node_flags = {}, with_replica = false)
10
- @with_replica = with_replica
11
- @node_flags = node_flags
12
- @map = build_slot_node_key_map(available_slots)
13
- end
14
-
15
- def exists?(slot)
16
- @map.key?(slot)
17
- end
18
-
19
- def find_node_key_of_master(slot)
20
- return nil unless exists?(slot)
21
-
22
- @map[slot][:master]
23
- end
24
-
25
- def find_node_key_of_slave(slot)
26
- return nil unless exists?(slot)
27
- return find_node_key_of_master(slot) if replica_disabled?
28
-
29
- @map[slot][:slaves].sample
30
- end
31
-
32
- def put(slot, node_key)
33
- # Since we're sharing a hash for build_slot_node_key_map, duplicate it
34
- # if it already exists instead of preserving as-is.
35
- @map[slot] = @map[slot] ? @map[slot].dup : { master: nil, slaves: [] }
36
-
37
- if master?(node_key)
38
- @map[slot][:master] = node_key
39
- elsif !@map[slot][:slaves].include?(node_key)
40
- @map[slot][:slaves] << node_key
41
- end
42
-
43
- nil
44
- end
45
-
46
- private
47
-
48
- def replica_disabled?
49
- !@with_replica
50
- end
51
-
52
- def master?(node_key)
53
- !slave?(node_key)
54
- end
55
-
56
- def slave?(node_key)
57
- @node_flags[node_key] == ROLE_SLAVE
58
- end
59
-
60
- # available_slots is mapping of node_key to list of slot ranges
61
- def build_slot_node_key_map(available_slots)
62
- by_ranges = {}
63
- available_slots.each do |node_key, slots_arr|
64
- by_ranges[slots_arr] ||= { master: nil, slaves: [] }
65
-
66
- if master?(node_key)
67
- by_ranges[slots_arr][:master] = node_key
68
- elsif !by_ranges[slots_arr][:slaves].include?(node_key)
69
- by_ranges[slots_arr][:slaves] << node_key
70
- end
71
- end
72
-
73
- by_slot = {}
74
- by_ranges.each do |slots_arr, nodes|
75
- slots_arr.each do |slots|
76
- slots.each do |slot|
77
- by_slot[slot] = nodes
78
- end
79
- end
80
- end
81
-
82
- by_slot
83
- end
84
- end
85
- end
86
- end