redis-cluster-client 0.16.1 → 0.16.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e90bf81ffcf5eb68e78018e950813bdb727274c17157088645e1d58d763252a9
4
- data.tar.gz: d4185f0e3d0b7d5069a9e156c607d39d0d10bd348f602a13ab53a68fa716e9fe
3
+ metadata.gz: 5c16432e083671229128632c116401dc9b1bd56f6b3b1e3c8f107956901d29f2
4
+ data.tar.gz: b464a6f87e8c952a5e4f5b22d0609a9be09b3c6c7817eb91ed779e0b142a4290
5
5
  SHA512:
6
- metadata.gz: e30aa213ff6f7be6e70bcc2bc7329b8f0d9358f98169cea311c9e2afa66bf3fc28db5e8eb84776b09564ab7095d08c7744405065e7fbd2bf8fec483b4ddfba67
7
- data.tar.gz: 5c8952e4eccce33fc59697e42bb135ecaaa25241e5491e1c854f9fb1f42b9ef03ae9044c83add3d21d4a2ac755d6d1d6f11eca1c7d6c41f47f7a31b15cc03d22
6
+ metadata.gz: 0b94204a193f0bfe56c75da819651c8c16c72bd9620c51996f2dcfc8b2c7faefc416c342ca7631772c96d4fec7a9e7c8aede9638c28fb540855603b829a1042d
7
+ data.tar.gz: 6d873858848aa00124a043efce6ee8fdd629516e8c5447802b079636de1dd4782b0584c84d791ed0bc7c589d66c273e985878cdea238054e60f1a5a5a2ce72c9
@@ -9,18 +9,57 @@ class RedisClient
9
9
  class Command
10
10
  EMPTY_STRING = ''
11
11
  EMPTY_HASH = {}.freeze
12
- EMPTY_ARRAY = [].freeze
13
12
 
14
- private_constant :EMPTY_STRING, :EMPTY_HASH, :EMPTY_ARRAY
13
+ private_constant :EMPTY_HASH
15
14
 
16
- Detail = Struct.new(
15
+ Spec = Struct.new(
17
16
  'RedisCommand',
18
17
  :first_key_position,
19
18
  :key_step,
20
19
  :write?,
21
20
  :readonly?,
22
21
  keyword_init: true
23
- )
22
+ ) do
23
+ def extract_first_key(command)
24
+ i = first_key_position.to_i
25
+ return command[i] if i > 0
26
+
27
+ i = determine_first_key_position(command)
28
+ return ::RedisClient::Cluster::Command::EMPTY_STRING if i == 0
29
+
30
+ command[i]
31
+ end
32
+
33
+ def should_send_to_primary?
34
+ write?
35
+ end
36
+
37
+ def should_send_to_replica?
38
+ readonly?
39
+ end
40
+
41
+ private
42
+
43
+ def determine_first_key_position(command) # rubocop:disable Metrics/AbcSize
44
+ cmd_name = command.first
45
+ if cmd_name.casecmp('xread').zero?
46
+ determine_optional_key_position(command, 'streams')
47
+ elsif cmd_name.casecmp('xreadgroup').zero?
48
+ determine_optional_key_position(command, 'streams')
49
+ elsif cmd_name.casecmp('migrate').zero?
50
+ command[3].empty? ? determine_optional_key_position(command, 'keys') : 3
51
+ elsif cmd_name.casecmp('memory').zero?
52
+ command[1].to_s.casecmp('usage').zero? ? 2 : 0
53
+ else
54
+ 0
55
+ end
56
+ end
57
+
58
+ def determine_optional_key_position(command, option_name)
59
+ i = command.index { |v| v.to_s.casecmp(option_name).zero? }
60
+ i.nil? ? 0 : i + 1
61
+ end
62
+ end
24
63
 
25
64
  class << self
26
65
  def load(nodes, slow_command_timeout: -1) # rubocop:disable Metrics/AbcSize
@@ -30,13 +69,14 @@ class RedisClient
30
69
  regular_timeout = node.read_timeout
31
70
  node.read_timeout = slow_command_timeout > 0.0 ? slow_command_timeout : regular_timeout
32
71
  reply = node.call('command')
33
- node.read_timeout = regular_timeout
34
72
  commands = parse_command_reply(reply)
35
73
  cmd = ::RedisClient::Cluster::Command.new(commands)
36
74
  break
37
75
  rescue ::RedisClient::Error => e
38
76
  errors ||= []
39
77
  errors << e
78
+ ensure
79
+ node.read_timeout = regular_timeout
40
80
  end
41
81
 
42
82
  return cmd unless cmd.nil?
@@ -64,7 +104,7 @@ class RedisClient
64
104
  else row[2].include?('write')
65
105
  end
66
106
 
67
- acc[row.first] = ::RedisClient::Cluster::Command::Detail.new(
107
+ acc[row.first] = ::RedisClient::Cluster::Command::Spec.new(
68
108
  first_key_position: pos,
69
109
  key_step: row[5],
70
110
  write?: writable,
@@ -78,53 +118,13 @@ class RedisClient
78
118
  @commands = commands || EMPTY_HASH
79
119
  end
80
120
 
81
- def extract_first_key(command)
82
- i = determine_first_key_position(command)
83
- return EMPTY_STRING if i == 0
84
-
85
- command[i]
86
- end
87
-
88
- def should_send_to_primary?(command)
89
- find_command_info(command.first)&.write?
90
- end
91
-
92
- def should_send_to_replica?(command)
93
- find_command_info(command.first)&.readonly?
121
+ def get_spec(name)
122
+ @commands[name] || @commands[name.to_s.downcase(:ascii)]
94
123
  end
95
124
 
96
125
  def exists?(name)
97
126
  @commands.key?(name) || @commands.key?(name.to_s.downcase(:ascii))
98
127
  end
99
-
100
- private
101
-
102
- def find_command_info(name)
103
- @commands[name] || @commands[name.to_s.downcase(:ascii)]
104
- end
105
-
106
- def determine_first_key_position(command) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/PerceivedComplexity
107
- i = find_command_info(command.first)&.first_key_position.to_i
108
- return i if i > 0
109
-
110
- cmd_name = command.first
111
- if cmd_name.casecmp('xread').zero?
112
- determine_optional_key_position(command, 'streams')
113
- elsif cmd_name.casecmp('xreadgroup').zero?
114
- determine_optional_key_position(command, 'streams')
115
- elsif cmd_name.casecmp('migrate').zero?
116
- command[3].empty? ? determine_optional_key_position(command, 'keys') : 3
117
- elsif cmd_name.casecmp('memory').zero?
118
- command[1].to_s.casecmp('usage').zero? ? 2 : 0
119
- else
120
- i
121
- end
122
- end
123
-
124
- def determine_optional_key_position(command, option_name)
125
- i = command.index { |v| v.to_s.casecmp(option_name).zero? }
126
- i.nil? ? 0 : i + 1
127
- end
128
128
  end
129
129
  end
130
130
  end
@@ -3,17 +3,57 @@
3
3
  class RedisClient
4
4
  class Cluster
5
5
  module ConcurrentWorker
6
- class None
7
- def new_group(size:)
8
- ::RedisClient::Cluster::ConcurrentWorker::Group.new(
9
- worker: self,
10
- queue: [],
11
- size: size
6
+ module None
7
+ class Group
8
+ Task = Struct.new(
9
+ 'RedisClusterClientSingleThreadTask',
10
+ :id, :result, keyword_init: true
12
11
  )
12
+
13
+ def initialize(size:)
14
+ @tasks = Array.new(size)
15
+ @idx = 0
16
+ end
17
+
18
+ def push(id, *args, **kwargs, &block)
19
+ raise InvalidNumberOfTasks, "max size reached: #{@idx}" if @idx == @tasks.size
20
+
21
+ result = exec(*args, **kwargs, &block)
22
+ @tasks[@idx] = Task.new(id: id, result: result)
23
+ @idx += 1
24
+ nil
25
+ end
26
+
27
+ def each
28
+ raise InvalidNumberOfTasks, "expected: #{@tasks.size}, actual: #{@idx}" if @idx != @tasks.size
29
+
30
+ @tasks.each { |task| yield(task.id, task.result) }
31
+ nil
32
+ end
33
+
34
+ def close
35
+ @idx = 0
36
+ @tasks.clear
37
+ nil
38
+ end
39
+
40
+ def inspect
41
+ "#<#{self.class.name} size: #{@idx}, max: #{@tasks.size}>"
42
+ end
43
+
44
+ private
45
+
46
+ def exec(*args, **kwargs, &block)
47
+ block&.call(*args, **kwargs)
48
+ rescue StandardError => e
49
+ e
50
+ end
13
51
  end
14
52
 
15
- def push(task)
16
- task.exec
53
+ module_function
54
+
55
+ def new_group(size:)
56
+ Group.new(size: size)
17
57
  end
18
58
 
19
59
  def close; end
@@ -73,7 +73,7 @@ class RedisClient
73
73
 
74
74
  def create(model: :none, size: 5)
75
75
  case model
76
- when :none then ::RedisClient::Cluster::ConcurrentWorker::None.new
76
+ when :none then ::RedisClient::Cluster::ConcurrentWorker::None
77
77
  when :on_demand then ::RedisClient::Cluster::ConcurrentWorker::OnDemand.new(size: size)
78
78
  when :pooled then ::RedisClient::Cluster::ConcurrentWorker::Pooled.new(size: size)
79
79
  else raise ArgumentError, "unknown model: #{model}"
@@ -25,8 +25,7 @@ class RedisClient
25
25
  end
26
26
 
27
27
  def any_primary_node_key(seed: nil)
28
- random = seed.nil? ? Random : Random.new(seed)
29
- @primary_node_keys.sample(random: random)
28
+ @primary_node_keys.sample(random: make_random(seed))
30
29
  end
31
30
 
32
31
  def process_topology_update!(replications, options) # rubocop:disable Metrics/AbcSize
@@ -59,6 +58,11 @@ class RedisClient
59
58
  @clients[node_key] = client
60
59
  end
61
60
  end
61
+
62
+ def make_random(seed)
63
+ # OPTIMIZE: Figure out the most elegant way to pin a node during a pipeline or scan.
64
+ seed.nil? ? Random : Random.new(seed)
65
+ end
62
66
  end
63
67
  end
64
68
  end
@@ -20,8 +20,7 @@ class RedisClient
20
20
  end
21
21
 
22
22
  def any_replica_node_key(seed: nil)
23
- random = seed.nil? ? Random : Random.new(seed)
24
- @existed_replicas.sample(random: random)&.first || any_primary_node_key(seed: seed)
23
+ @existed_replicas.sample(random: make_random(seed))&.first || any_primary_node_key(seed: seed)
25
24
  end
26
25
 
27
26
  def process_topology_update!(replications, options)
@@ -18,8 +18,7 @@ class RedisClient
18
18
  end
19
19
 
20
20
  def any_primary_node_key(seed: nil)
21
- random = seed.nil? ? Random : Random.new(seed)
22
- @primary_node_keys.sample(random: random)
21
+ @primary_node_keys.sample(random: make_random(seed))
23
22
  end
24
23
 
25
24
  alias any_replica_node_key any_primary_node_key
@@ -12,7 +12,7 @@ class RedisClient
12
12
  end
13
13
 
14
14
  def clients_for_scanning(seed: nil)
15
- random = seed.nil? ? Random : Random.new(seed)
15
+ random = make_random(seed)
16
16
  keys = @replications.map do |primary_node_key, replica_node_keys|
17
17
  replica_node_keys.empty? ? primary_node_key : replica_node_keys.sample(random: random)
18
18
  end
@@ -21,13 +21,13 @@ class RedisClient
21
21
  end
22
22
 
23
23
  def find_node_key_of_replica(primary_node_key, seed: nil)
24
- random = seed.nil? ? Random : Random.new(seed)
25
- @replications.fetch(primary_node_key, EMPTY_ARRAY).sample(random: random) || primary_node_key
24
+ replica_node_keys = @replications.fetch(primary_node_key, EMPTY_ARRAY)
25
+ replica_node_key = replica_node_keys.size <= 1 ? replica_node_keys.first : replica_node_keys.sample(random: make_random(seed))
26
+ replica_node_key || primary_node_key
26
27
  end
27
28
 
28
29
  def any_replica_node_key(seed: nil)
29
- random = seed.nil? ? Random : Random.new(seed)
30
- @replica_node_keys.sample(random: random) || any_primary_node_key(seed: seed)
30
+ @replica_node_keys.sample(random: make_random(seed)) || any_primary_node_key(seed: seed)
31
31
  end
32
32
  end
33
33
  end
@@ -12,7 +12,7 @@ class RedisClient
12
12
  end
13
13
 
14
14
  def clients_for_scanning(seed: nil)
15
- random = seed.nil? ? Random : Random.new(seed)
15
+ random = make_random(seed)
16
16
  keys = @replications.map do |primary_node_key, replica_node_keys|
17
17
  decide_use_primary?(random, replica_node_keys.size) ? primary_node_key : replica_node_keys.sample(random: random)
18
18
  end
@@ -21,7 +21,7 @@ class RedisClient
21
21
  end
22
22
 
23
23
  def find_node_key_of_replica(primary_node_key, seed: nil)
24
- random = seed.nil? ? Random : Random.new(seed)
24
+ random = make_random(seed)
25
25
 
26
26
  replica_node_keys = @replications.fetch(primary_node_key, EMPTY_ARRAY)
27
27
  if decide_use_primary?(random, replica_node_keys.size)
@@ -32,8 +32,7 @@ class RedisClient
32
32
  end
33
33
 
34
34
  def any_replica_node_key(seed: nil)
35
- random = seed.nil? ? Random : Random.new(seed)
36
- @replica_node_keys.sample(random: random) || any_primary_node_key(seed: seed)
35
+ @replica_node_keys.sample(random: make_random(seed)) || any_primary_node_key(seed: seed)
37
36
  end
38
37
 
39
38
  private
@@ -479,9 +479,11 @@ class RedisClient
479
479
  def with_reload_jitter
480
480
  return unless @next_reload_time.nil? || obtain_current_time >= @next_reload_time
481
481
 
482
- yield
483
-
484
- @next_reload_time = obtain_current_time + @random.rand(JITTER_WINDOW)
482
+ begin
483
+ yield
484
+ ensure
485
+ @next_reload_time = obtain_current_time + @random.rand(JITTER_WINDOW)
486
+ end
485
487
  end
486
488
 
487
489
  def with_reload_lock
@@ -18,12 +18,30 @@ class RedisClient
18
18
  end
19
19
 
20
20
  def split(node_key)
21
- pos = node_key&.rindex(DELIMITER, -1)
21
+ return [node_key, nil] if node_key.nil? || node_key.empty?
22
+
23
+ bracketed = split_bracketed(node_key)
24
+ return bracketed unless bracketed.nil?
25
+
26
+ pos = node_key.rindex(DELIMITER, -1)
22
27
  return [node_key, nil] if pos.nil?
23
28
 
24
29
  [node_key[0, pos], node_key[(pos + 1)..]]
25
30
  end
26
31
 
32
+ def split_bracketed(node_key)
33
+ return nil unless node_key.start_with?('[')
34
+
35
+ end_bracket = node_key.index(']')
36
+ return nil if end_bracket.nil?
37
+
38
+ host = node_key[1, end_bracket - 1]
39
+ remainder = node_key[(end_bracket + 1)..]
40
+ port = remainder.start_with?(DELIMITER) ? remainder[1..] : nil
41
+ [host, port]
42
+ end
43
+ private_class_method :split_bracketed
44
+
27
45
  def build_from_uri(uri)
28
46
  return '' if uri.nil?
29
47
 
@@ -54,7 +54,7 @@ class RedisClient
54
54
  rescue ::RedisClient::ConnectionError
55
55
  # Deduct the number of retries that happened _inside_ router#handle_redirection from our remaining
56
56
  # _external_ retries. Always deduct at least one in case handle_redirection raises without trying the block.
57
- retry_count -= [times_block_executed, 1].min
57
+ retry_count -= times_block_executed == 0 ? 1 : [times_block_executed, 1].min
58
58
  raise if retry_count < 0
59
59
 
60
60
  retry
@@ -230,10 +230,10 @@ class RedisClient
230
230
 
231
231
  def append_pipeline(node_key)
232
232
  @pipelines ||= {}
233
- @pipelines[node_key] ||= ::RedisClient::Cluster::Pipeline::Extended.new(::RedisClient::Cluster::NoopCommandBuilder)
234
- @pipelines[node_key].add_outer_index(@size)
233
+ pi = (@pipelines[node_key] ||= ::RedisClient::Cluster::Pipeline::Extended.new(::RedisClient::Cluster::NoopCommandBuilder))
234
+ pi.add_outer_index(@size)
235
235
  @size += 1
236
- @pipelines[node_key]
236
+ pi
237
237
  end
238
238
 
239
239
  def do_pipelining(client, pipeline)
@@ -61,7 +61,17 @@ class RedisClient
61
61
  end
62
62
 
63
63
  BUF_SIZE = Integer(ENV.fetch('REDIS_CLIENT_PUBSUB_BUF_SIZE', 1024))
64
- private_constant :BUF_SIZE
64
+ RECOVERY_BASE_INTERVAL = Float(ENV.fetch('REDIS_CLIENT_PUBSUB_RECOVERY_INTERVAL_SEC', 1.0))
65
+ RECOVERY_MAX_INTERVAL = Float(ENV.fetch('REDIS_CLIENT_PUBSUB_RECOVERY_MAX_INTERVAL_SEC', 30.0))
66
+ RECOVERY_MAX_ATTEMPTS = Integer(ENV.fetch('REDIS_CLIENT_PUBSUB_RECOVERY_MAX_ATTEMPTS', 10))
67
+ SUBSCRIBE_COMMANDS = %w[subscribe psubscribe ssubscribe].freeze
68
+ UNSUBSCRIBE_TO_SUBSCRIBE = {
69
+ 'unsubscribe' => 'subscribe',
70
+ 'punsubscribe' => 'psubscribe',
71
+ 'sunsubscribe' => 'ssubscribe'
72
+ }.freeze
73
+ private_constant :BUF_SIZE, :RECOVERY_BASE_INTERVAL, :RECOVERY_MAX_INTERVAL, :RECOVERY_MAX_ATTEMPTS,
74
+ :SUBSCRIBE_COMMANDS, :UNSUBSCRIBE_TO_SUBSCRIBE
65
75
 
66
76
  def initialize(router, command_builder)
67
77
  @router = router
@@ -74,14 +84,14 @@ class RedisClient
74
84
  def call(*args, **kwargs)
75
85
  command = @command_builder.generate(args, kwargs)
76
86
  _call(command)
77
- @commands << command
87
+ remember(command)
78
88
  nil
79
89
  end
80
90
 
81
91
  def call_v(command)
82
92
  command = @command_builder.generate(command)
83
93
  _call(command)
84
- @commands << command
94
+ remember(command)
85
95
  nil
86
96
  end
87
97
 
@@ -118,6 +128,35 @@ class RedisClient
118
128
 
119
129
  private
120
130
 
131
+ def remember(command)
132
+ cmd = command.first.to_s.downcase
133
+ if SUBSCRIBE_COMMANDS.include?(cmd)
134
+ @commands << command
135
+ elsif (subscribe_cmd = UNSUBSCRIBE_TO_SUBSCRIBE[cmd])
136
+ forget_subscriptions(subscribe_cmd, command[1..])
137
+ else
138
+ @commands << command
139
+ end
140
+ end
141
+
142
+ def forget_subscriptions(subscribe_cmd, channels)
143
+ if channels.nil? || channels.empty?
144
+ @commands.reject! { |c| c.first.to_s.casecmp(subscribe_cmd).zero? }
145
+ else
146
+ @commands.reject! { |c| prune_entry(c, subscribe_cmd, channels) }
147
+ end
148
+ end
149
+
150
+ def prune_entry(entry, subscribe_cmd, channels)
151
+ return false unless entry.first.to_s.casecmp(subscribe_cmd).zero?
152
+
153
+ remaining = entry[1..] - channels
154
+ return true if remaining.empty?
155
+
156
+ entry.replace([entry.first, *remaining])
157
+ false
158
+ end
159
+
121
160
  def _call(command) # rubocop:disable Metrics/AbcSize
122
161
  if command.first.casecmp('subscribe').zero?
123
162
  call_to_single_state(command)
@@ -179,6 +218,7 @@ class RedisClient
179
218
  end
180
219
 
181
220
  def start_over
221
+ attempt = 0
182
222
  loop do
183
223
  @router.renew_cluster_state
184
224
  @state_dict.each_value(&:close)
@@ -186,9 +226,16 @@ class RedisClient
186
226
  @commands.each { |command| _call(command) }
187
227
  break
188
228
  rescue ::RedisClient::ConnectionError, ::RedisClient::Cluster::NodeMightBeDown
189
- sleep 1.0
229
+ attempt += 1
230
+ raise if attempt >= RECOVERY_MAX_ATTEMPTS
231
+
232
+ sleep recovery_interval(attempt)
190
233
  end
191
234
  end
235
+
236
+ def recovery_interval(attempt)
237
+ [RECOVERY_BASE_INTERVAL * (2**(attempt - 1)), RECOVERY_MAX_INTERVAL].min
238
+ end
192
239
  end
193
240
  end
194
241
  end
@@ -246,23 +246,27 @@ class RedisClient
246
246
  end
247
247
 
248
248
  def find_node_key(command, seed: nil)
249
- key = @command.extract_first_key(command)
250
- find_node_key_by_key(key, seed: seed, primary: @command.should_send_to_primary?(command))
249
+ cmd_spec = @command.get_spec(command.first)
250
+ find_node_key_by_key(
251
+ cmd_spec&.extract_first_key(command),
252
+ seed: seed,
253
+ primary: cmd_spec&.should_send_to_primary?
254
+ )
251
255
  end
252
256
 
253
257
  def find_primary_node_key(command)
254
- key = @command.extract_first_key(command)
255
- return nil unless key&.size&.> 0
258
+ key = @command.get_spec(command.first)&.extract_first_key(command)
259
+ return unless key&.size&.> 0
256
260
 
257
261
  find_node_key_by_key(key, primary: true)
258
262
  end
259
263
 
260
264
  def find_slot(command)
261
- find_slot_by_key(@command.extract_first_key(command))
265
+ find_slot_by_key(@command.get_spec(command.first)&.extract_first_key(command))
262
266
  end
263
267
 
264
268
  def find_slot_by_key(key)
265
- return if key.empty?
269
+ return if key.nil? || key.empty?
266
270
 
267
271
  ::RedisClient::Cluster::KeySlotConverter.convert(key)
268
272
  end
@@ -467,8 +471,13 @@ class RedisClient
467
471
 
468
472
  return assign_node_and_send_command(method, command, args, &block) if command.size <= keys_step + 1 || ::RedisClient::Cluster::KeySlotConverter.hash_tag_included?(command[1])
469
473
 
470
- seed = @config.use_replica? && @config.replica_affinity == :random ? nil : Random.new_seed
471
- pipeline = ::RedisClient::Cluster::Pipeline.new(self, @command_builder, @concurrent_worker, exception: true, seed: seed)
474
+ pipeline = ::RedisClient::Cluster::Pipeline.new(
475
+ self,
476
+ @command_builder,
477
+ @concurrent_worker,
478
+ exception: true,
479
+ seed: Random.new_seed
480
+ )
472
481
 
473
482
  single_command = Array.new(keys_step + 1)
474
483
  single_command[0] = single_key_cmd
@@ -95,13 +95,12 @@ class RedisClient
95
95
  end
96
96
 
97
97
  def pipelined(exception: true)
98
- seed = @config.use_replica? && @config.replica_affinity == :random ? nil : Random.new_seed
99
98
  pipeline = ::RedisClient::Cluster::Pipeline.new(
100
99
  router,
101
100
  @command_builder,
102
101
  @concurrent_worker,
103
102
  exception: exception,
104
- seed: seed
103
+ seed: Random.new_seed
105
104
  )
106
105
 
107
106
  yield pipeline
@@ -32,6 +32,12 @@ class RedisClient
32
32
 
33
33
  InvalidClientConfigError = Class.new(::RedisClient::Cluster::Error)
34
34
 
35
+ SENSITIVE_INSPECT_KEYS = %i[username password].freeze
36
+ INSPECT_REDACTED_KEYS = %i[command_builder].freeze
37
+ INSPECT_PLACEHOLDER = '[FILTERED]'
38
+
39
+ private_constant :SENSITIVE_INSPECT_KEYS, :INSPECT_REDACTED_KEYS, :INSPECT_PLACEHOLDER
40
+
35
41
  attr_reader :command_builder, :client_config, :replica_affinity, :slow_command_timeout,
36
42
  :connect_with_original_config, :startup_nodes, :max_startup_sample, :id
37
43
 
@@ -65,7 +71,7 @@ class RedisClient
65
71
  end
66
72
 
67
73
  def inspect
68
- "#<#{self.class.name} #{startup_nodes.values.map { |v| v.reject { |k| k == :command_builder } }}>"
74
+ "#<#{self.class.name} #{startup_nodes.values.map { |v| redact_for_inspect(v) }}>"
69
75
  end
70
76
 
71
77
  def connect_timeout
@@ -117,6 +123,14 @@ class RedisClient
117
123
 
118
124
  private
119
125
 
126
+ def redact_for_inspect(node_config)
127
+ node_config.each_with_object({}) do |(key, value), redacted|
128
+ next if INSPECT_REDACTED_KEYS.include?(key)
129
+
130
+ redacted[key] = SENSITIVE_INSPECT_KEYS.include?(key) ? INSPECT_PLACEHOLDER : value
131
+ end
132
+ end
133
+
120
134
  def merge_concurrency_option(option)
121
135
  opts = {}
122
136
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis-cluster-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.1
4
+ version: 0.16.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Taishi Kasuga