redis-client 0.14.1 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
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+