redis 4.8.1 → 6.0.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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +138 -1
  3. data/README.md +285 -169
  4. data/lib/redis/client.rb +116 -611
  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 +109 -7
  9. data/lib/redis/commands/hashes.rb +179 -8
  10. data/lib/redis/commands/hyper_log_log.rb +1 -1
  11. data/lib/redis/commands/keys.rb +32 -24
  12. data/lib/redis/commands/lists.rb +167 -25
  13. data/lib/redis/commands/modules/json.rb +530 -0
  14. data/lib/redis/commands/modules/search/aggregation.rb +418 -0
  15. data/lib/redis/commands/modules/search/dialect.rb +14 -0
  16. data/lib/redis/commands/modules/search/field.rb +306 -0
  17. data/lib/redis/commands/modules/search/hybrid.rb +359 -0
  18. data/lib/redis/commands/modules/search/index.rb +351 -0
  19. data/lib/redis/commands/modules/search/index_definition.rb +114 -0
  20. data/lib/redis/commands/modules/search/miscellaneous.rb +607 -0
  21. data/lib/redis/commands/modules/search/query.rb +738 -0
  22. data/lib/redis/commands/modules/search/result.rb +488 -0
  23. data/lib/redis/commands/modules/search/schema.rb +211 -0
  24. data/lib/redis/commands/modules/search.rb +19 -0
  25. data/lib/redis/commands/pubsub.rb +34 -25
  26. data/lib/redis/commands/server.rb +15 -15
  27. data/lib/redis/commands/sets.rb +76 -40
  28. data/lib/redis/commands/sorted_sets.rb +128 -19
  29. data/lib/redis/commands/streams.rb +75 -28
  30. data/lib/redis/commands/strings.rb +18 -17
  31. data/lib/redis/commands/transactions.rb +7 -31
  32. data/lib/redis/commands.rb +39 -20
  33. data/lib/redis/distributed.rb +407 -73
  34. data/lib/redis/errors.rb +20 -50
  35. data/lib/redis/hash_ring.rb +26 -26
  36. data/lib/redis/lib_identity.rb +105 -0
  37. data/lib/redis/pipeline.rb +47 -222
  38. data/lib/redis/subscribe.rb +51 -15
  39. data/lib/redis/version.rb +1 -1
  40. data/lib/redis.rb +213 -188
  41. metadata +25 -59
  42. data/lib/redis/cluster/command.rb +0 -79
  43. data/lib/redis/cluster/command_loader.rb +0 -33
  44. data/lib/redis/cluster/key_slot_converter.rb +0 -72
  45. data/lib/redis/cluster/node.rb +0 -120
  46. data/lib/redis/cluster/node_key.rb +0 -31
  47. data/lib/redis/cluster/node_loader.rb +0 -34
  48. data/lib/redis/cluster/option.rb +0 -100
  49. data/lib/redis/cluster/slot.rb +0 -86
  50. data/lib/redis/cluster/slot_loader.rb +0 -46
  51. data/lib/redis/cluster.rb +0 -315
  52. data/lib/redis/connection/command_helper.rb +0 -41
  53. data/lib/redis/connection/hiredis.rb +0 -68
  54. data/lib/redis/connection/registry.rb +0 -13
  55. data/lib/redis/connection/ruby.rb +0 -437
  56. data/lib/redis/connection/synchrony.rb +0 -148
  57. 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.8.1
4
+ version: 6.0.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: 2023-02-10 00:00:00.000000000 Z
18
+ date: 2026-07-31 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.30.1
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.30.1
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
@@ -93,6 +54,18 @@ files:
93
54
  - lib/redis/commands/hyper_log_log.rb
94
55
  - lib/redis/commands/keys.rb
95
56
  - lib/redis/commands/lists.rb
57
+ - lib/redis/commands/modules/json.rb
58
+ - lib/redis/commands/modules/search.rb
59
+ - lib/redis/commands/modules/search/aggregation.rb
60
+ - lib/redis/commands/modules/search/dialect.rb
61
+ - lib/redis/commands/modules/search/field.rb
62
+ - lib/redis/commands/modules/search/hybrid.rb
63
+ - lib/redis/commands/modules/search/index.rb
64
+ - lib/redis/commands/modules/search/index_definition.rb
65
+ - lib/redis/commands/modules/search/miscellaneous.rb
66
+ - lib/redis/commands/modules/search/query.rb
67
+ - lib/redis/commands/modules/search/result.rb
68
+ - lib/redis/commands/modules/search/schema.rb
96
69
  - lib/redis/commands/pubsub.rb
97
70
  - lib/redis/commands/scripting.rb
98
71
  - lib/redis/commands/server.rb
@@ -101,15 +74,10 @@ files:
101
74
  - lib/redis/commands/streams.rb
102
75
  - lib/redis/commands/strings.rb
103
76
  - 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
77
  - lib/redis/distributed.rb
111
78
  - lib/redis/errors.rb
112
79
  - lib/redis/hash_ring.rb
80
+ - lib/redis/lib_identity.rb
113
81
  - lib/redis/pipeline.rb
114
82
  - lib/redis/subscribe.rb
115
83
  - lib/redis/version.rb
@@ -119,10 +87,9 @@ licenses:
119
87
  metadata:
120
88
  bug_tracker_uri: https://github.com/redis/redis-rb/issues
121
89
  changelog_uri: https://github.com/redis/redis-rb/blob/master/CHANGELOG.md
122
- documentation_uri: https://www.rubydoc.info/gems/redis/4.8.1
90
+ documentation_uri: https://www.rubydoc.info/gems/redis/6.0.0
123
91
  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:
92
+ source_code_uri: https://github.com/redis/redis-rb/tree/v6.0.0
126
93
  rdoc_options: []
127
94
  require_paths:
128
95
  - lib
@@ -130,15 +97,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
130
97
  requirements:
131
98
  - - ">="
132
99
  - !ruby/object:Gem::Version
133
- version: 2.4.0
100
+ version: 3.2.0
134
101
  required_rubygems_version: !ruby/object:Gem::Requirement
135
102
  requirements:
136
103
  - - ">="
137
104
  - !ruby/object:Gem::Version
138
105
  version: '0'
139
106
  requirements: []
140
- rubygems_version: 3.1.2
141
- signing_key:
107
+ rubygems_version: 3.6.2
142
108
  specification_version: 4
143
109
  summary: A Ruby client library for Redis
144
110
  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
@@ -1,120 +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
- def scale_reading_clients
62
- reading_clients = []
63
-
64
- @clients.each do |node_key, client|
65
- next unless replica_disabled? ? master?(node_key) : slave?(node_key)
66
-
67
- reading_clients << client
68
- end
69
-
70
- reading_clients
71
- end
72
-
73
- private
74
-
75
- def replica_disabled?
76
- !@with_replica
77
- end
78
-
79
- def master?(node_key)
80
- !slave?(node_key)
81
- end
82
-
83
- def slave?(node_key)
84
- @node_flags[node_key] == ROLE_SLAVE
85
- end
86
-
87
- def build_clients(options)
88
- clients = options.map do |node_key, option|
89
- next if replica_disabled? && slave?(node_key)
90
-
91
- option = option.merge(readonly: true) if slave?(node_key)
92
-
93
- client = Client.new(option)
94
- [node_key, client]
95
- end
96
-
97
- clients.compact.to_h
98
- end
99
-
100
- def try_map
101
- errors = {}
102
- results = {}
103
-
104
- @clients.each do |node_key, client|
105
- begin
106
- reply = yield(node_key, client)
107
- results[node_key] = reply unless reply.nil?
108
- rescue CommandError => err
109
- errors[node_key] = err
110
- next
111
- end
112
- end
113
-
114
- return results if errors.empty?
115
-
116
- raise CommandErrorCollection, errors
117
- end
118
- end
119
- end
120
- 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,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'redis/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
- errors = nodes.map do |node|
13
- begin
14
- return fetch_node_info(node)
15
- rescue CannotConnectError, ConnectionError, CommandError => error
16
- error
17
- end
18
- end
19
-
20
- raise InitialSetupError, errors
21
- end
22
-
23
- def fetch_node_info(node)
24
- node.call(%i[cluster nodes])
25
- .split("\n")
26
- .map { |str| str.split(' ') }
27
- .map { |arr| [arr[1].split('@').first, (arr[2].split(',') & %w[master slave]).first] }
28
- .to_h
29
- end
30
-
31
- private_class_method :fetch_node_info
32
- end
33
- end
34
- end
@@ -1,100 +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
- @fixed_hostname = options.delete(:fixed_hostname)
21
- add_common_node_option_if_needed(options, @node_opts, :scheme)
22
- add_common_node_option_if_needed(options, @node_opts, :username)
23
- add_common_node_option_if_needed(options, @node_opts, :password)
24
- @options = options
25
- end
26
-
27
- def per_node_key
28
- @node_opts.map do |opt|
29
- node_key = NodeKey.build_from_host_port(opt[:host], opt[:port])
30
- options = @options.merge(opt)
31
- options = options.merge(host: @fixed_hostname) if @fixed_hostname && !@fixed_hostname.empty?
32
- [node_key, options]
33
- end.to_h
34
- end
35
-
36
- def use_replica?
37
- @replica
38
- end
39
-
40
- def update_node(addrs)
41
- @node_opts = build_node_options(addrs)
42
- end
43
-
44
- def add_node(host, port)
45
- @node_opts << { host: host, port: port }
46
- end
47
-
48
- private
49
-
50
- def build_node_options(addrs)
51
- raise InvalidClientOptionError, 'Redis option of `cluster` must be an Array' unless addrs.is_a?(Array)
52
-
53
- addrs.map { |addr| parse_node_addr(addr) }
54
- end
55
-
56
- def parse_node_addr(addr)
57
- case addr
58
- when String
59
- parse_node_url(addr)
60
- when Hash
61
- parse_node_option(addr)
62
- else
63
- raise InvalidClientOptionError, 'Redis option of `cluster` must includes String or Hash'
64
- end
65
- end
66
-
67
- def parse_node_url(addr)
68
- uri = URI(addr)
69
- raise InvalidClientOptionError, "Invalid uri scheme #{addr}" unless VALID_SCHEMES.include?(uri.scheme)
70
-
71
- db = uri.path.split('/')[1]&.to_i
72
- username = uri.user ? URI.decode_www_form_component(uri.user) : nil
73
- password = uri.password ? URI.decode_www_form_component(uri.password) : nil
74
-
75
- { scheme: uri.scheme, username: username, password: password, host: uri.host, port: uri.port, db: db }
76
- .reject { |_, v| v.nil? || v == '' }
77
- rescue URI::InvalidURIError => err
78
- raise InvalidClientOptionError, err.message
79
- end
80
-
81
- def parse_node_option(addr)
82
- addr = addr.map { |k, v| [k.to_sym, v] }.to_h
83
- if addr.values_at(:host, :port).any?(&:nil?)
84
- raise InvalidClientOptionError, 'Redis option of `cluster` must includes `:host` and `:port` keys'
85
- end
86
-
87
- addr
88
- end
89
-
90
- # Redis cluster node returns only host and port information.
91
- # So we should complement additional information such as:
92
- # scheme, username, password and so on.
93
- def add_common_node_option_if_needed(options, node_opts, key)
94
- return options if options[key].nil? && node_opts.first[key].nil?
95
-
96
- options[key] ||= node_opts.first[key]
97
- end
98
- end
99
- end
100
- end