redis-client 0.22.2 → 0.23.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: 796fe04bd550f92d5bf2656a62de54caf1f640b45ab5d5f51fed61d0b61ee883
4
- data.tar.gz: 4b34ce4e0ed5a2d4816863970789e7c23ce53fe8a561718b78b79432d09e3fe8
3
+ metadata.gz: 588c7b4963a757c4d297236e15e0fd2b84d1137f42f14ca26dcfdf3349588126
4
+ data.tar.gz: cc68ebf122ca5c9044c9c0e08713071e60b151c526f37699d7fa5d28c2e52e5f
5
5
  SHA512:
6
- metadata.gz: d9dfb29603171606e71daaa43474812790988876720dd254a0069fc429ba035ede74b1b19d243967b9adb61c0d48ab4aa2931ef188cd2bb17d820b480967c897
7
- data.tar.gz: e6ff3a4543991e139a26f8fa4bdb714aafe83678490f9890ca5fd0ec1a95bdddd0c8fec57dcb4ff88fb28a19d577a556172cb39e4bd4f290d41c6871de43345b
6
+ metadata.gz: 3a23804a8dbf13a35f8dae709586c44b3b81bd4d6d671ed79890b734b9e722f996c6d514bc609da511a0695deeefbfdfafcf8545fd6a0f7d8144d1936f72f417
7
+ data.tar.gz: 1be3aff9e63209a7424c0078f1fe26d8f45212034f0a8d18fa8cb778f7bdb12200effa4796082dd7a1096f18f840cdf84ddff8a9ab3ff2ad6a552db9db595484
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Unreleased
2
2
 
3
+ # 0.23.0
4
+
5
+ - Allow `password` to be a callable. Makes it easy to implement short lived password authentication strategies.
6
+ - Fix a thread safety issue in `hiredis-client` when using the `pubsub` client concurrently.
7
+
3
8
  # 0.22.2
4
9
 
5
10
  - Fix the sentinel client to properly extend timeout for blocking commands.
data/README.md CHANGED
@@ -1,9 +1,10 @@
1
1
  # RedisClient
2
2
 
3
- `redis-client` is a simple, low-level, client for Redis 6+.
3
+ `redis-client` is a simple, low-level, client for [Redis](https://redis.io/) 6+, [Valkey](https://valkey.io/) 7+, [KeyDB](https://docs.keydb.dev/),
4
+ and several other databases that implement the same `RESP3` protocol.
4
5
 
5
6
  Contrary to the `redis` gem, `redis-client` doesn't try to map all Redis commands to Ruby constructs,
6
- it merely is a thin wrapper on top of the RESP3 protocol.
7
+ it merely is a thin wrapper on top of the `RESP3` protocol.
7
8
 
8
9
  ## Installation
9
10
 
@@ -77,7 +78,7 @@ redis.call("GET", "mykey")
77
78
  - `db`: The database to select after connecting, defaults to `0`.
78
79
  - `id` ID for the client connection, assigns name to current connection by sending `CLIENT SETNAME`.
79
80
  - `username` Username to authenticate against server, defaults to `"default"`.
80
- - `password` Password to authenticate against server.
81
+ - `password` Password to authenticate against server. Can either be a String or a callable that recieve `username` as argument and return a passowrd as a String.
81
82
  - `timeout`: The general timeout in seconds, default to `1.0`.
82
83
  - `connect_timeout`: The connection timeout, takes precedence over the general timeout when connecting to the server.
83
84
  - `read_timeout`: The read timeout, takes precedence over the general timeout when reading responses from the server.
@@ -89,7 +90,7 @@ redis.call("GET", "mykey")
89
90
 
90
91
  ### Sentinel support
91
92
 
92
- The client is able to perform automatic failover by using [Redis Sentinel](https://redis.io/docs/manual/sentinel/).
93
+ The client is able to perform automatic failover by using [Redis Sentinel](https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel/).
93
94
 
94
95
  To connect using Sentinel, use:
95
96
 
@@ -63,7 +63,7 @@ class RedisClient
63
63
  private
64
64
 
65
65
  def refresh_state
66
- now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
66
+ now = RedisClient.now
67
67
  @lock.synchronize do
68
68
  if @errors.last < (now - @error_timeout)
69
69
  if @success_threshold > 0
@@ -78,7 +78,7 @@ class RedisClient
78
78
  end
79
79
 
80
80
  def record_error
81
- now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
81
+ now = RedisClient.now
82
82
  expiry = now - @error_timeout
83
83
  @lock.synchronize do
84
84
  if @state == :closed
@@ -12,8 +12,8 @@ class RedisClient
12
12
  DEFAULT_DB = 0
13
13
 
14
14
  module Common
15
- attr_reader :db, :password, :id, :ssl, :ssl_params, :command_builder, :inherit_socket,
16
- :connect_timeout, :read_timeout, :write_timeout, :driver, :connection_prelude, :protocol,
15
+ attr_reader :db, :id, :ssl, :ssl_params, :command_builder, :inherit_socket,
16
+ :connect_timeout, :read_timeout, :write_timeout, :driver, :protocol,
17
17
  :middlewares_stack, :custom, :circuit_breaker
18
18
 
19
19
  alias_method :ssl?, :ssl
@@ -70,7 +70,7 @@ class RedisClient
70
70
 
71
71
  reconnect_attempts = Array.new(reconnect_attempts, 0).freeze if reconnect_attempts.is_a?(Integer)
72
72
  @reconnect_attempts = reconnect_attempts
73
- @connection_prelude = build_connection_prelude
73
+ @connection_prelude = (build_connection_prelude unless @password.respond_to?(:call))
74
74
 
75
75
  circuit_breaker = CircuitBreaker.new(**circuit_breaker) if circuit_breaker.is_a?(Hash)
76
76
  if @circuit_breaker = circuit_breaker
@@ -87,6 +87,22 @@ class RedisClient
87
87
  @middlewares_stack = middlewares_stack
88
88
  end
89
89
 
90
+ def connection_prelude
91
+ if @password.respond_to?(:call)
92
+ build_connection_prelude
93
+ else
94
+ @connection_prelude
95
+ end
96
+ end
97
+
98
+ def password
99
+ if @password.respond_to?(:call)
100
+ @password.call(username)
101
+ else
102
+ @password
103
+ end
104
+ end
105
+
90
106
  def username
91
107
  @username || DEFAULT_USERNAME
92
108
  end
@@ -151,17 +167,18 @@ class RedisClient
151
167
 
152
168
  def build_connection_prelude
153
169
  prelude = []
170
+ pass = password
154
171
  if protocol == 3
155
- prelude << if @password
156
- ["HELLO", "3", "AUTH", @username || DEFAULT_USERNAME, @password]
172
+ prelude << if pass
173
+ ["HELLO", "3", "AUTH", username, pass]
157
174
  else
158
175
  ["HELLO", "3"]
159
176
  end
160
- elsif @password
177
+ elsif pass
161
178
  prelude << if @username && !@username.empty?
162
- ["AUTH", @username, @password]
179
+ ["AUTH", @username, pass]
163
180
  else
164
- ["AUTH", @password]
181
+ ["AUTH", pass]
165
182
  end
166
183
  end
167
184
 
@@ -104,9 +104,9 @@ class RedisClient
104
104
  end
105
105
 
106
106
  def measure_round_trip_delay
107
- start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
107
+ start = RedisClient.now_ms
108
108
  call(["PING"], @read_timeout)
109
- Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond) - start
109
+ RedisClient.now_ms - start
110
110
  end
111
111
 
112
112
  private
@@ -116,7 +116,12 @@ class RedisClient
116
116
  UNIXSocket.new(@config.path)
117
117
  else
118
118
  sock = if SUPPORTS_RESOLV_TIMEOUT
119
- Socket.tcp(@config.host, @config.port, connect_timeout: @connect_timeout, resolv_timeout: @connect_timeout)
119
+ begin
120
+ Socket.tcp(@config.host, @config.port, connect_timeout: @connect_timeout, resolv_timeout: @connect_timeout)
121
+ rescue Errno::ETIMEDOUT => timeout_error
122
+ timeout_error.message << ": #{@connect_timeout}s"
123
+ raise
124
+ end
120
125
  else
121
126
  Socket.tcp(@config.host, @config.port, connect_timeout: @connect_timeout)
122
127
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class RedisClient
4
- VERSION = "0.22.2"
4
+ VERSION = "0.23.0"
5
5
  end
data/lib/redis_client.rb CHANGED
@@ -45,6 +45,14 @@ class RedisClient
45
45
  def default_driver=(name)
46
46
  @default_driver = driver(name)
47
47
  end
48
+
49
+ def now
50
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
51
+ end
52
+
53
+ def now_ms
54
+ Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
55
+ end
48
56
  end
49
57
 
50
58
  register_driver :ruby do
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.22.2
4
+ version: 0.23.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: 2024-05-22 00:00:00.000000000 Z
11
+ date: 2024-12-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: connection_pool
@@ -32,11 +32,8 @@ extensions: []
32
32
  extra_rdoc_files: []
33
33
  files:
34
34
  - CHANGELOG.md
35
- - Gemfile
36
- - Gemfile.lock
37
35
  - LICENSE.md
38
36
  - README.md
39
- - Rakefile
40
37
  - lib/redis-client.rb
41
38
  - lib/redis_client.rb
42
39
  - lib/redis_client/circuit_breaker.rb
@@ -53,7 +50,6 @@ files:
53
50
  - lib/redis_client/sentinel_config.rb
54
51
  - lib/redis_client/url_config.rb
55
52
  - lib/redis_client/version.rb
56
- - redis-client.gemspec
57
53
  homepage: https://github.com/redis-rb/redis-client
58
54
  licenses:
59
55
  - MIT
@@ -77,7 +73,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
77
73
  - !ruby/object:Gem::Version
78
74
  version: '0'
79
75
  requirements: []
80
- rubygems_version: 3.5.9
76
+ rubygems_version: 3.0.3.1
81
77
  signing_key:
82
78
  specification_version: 4
83
79
  summary: Simple low-level client for Redis 6+
data/Gemfile DELETED
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source "https://rubygems.org"
4
-
5
- # Specify your gem's dependencies in redis-client.gemspec
6
- gemspec name: "redis-client"
7
-
8
- gem "minitest"
9
- gem "rake", "~> 13.2"
10
- gem "rake-compiler"
11
- gem "rubocop"
12
- gem "rubocop-minitest"
13
- gem "toxiproxy"
14
-
15
- group :benchmark do
16
- gem "benchmark-ips"
17
- gem "hiredis"
18
- gem "redis", "~> 4.6"
19
- gem "stackprof", platform: :mri
20
- end
21
-
22
- gem "byebug", platform: :mri
data/Gemfile.lock DELETED
@@ -1,72 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- redis-client (0.22.2)
5
- connection_pool
6
-
7
- GEM
8
- remote: https://rubygems.org/
9
- specs:
10
- ast (2.4.2)
11
- benchmark-ips (2.13.0)
12
- byebug (11.1.3)
13
- connection_pool (2.4.1)
14
- hiredis (0.6.3)
15
- hiredis (0.6.3-java)
16
- json (2.7.1)
17
- json (2.7.1-java)
18
- minitest (5.23.0)
19
- parallel (1.24.0)
20
- parser (3.3.0.5)
21
- ast (~> 2.4.1)
22
- racc
23
- racc (1.7.3)
24
- racc (1.7.3-java)
25
- rainbow (3.1.1)
26
- rake (13.2.1)
27
- rake-compiler (1.2.7)
28
- rake
29
- redis (4.6.0)
30
- regexp_parser (2.9.0)
31
- rexml (3.2.6)
32
- rubocop (1.50.2)
33
- json (~> 2.3)
34
- parallel (~> 1.10)
35
- parser (>= 3.2.0.0)
36
- rainbow (>= 2.2.2, < 4.0)
37
- regexp_parser (>= 1.8, < 3.0)
38
- rexml (>= 3.2.5, < 4.0)
39
- rubocop-ast (>= 1.28.0, < 2.0)
40
- ruby-progressbar (~> 1.7)
41
- unicode-display_width (>= 2.4.0, < 3.0)
42
- rubocop-ast (1.30.0)
43
- parser (>= 3.2.1.0)
44
- rubocop-minitest (0.30.0)
45
- rubocop (>= 1.39, < 2.0)
46
- ruby-progressbar (1.13.0)
47
- stackprof (0.2.26)
48
- toxiproxy (2.0.2)
49
- unicode-display_width (2.5.0)
50
-
51
- PLATFORMS
52
- ruby
53
- universal-java-18
54
- x86_64-darwin-20
55
- x86_64-linux
56
-
57
- DEPENDENCIES
58
- benchmark-ips
59
- byebug
60
- hiredis
61
- minitest
62
- rake (~> 13.2)
63
- rake-compiler
64
- redis (~> 4.6)
65
- redis-client!
66
- rubocop
67
- rubocop-minitest
68
- stackprof
69
- toxiproxy
70
-
71
- BUNDLED WITH
72
- 2.3.13
data/Rakefile DELETED
@@ -1,126 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "rake/extensiontask"
4
- require "rake/testtask"
5
- require 'rubocop/rake_task'
6
-
7
- RuboCop::RakeTask.new
8
-
9
- require "rake/clean"
10
- CLOBBER.include "pkg"
11
- require "bundler/gem_helper"
12
- Bundler::GemHelper.install_tasks(name: "redis-client")
13
- Bundler::GemHelper.install_tasks(dir: "hiredis-client", name: "hiredis-client")
14
-
15
- gemspec = Gem::Specification.load("redis-client.gemspec")
16
- Rake::ExtensionTask.new do |ext|
17
- ext.name = "hiredis_connection"
18
- ext.ext_dir = "hiredis-client/ext/redis_client/hiredis"
19
- ext.lib_dir = "hiredis-client/lib/redis_client"
20
- ext.gem_spec = gemspec
21
- CLEAN.add("#{ext.ext_dir}/vendor/*.{a,o}")
22
- end
23
-
24
- namespace :test do
25
- Rake::TestTask.new(:ruby) do |t|
26
- t.libs << "test"
27
- t.libs << "lib"
28
- t.test_files = FileList["test/**/*_test.rb"].exclude("test/sentinel/*_test.rb")
29
- t.options = '-v' if ENV['CI'] || ENV['VERBOSE']
30
- end
31
-
32
- Rake::TestTask.new(:sentinel) do |t|
33
- t.libs << "test/sentinel"
34
- t.libs << "test"
35
- t.libs << "lib"
36
- t.test_files = FileList["test/sentinel/*_test.rb"]
37
- t.options = '-v' if ENV['CI'] || ENV['VERBOSE']
38
- end
39
-
40
- Rake::TestTask.new(:hiredis) do |t|
41
- t.libs << "test/hiredis"
42
- t.libs << "test"
43
- t.libs << "hiredis-client/lib"
44
- t.libs << "lib"
45
- t.test_files = FileList["test/**/*_test.rb"].exclude("test/sentinel/*_test.rb")
46
- t.options = '-v' if ENV['CI'] || ENV['VERBOSE']
47
- end
48
- end
49
-
50
- hiredis_supported = RUBY_ENGINE == "ruby" && !RUBY_PLATFORM.match?(/mswin/)
51
- if hiredis_supported
52
- task test: %i[test:ruby test:hiredis test:sentinel]
53
- else
54
- task test: %i[test:ruby test:sentinel]
55
- end
56
-
57
- namespace :hiredis do
58
- task :download do
59
- version = "1.0.2"
60
- archive_path = "tmp/hiredis-#{version}.tar.gz"
61
- url = "https://github.com/redis/hiredis/archive/refs/tags/v#{version}.tar.gz"
62
- system("curl", "-L", url, out: archive_path) or raise "Downloading of #{url} failed"
63
- system("rm", "-rf", "hiredis-client/ext/redis_client/hiredis/vendor/")
64
- system("mkdir", "-p", "hiredis-client/ext/redis_client/hiredis/vendor/")
65
- system(
66
- "tar", "xvzf", archive_path,
67
- "-C", "hiredis-client/ext/redis_client/hiredis/vendor",
68
- "--strip-components", "1",
69
- )
70
- system("rm", "-rf", "hiredis-client/ext/redis_client/hiredis/vendor/examples")
71
- end
72
- end
73
-
74
- benchmark_suites = %w(single pipelined drivers)
75
- benchmark_modes = %i[ruby yjit hiredis]
76
- namespace :benchmark do
77
- benchmark_suites.each do |suite|
78
- benchmark_modes.each do |mode|
79
- next if suite == "drivers" && mode == :hiredis
80
-
81
- name = "#{suite}_#{mode}"
82
- desc name
83
- task name do
84
- output_path = "benchmark/#{name}.md"
85
- sh "rm", "-f", output_path
86
- File.open(output_path, "w+") do |output|
87
- output.puts("ruby: `#{RUBY_DESCRIPTION}`\n\n")
88
- output.puts("redis-server: `#{`redis-server -v`.strip}`\n\n")
89
- output.puts
90
- output.flush
91
- env = {}
92
- args = []
93
- args << "--yjit" if mode == :yjit
94
- env["DRIVER"] = mode == :hiredis ? "hiredis" : "ruby"
95
- system(env, RbConfig.ruby, *args, "benchmark/#{suite}.rb", out: output)
96
- end
97
-
98
- skipping = false
99
- output = File.readlines(output_path).reject do |line|
100
- if skipping
101
- if line == "Comparison:\n"
102
- skipping = false
103
- true
104
- else
105
- skipping
106
- end
107
- else
108
- skipping = true if line.start_with?("Warming up ---")
109
- skipping
110
- end
111
- end
112
- File.write(output_path, output.join)
113
- end
114
- end
115
- end
116
-
117
- task all: benchmark_suites.flat_map { |s| benchmark_modes.flat_map { |m| "#{s}_#{m}" } }
118
- end
119
-
120
- if hiredis_supported
121
- task default: %i[compile test rubocop]
122
- task ci: %i[compile test:ruby test:hiredis]
123
- else
124
- task default: %i[test rubocop]
125
- task ci: %i[test:ruby]
126
- end
data/redis-client.gemspec DELETED
@@ -1,32 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "lib/redis_client/version"
4
-
5
- Gem::Specification.new do |spec|
6
- spec.name = "redis-client"
7
- spec.version = RedisClient::VERSION
8
- spec.authors = ["Jean Boussier"]
9
- spec.email = ["jean.boussier@gmail.com"]
10
-
11
- spec.summary = "Simple low-level client for Redis 6+"
12
- spec.homepage = "https://github.com/redis-rb/redis-client"
13
- spec.license = "MIT"
14
- spec.required_ruby_version = ">= 2.6.0"
15
-
16
- spec.metadata["allowed_push_host"] = "https://rubygems.org"
17
-
18
- spec.metadata["homepage_uri"] = spec.homepage
19
- spec.metadata["source_code_uri"] = spec.homepage
20
- spec.metadata["changelog_uri"] = File.join(spec.homepage, "blob/master/CHANGELOG.md")
21
-
22
- # Specify which files should be added to the gem when it is released.
23
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
- spec.files = Dir.chdir(File.expand_path(__dir__)) do
25
- `git ls-files -z`.split("\x0").reject do |f|
26
- (f == __FILE__) || f.match(%r{\A(?:(?:bin|hiredis-client|test|spec|features|benchmark)/|\.(?:git|rubocop))})
27
- end
28
- end
29
- spec.require_paths = ["lib"]
30
-
31
- spec.add_runtime_dependency "connection_pool"
32
- end