redis-client 0.14.1 → 0.15.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e9858303eff06d56f8e144eb7033995fc92839c41fe39d0bda6f1d9c2fed91e9
4
- data.tar.gz: a4f4ca2073a32e28932c58e537963a2f0b2321e36ca472c6edc9edab1b29122b
3
+ metadata.gz: aa81349f7f830db128f2637bc4c782543b9c51a16075b54fdc4700810a95ae83
4
+ data.tar.gz: d34e19d6229292bb6b15e61aebeda57729780040e098f0fae1beb534f9717ff9
5
5
  SHA512:
6
- metadata.gz: 3c20b17e4587980ed89e0877b20cb9aee7d16365401127cfdc3b4f451f7706bf338988e97ac9d1e62d33c9af5b45ebfb3c35310205fbd309995ace46c3b85fce
7
- data.tar.gz: 21ed1731b716f1bd6e525b638b7715683979860faa0d14f2fc6f210c4d185a95d2329650b56cf731734d22d7008cdadccc2055a8d5ff7a332b043836e0d39b75
6
+ metadata.gz: 01ed887cfc90ffc36ff82669a459d1c9b13b72c9d81f66ca770e7642c16cba90d30cfd31768029b81a1519778f6af2905b145ab606f070008ac471192ce812fa
7
+ data.tar.gz: 64320bfdcdaa3fd4402ae41350dc0451127f895e3f224d916140ea3fc5813dfe9344f04dc18c20d82e9c3a0878d19ade9b3716274278a21fbe3fd1c5da855797
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Unreleased
2
2
 
3
+ # 0.15.0
4
+
5
+ - Discard sockets rather than explictly close them when a fork is detected. #126.
6
+ - Allow to configure sentinel client via url. #117.
7
+ - Fix sentinel to preverse the auth/password when refreshing the sentinel list. #107.
8
+
3
9
  # 0.14.1
4
10
 
5
11
  - Include the timeout value in TimeoutError messages.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- redis-client (0.14.1)
4
+ redis-client (0.15.0)
5
5
  connection_pool
6
6
 
7
7
  GEM
@@ -10,7 +10,7 @@ GEM
10
10
  ast (2.4.2)
11
11
  benchmark-ips (2.12.0)
12
12
  byebug (11.1.3)
13
- connection_pool (2.4.0)
13
+ connection_pool (2.4.1)
14
14
  hiredis (0.6.3)
15
15
  hiredis (0.6.3-java)
16
16
  minitest (5.15.0)
@@ -19,7 +19,7 @@ GEM
19
19
  ast (~> 2.4.1)
20
20
  rainbow (3.1.1)
21
21
  rake (13.0.6)
22
- rake-compiler (1.2.1)
22
+ rake-compiler (1.2.3)
23
23
  rake
24
24
  redis (4.6.0)
25
25
  regexp_parser (2.5.0)
@@ -38,7 +38,7 @@ GEM
38
38
  rubocop-minitest (0.19.1)
39
39
  rubocop (>= 0.90, < 2.0)
40
40
  ruby-progressbar (1.11.0)
41
- stackprof (0.2.24)
41
+ stackprof (0.2.25)
42
42
  toxiproxy (2.0.2)
43
43
  unicode-display_width (2.2.0)
44
44
 
data/README.md CHANGED
@@ -343,6 +343,10 @@ loop do
343
343
  end
344
344
  ```
345
345
 
346
+ *Note*: pubsub connections are stateful, as such they won't ever reconnect automatically.
347
+ The caller is resonsible for reconnecting if the connection is lost and to resubscribe to
348
+ all channels.
349
+
346
350
  ## Production
347
351
 
348
352
  ### Instrumentation and Middlewares
@@ -439,6 +443,8 @@ redis.call("GET", "counter") # Will be retried up to 3 times.
439
443
  redis.call_once("INCR", "counter") # Won't be retried.
440
444
  ```
441
445
 
446
+ **Note**: automatic reconnection doesn't apply to pubsub clients as their connection is stateful.
447
+
442
448
  ### Exponential backoff
443
449
 
444
450
  Alternatively, `reconnect_attempts` accepts a list of sleep durations for implementing exponential backoff:
@@ -162,37 +162,21 @@ class RedisClient
162
162
  **kwargs
163
163
  )
164
164
  if url
165
- uri = URI(url)
166
- unless uri.scheme == "redis" || uri.scheme == "rediss"
167
- raise ArgumentError, "Invalid URL: #{url.inspect}"
168
- end
169
-
170
- kwargs[:ssl] = uri.scheme == "rediss" unless kwargs.key?(:ssl)
171
-
172
- kwargs[:username] ||= uri.user if uri.password && !uri.user.empty?
173
-
174
- kwargs[:password] ||= if uri.user && !uri.password
175
- URI.decode_www_form_component(uri.user)
176
- elsif uri.user && uri.password
177
- URI.decode_www_form_component(uri.password)
178
- end
179
-
180
- db_path = uri.path&.delete_prefix("/")
181
- kwargs[:db] ||= Integer(db_path) if db_path && !db_path.empty?
165
+ url_config = URLConfig.new(url)
166
+ kwargs = {
167
+ ssl: url_config.ssl?,
168
+ username: url_config.username,
169
+ password: url_config.password,
170
+ db: url_config.db,
171
+ }.compact.merge(kwargs)
172
+ host ||= url_config.host
173
+ port ||= url_config.port
182
174
  end
183
175
 
184
176
  super(**kwargs)
185
177
 
186
- @host = host
187
- unless @host
188
- uri_host = uri&.host
189
- uri_host = nil if uri_host&.empty?
190
- if uri_host
191
- @host = uri_host&.sub(/\A\[(.*)\]\z/, '\1')
192
- end
193
- end
194
- @host ||= DEFAULT_HOST
195
- @port = Integer(port || uri&.port || DEFAULT_PORT)
178
+ @host = host || DEFAULT_HOST
179
+ @port = Integer(port || DEFAULT_PORT)
196
180
  @path = path
197
181
  end
198
182
  end
@@ -24,6 +24,10 @@ class RedisClient
24
24
  @io.to_io.close
25
25
  end
26
26
 
27
+ def reopen(*args)
28
+ @io.to_io.reopen(*args)
29
+ end
30
+
27
31
  def closed?
28
32
  @io.to_io.closed?
29
33
  end
@@ -58,6 +58,13 @@ class RedisClient
58
58
  super
59
59
  end
60
60
 
61
+ def discard
62
+ unless @io.closed?
63
+ @io.reopen(File::NULL)
64
+ end
65
+ close
66
+ end
67
+
61
68
  def read_timeout=(timeout)
62
69
  @read_timeout = timeout
63
70
  @io.read_timeout = timeout if @io
@@ -101,6 +108,12 @@ class RedisClient
101
108
  raise ConnectionError, error.message
102
109
  end
103
110
 
111
+ def measure_round_trip_delay
112
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
113
+ call(["PING"], @read_timeout)
114
+ Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond) - start
115
+ end
116
+
104
117
  private
105
118
 
106
119
  def connect
@@ -7,11 +7,28 @@ class RedisClient
7
7
  SENTINEL_DELAY = 0.25
8
8
  DEFAULT_RECONNECT_ATTEMPTS = 2
9
9
 
10
- def initialize(name:, sentinels:, role: :master, **client_config)
10
+ attr_reader :name
11
+
12
+ def initialize(sentinels:, role: :master, name: nil, url: nil, **client_config)
11
13
  unless %i(master replica slave).include?(role)
12
14
  raise ArgumentError, "Expected role to be either :master or :replica, got: #{role.inspect}"
13
15
  end
14
16
 
17
+ if url
18
+ url_config = URLConfig.new(url)
19
+ client_config = {
20
+ username: url_config.username,
21
+ password: url_config.password,
22
+ db: url_config.db,
23
+ }.compact.merge(client_config)
24
+ name ||= url_config.host
25
+ end
26
+
27
+ @name = name
28
+ unless @name
29
+ raise ArgumentError, "RedisClient::SentinelConfig requires either a name or an url with a host"
30
+ end
31
+
15
32
  @to_list_of_hash = @to_hash = nil
16
33
  @extra_config = {}
17
34
  if client_config[:protocol] == 2
@@ -25,8 +42,6 @@ class RedisClient
25
42
  end
26
43
  end
27
44
 
28
- @name = name
29
- @sentinel_configs = sentinels_to_configs(sentinels)
30
45
  @sentinels = {}.compare_by_identity
31
46
  @role = role
32
47
  @mutex = Mutex.new
@@ -35,6 +50,7 @@ class RedisClient
35
50
  client_config[:reconnect_attempts] ||= DEFAULT_RECONNECT_ATTEMPTS
36
51
  @client_config = client_config || {}
37
52
  super(**client_config)
53
+ @sentinel_configs = sentinels_to_configs(sentinels)
38
54
  end
39
55
 
40
56
  def sentinels
@@ -90,9 +106,9 @@ class RedisClient
90
106
  sentinels.map do |sentinel|
91
107
  case sentinel
92
108
  when String
93
- Config.new(**@extra_config, url: sentinel)
109
+ Config.new(**@client_config, **@extra_config, url: sentinel, db: nil)
94
110
  else
95
- Config.new(**@extra_config, **sentinel)
111
+ Config.new(**@client_config, **@extra_config, **sentinel, db: nil)
96
112
  end
97
113
  end
98
114
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+
5
+ class RedisClient
6
+ class URLConfig
7
+ DEFAULT_SCHEMA = "redis"
8
+ SSL_SCHEMA = "rediss"
9
+
10
+ attr_reader :url, :uri
11
+
12
+ def initialize(url)
13
+ @url = url
14
+ @uri = URI(url)
15
+ unless uri.scheme == DEFAULT_SCHEMA || uri.scheme == SSL_SCHEMA
16
+ raise ArgumentError, "Invalid URL: #{url.inspect}"
17
+ end
18
+ end
19
+
20
+ def ssl?
21
+ @uri.scheme == SSL_SCHEMA
22
+ end
23
+
24
+ def db
25
+ db_path = uri.path&.delete_prefix("/")
26
+ Integer(db_path) if db_path && !db_path.empty?
27
+ end
28
+
29
+ def username
30
+ uri.user if uri.password && !uri.user.empty?
31
+ end
32
+
33
+ def password
34
+ if uri.user && !uri.password
35
+ URI.decode_www_form_component(uri.user)
36
+ elsif uri.user && uri.password
37
+ URI.decode_www_form_component(uri.password)
38
+ end
39
+ end
40
+
41
+ def host
42
+ return if uri.host.nil? || uri.host.empty?
43
+
44
+ uri.host.sub(/\A\[(.*)\]\z/, '\1')
45
+ end
46
+
47
+ def port
48
+ return unless uri.port
49
+
50
+ Integer(uri.port)
51
+ end
52
+ end
53
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class RedisClient
4
- VERSION = "0.14.1"
4
+ VERSION = "0.15.0"
5
5
  end
data/lib/redis_client.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "redis_client/version"
4
4
  require "redis_client/command_builder"
5
+ require "redis_client/url_config"
5
6
  require "redis_client/config"
6
7
  require "redis_client/pid_cache"
7
8
  require "redis_client/sentinel_config"
@@ -204,6 +205,14 @@ class RedisClient
204
205
  sub
205
206
  end
206
207
 
208
+ def measure_round_trip_delay
209
+ ensure_connected do |connection|
210
+ @middlewares.call(["PING"], config) do
211
+ connection.measure_round_trip_delay
212
+ end
213
+ end
214
+ end
215
+
207
216
  def call(*command, **kwargs)
208
217
  command = @command_builder.generate(command, kwargs)
209
218
  result = ensure_connected do |connection|
@@ -349,6 +358,11 @@ class RedisClient
349
358
  self
350
359
  end
351
360
 
361
+ def discard
362
+ @raw_connection&.discard
363
+ self
364
+ end
365
+
352
366
  def pipelined
353
367
  pipeline = Pipeline.new(@command_builder)
354
368
  yield pipeline
@@ -609,7 +623,7 @@ class RedisClient
609
623
  end
610
624
 
611
625
  def ensure_connected(retryable: true)
612
- close if !config.inherit_socket && @pid != PIDCache.pid
626
+ discard if !config.inherit_socket && @pid != PIDCache.pid
613
627
 
614
628
  if @disable_reconnection
615
629
  if block_given?
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.1
4
+ version: 0.15.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jean Boussier
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-03-30 00:00:00.000000000 Z
11
+ date: 2023-08-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: connection_pool
@@ -51,6 +51,7 @@ files:
51
51
  - lib/redis_client/ruby_connection/buffered_io.rb
52
52
  - lib/redis_client/ruby_connection/resp3.rb
53
53
  - lib/redis_client/sentinel_config.rb
54
+ - lib/redis_client/url_config.rb
54
55
  - lib/redis_client/version.rb
55
56
  - redis-client.gemspec
56
57
  homepage: https://github.com/redis-rb/redis-client
@@ -76,7 +77,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
76
77
  - !ruby/object:Gem::Version
77
78
  version: '0'
78
79
  requirements: []
79
- rubygems_version: 3.4.6
80
+ rubygems_version: 3.3.7
80
81
  signing_key:
81
82
  specification_version: 4
82
83
  summary: Simple low-level client for Redis 6+