redis 5.4.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +52 -1
- data/README.md +165 -12
- data/lib/redis/client.rb +42 -10
- data/lib/redis/commands/geo.rb +108 -6
- data/lib/redis/commands/hashes.rb +166 -2
- data/lib/redis/commands/keys.rb +5 -1
- data/lib/redis/commands/lists.rb +93 -0
- data/lib/redis/commands/modules/json.rb +530 -0
- data/lib/redis/commands/modules/search/aggregation.rb +418 -0
- data/lib/redis/commands/modules/search/dialect.rb +14 -0
- data/lib/redis/commands/modules/search/field.rb +306 -0
- data/lib/redis/commands/modules/search/hybrid.rb +359 -0
- data/lib/redis/commands/modules/search/index.rb +351 -0
- data/lib/redis/commands/modules/search/index_definition.rb +114 -0
- data/lib/redis/commands/modules/search/miscellaneous.rb +607 -0
- data/lib/redis/commands/modules/search/query.rb +738 -0
- data/lib/redis/commands/modules/search/result.rb +488 -0
- data/lib/redis/commands/modules/search/schema.rb +211 -0
- data/lib/redis/commands/modules/search.rb +19 -0
- data/lib/redis/commands/sets.rb +41 -0
- data/lib/redis/commands/sorted_sets.rb +0 -1
- data/lib/redis/commands/streams.rb +28 -8
- data/lib/redis/commands.rb +30 -10
- data/lib/redis/distributed.rb +271 -1
- data/lib/redis/lib_identity.rb +105 -0
- data/lib/redis/subscribe.rb +1 -1
- data/lib/redis/version.rb +1 -1
- data/lib/redis.rb +141 -11
- metadata +22 -9
data/lib/redis/subscribe.rb
CHANGED
data/lib/redis/version.rb
CHANGED
data/lib/redis.rb
CHANGED
|
@@ -53,8 +53,14 @@ class Redis
|
|
|
53
53
|
# @option options [String] :password Password to authenticate against server
|
|
54
54
|
# @option options [Integer] :db (0) Database to select after connect and on reconnects
|
|
55
55
|
# @option options [Symbol] :driver Driver to use, currently supported: `:ruby`, `:hiredis`
|
|
56
|
+
# @option options [Integer] :protocol (3) RESP protocol version to negotiate (`HELLO`). Defaults
|
|
57
|
+
# to RESP3; set to `2` for RESP2. Servers without RESP3 support automatically fall back to RESP2.
|
|
56
58
|
# @option options [String] :id ID for the client connection, assigns name to current connection by sending
|
|
57
59
|
# `CLIENT SETNAME`
|
|
60
|
+
# @option options [String, Array<String>, false] :driver_info Identity a library built on top of `redis-rb`
|
|
61
|
+
# reports to the server via `CLIENT SETINFO`, shown as `lib-name=redis-rb(<driver_info>)` in `CLIENT LIST`.
|
|
62
|
+
# The recommended format is `<name>_v<version>`; an Array is joined with `;`. Pass `false` to disable
|
|
63
|
+
# client identification entirely.
|
|
58
64
|
# @option options [Integer, Array<Integer, Float>] :reconnect_attempts Number of attempts trying to connect,
|
|
59
65
|
# or a list of sleep duration between attempts.
|
|
60
66
|
# @option options [Boolean] :inherit_socket (false) Whether to use socket in forked process or not
|
|
@@ -69,16 +75,29 @@ class Redis
|
|
|
69
75
|
if ENV["REDIS_URL"] && SERVER_URL_OPTIONS.none? { |o| @options.key?(o) }
|
|
70
76
|
@options[:url] = ENV["REDIS_URL"]
|
|
71
77
|
end
|
|
72
|
-
|
|
78
|
+
# Kept as state, not just a local: the RESP3->RESP2 fallback rebuilds @client and must re-apply
|
|
79
|
+
# socket inheritance, otherwise fork safety would be silently lost after a downgrade.
|
|
80
|
+
@inherit_socket = @options.delete(:inherit_socket)
|
|
81
|
+
# HIMPORT fieldsets are server session state that dies with the physical connection; the
|
|
82
|
+
# registry remembers each prepared schema so a lost session can be repaired (see the
|
|
83
|
+
# himport_* overrides below). Deleted from @options so it never reaches RedisClient::Config.
|
|
84
|
+
@himport_auto_prepare = @options.delete(:himport_auto_prepare) != false
|
|
85
|
+
@himport_fieldsets = {}
|
|
73
86
|
@subscription_client = nil
|
|
74
87
|
|
|
75
|
-
@client =
|
|
76
|
-
@client.inherit_socket! if inherit_socket
|
|
88
|
+
@client = build_client
|
|
77
89
|
end
|
|
78
90
|
|
|
79
91
|
# Run code without the client reconnecting
|
|
80
92
|
def without_reconnect(&block)
|
|
81
|
-
@client
|
|
93
|
+
# Route through #synchronize like every other @client access: it holds @monitor and applies the
|
|
94
|
+
# RESP3->RESP2 fallback. disable_reconnection establishes the connection eagerly, so if that
|
|
95
|
+
# handshake fails the fallback rebuilds @client and the whole block re-runs against the new
|
|
96
|
+
# (RESP2) client — keeping disable_reconnection bound to the live @client rather than a discarded
|
|
97
|
+
# one. Referencing the block argument (not @client) is what makes the retry pick up the rebuild.
|
|
98
|
+
synchronize do |client|
|
|
99
|
+
client.disable_reconnection(&block)
|
|
100
|
+
end
|
|
82
101
|
end
|
|
83
102
|
|
|
84
103
|
# Test whether or not the client is connected
|
|
@@ -118,7 +137,10 @@ class Redis
|
|
|
118
137
|
end
|
|
119
138
|
|
|
120
139
|
def dup
|
|
121
|
-
|
|
140
|
+
# inherit_socket and himport_auto_prepare are stripped from @options before the client config
|
|
141
|
+
# is built (RedisClient::Config doesn't know them); merge the live values back so the
|
|
142
|
+
# duplicate keeps the caller's settings instead of silently reverting to defaults.
|
|
143
|
+
self.class.new(@options.merge(inherit_socket: @inherit_socket, himport_auto_prepare: @himport_auto_prepare))
|
|
122
144
|
end
|
|
123
145
|
|
|
124
146
|
def connection
|
|
@@ -131,8 +153,70 @@ class Redis
|
|
|
131
153
|
}
|
|
132
154
|
end
|
|
133
155
|
|
|
156
|
+
# HIMPORT overrides adding fieldset-loss recovery on top of the transport-pure methods in
|
|
157
|
+
# Commands::Hashes. The connection can be transparently replaced under the caller (reconnect
|
|
158
|
+
# after a network error, failover, RESET by a proxy), destroying every prepared fieldset. The
|
|
159
|
+
# server then answers HIMPORT SET with "no such fieldset" — the authoritative signal that the
|
|
160
|
+
# session was lost. These overrides remember the last schema prepared per fieldset name and,
|
|
161
|
+
# on that error, re-prepare it and retry the SET exactly once. An explicitly discarded
|
|
162
|
+
# fieldset is removed from the registry and is never resurrected. Disable the recovery with
|
|
163
|
+
# `Redis.new(himport_auto_prepare: false)`; the registry is still recorded for manual use.
|
|
164
|
+
|
|
165
|
+
# Each method holds @monitor across the server command AND its registry mutation: the two must
|
|
166
|
+
# be atomic with respect to other threads, otherwise a himport_set failing between another
|
|
167
|
+
# thread's DISCARD reply and its registry delete would still see the schema and re-prepare,
|
|
168
|
+
# resurrecting the fieldset the discard just removed. @monitor is reentrant, so the nested
|
|
169
|
+
# send_command/himport_prepare calls re-enter it safely.
|
|
170
|
+
|
|
171
|
+
def himport_prepare(fieldset_name, *fields)
|
|
172
|
+
fields.flatten!(1)
|
|
173
|
+
@monitor.synchronize do
|
|
174
|
+
reply = super(fieldset_name, fields)
|
|
175
|
+
@himport_fieldsets[fieldset_name.to_s] = fields.dup.freeze
|
|
176
|
+
reply
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def himport_set(key, fieldset_name, *values)
|
|
181
|
+
@monitor.synchronize do
|
|
182
|
+
super
|
|
183
|
+
rescue CommandError => error
|
|
184
|
+
fields = @himport_fieldsets[fieldset_name.to_s]
|
|
185
|
+
raise unless @himport_auto_prepare && fields && error.message.include?("no such fieldset")
|
|
186
|
+
|
|
187
|
+
himport_prepare(fieldset_name, fields)
|
|
188
|
+
# `super` (not himport_set) so a second failure propagates instead of recovering again.
|
|
189
|
+
super
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def himport_discard(fieldset_name)
|
|
194
|
+
@monitor.synchronize do
|
|
195
|
+
reply = super
|
|
196
|
+
@himport_fieldsets.delete(fieldset_name.to_s)
|
|
197
|
+
reply
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def himport_discard_all
|
|
202
|
+
@monitor.synchronize do
|
|
203
|
+
reply = super
|
|
204
|
+
@himport_fieldsets.clear
|
|
205
|
+
reply
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
134
209
|
private
|
|
135
210
|
|
|
211
|
+
# Builds @client from @options and applies any instance-level settings (socket inheritance) that
|
|
212
|
+
# live outside @options. Used both at construction and when the RESP3->RESP2 fallback rebuilds the
|
|
213
|
+
# client, so those settings survive a protocol downgrade.
|
|
214
|
+
def build_client
|
|
215
|
+
client = initialize_client(@options)
|
|
216
|
+
client.inherit_socket! if @inherit_socket
|
|
217
|
+
client
|
|
218
|
+
end
|
|
219
|
+
|
|
136
220
|
def initialize_client(options)
|
|
137
221
|
if options.key?(:cluster)
|
|
138
222
|
raise "Redis Cluster support was moved to the `redis-clustering` gem."
|
|
@@ -145,22 +229,64 @@ class Redis
|
|
|
145
229
|
end
|
|
146
230
|
end
|
|
147
231
|
|
|
232
|
+
# All access to @client funnels through here: it serializes on @monitor and applies the RESP3
|
|
233
|
+
# protocol fallback. Routing pipelined/multi/watch (which all call synchronize) through the same
|
|
234
|
+
# path means they fall back to RESP2 against pre-HELLO servers just like single commands do.
|
|
148
235
|
def synchronize
|
|
149
|
-
@monitor.synchronize
|
|
236
|
+
@monitor.synchronize do
|
|
237
|
+
with_protocol_fallback do
|
|
238
|
+
yield(@client)
|
|
239
|
+
end
|
|
240
|
+
end
|
|
150
241
|
end
|
|
151
242
|
|
|
152
243
|
def send_command(command, &block)
|
|
153
|
-
|
|
154
|
-
|
|
244
|
+
synchronize do |client|
|
|
245
|
+
client.call_v(command, &block)
|
|
155
246
|
end
|
|
156
247
|
rescue ::RedisClient::Error => error
|
|
157
248
|
Client.translate_error!(error)
|
|
158
249
|
end
|
|
159
250
|
|
|
160
251
|
def send_blocking_command(command, timeout, &block)
|
|
161
|
-
|
|
162
|
-
|
|
252
|
+
synchronize do |client|
|
|
253
|
+
client.blocking_call_v(timeout, command, &block)
|
|
163
254
|
end
|
|
255
|
+
rescue ::RedisClient::Error => error
|
|
256
|
+
Client.translate_error!(error)
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# We default to RESP3. Servers that don't support it reject the `HELLO 3` handshake (most
|
|
260
|
+
# notably Redis < 6.0, which has no HELLO command). Rebuild the client as RESP2 once so those
|
|
261
|
+
# servers keep working without the user setting `protocol: 2`.
|
|
262
|
+
#
|
|
263
|
+
# This is the single fallback point for every client type. Each one surfaces the resp3-unsupported
|
|
264
|
+
# error here untranslated (still a RedisClient::Error): standalone/distributed via
|
|
265
|
+
# Redis::Client#call_v et al., sentinel via the plain RedisClient (which never translates), and
|
|
266
|
+
# cluster via Redis::Cluster::Client#handle_errors. Because every @client access — single commands,
|
|
267
|
+
# pipelined, multi, watch, and the pub/sub socket — flows through #synchronize, wrapping it here
|
|
268
|
+
# covers them all.
|
|
269
|
+
#
|
|
270
|
+
# Must be called while holding @monitor: it closes and replaces @client, so it has to be
|
|
271
|
+
# serialized with the command execution that uses @client. The retried block re-reads @client, so
|
|
272
|
+
# callers must reference the rebuilt instance (via the synchronize block argument), not a cached
|
|
273
|
+
# one.
|
|
274
|
+
def with_protocol_fallback
|
|
275
|
+
yield
|
|
276
|
+
rescue ::RedisClient::Error => error
|
|
277
|
+
if @options.fetch(:protocol, 3).to_i == 3 && Client.resp3_unsupported?(error)
|
|
278
|
+
@options = @options.merge(protocol: 2)
|
|
279
|
+
@client.close
|
|
280
|
+
@client = build_client
|
|
281
|
+
# Warn only once the RESP2 client is actually in place — if the rebuild itself raises we
|
|
282
|
+
# haven't really fallen back. Fires once per client: @options[:protocol] is now 2, so this
|
|
283
|
+
# branch never re-enters. Passing `protocol: 2` explicitly skips it (and silences this).
|
|
284
|
+
warn("Redis: the server does not support RESP3 (the HELLO 3 handshake failed); falling back " \
|
|
285
|
+
"to RESP2. Pass `protocol: 2` to select RESP2 explicitly and silence this warning.")
|
|
286
|
+
retry
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
raise
|
|
164
290
|
end
|
|
165
291
|
|
|
166
292
|
def _subscription(method, timeout, channels, block)
|
|
@@ -170,7 +296,10 @@ class Redis
|
|
|
170
296
|
end
|
|
171
297
|
|
|
172
298
|
begin
|
|
173
|
-
|
|
299
|
+
# The pub/sub second socket is opened via @client.pubsub, which connects through
|
|
300
|
+
# ensure_connected rather than a command path. Route it through #synchronize so the same
|
|
301
|
+
# RESP3->RESP2 fallback applies when subscribe is the first operation against an old server.
|
|
302
|
+
@subscription_client = SubscribedClient.new(synchronize(&:pubsub))
|
|
174
303
|
if timeout > 0
|
|
175
304
|
@subscription_client.send(method, timeout, *channels, &block)
|
|
176
305
|
else
|
|
@@ -191,6 +320,7 @@ class Redis
|
|
|
191
320
|
end
|
|
192
321
|
|
|
193
322
|
require "redis/version"
|
|
323
|
+
require "redis/lib_identity"
|
|
194
324
|
require "redis/client"
|
|
195
325
|
require "redis/pipeline"
|
|
196
326
|
require "redis/subscribe"
|
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
|
+
version: 6.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ezra Zygmuntowicz
|
|
@@ -15,22 +15,22 @@ authors:
|
|
|
15
15
|
- Pieter Noordhuis
|
|
16
16
|
bindir: bin
|
|
17
17
|
cert_chain: []
|
|
18
|
-
date:
|
|
18
|
+
date: 2026-07-31 00:00:00.000000000 Z
|
|
19
19
|
dependencies:
|
|
20
20
|
- !ruby/object:Gem::Dependency
|
|
21
21
|
name: redis-client
|
|
22
22
|
requirement: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
|
-
- -
|
|
24
|
+
- - '='
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: 0.
|
|
26
|
+
version: 0.30.1
|
|
27
27
|
type: :runtime
|
|
28
28
|
prerelease: false
|
|
29
29
|
version_requirements: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- -
|
|
31
|
+
- - '='
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: 0.
|
|
33
|
+
version: 0.30.1
|
|
34
34
|
description: |2
|
|
35
35
|
A Ruby client that tries to match Redis' API one-to-one, while still
|
|
36
36
|
providing an idiomatic interface.
|
|
@@ -54,6 +54,18 @@ files:
|
|
|
54
54
|
- lib/redis/commands/hyper_log_log.rb
|
|
55
55
|
- lib/redis/commands/keys.rb
|
|
56
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
|
|
57
69
|
- lib/redis/commands/pubsub.rb
|
|
58
70
|
- lib/redis/commands/scripting.rb
|
|
59
71
|
- lib/redis/commands/server.rb
|
|
@@ -65,6 +77,7 @@ files:
|
|
|
65
77
|
- lib/redis/distributed.rb
|
|
66
78
|
- lib/redis/errors.rb
|
|
67
79
|
- lib/redis/hash_ring.rb
|
|
80
|
+
- lib/redis/lib_identity.rb
|
|
68
81
|
- lib/redis/pipeline.rb
|
|
69
82
|
- lib/redis/subscribe.rb
|
|
70
83
|
- lib/redis/version.rb
|
|
@@ -74,9 +87,9 @@ licenses:
|
|
|
74
87
|
metadata:
|
|
75
88
|
bug_tracker_uri: https://github.com/redis/redis-rb/issues
|
|
76
89
|
changelog_uri: https://github.com/redis/redis-rb/blob/master/CHANGELOG.md
|
|
77
|
-
documentation_uri: https://www.rubydoc.info/gems/redis/
|
|
90
|
+
documentation_uri: https://www.rubydoc.info/gems/redis/6.0.0
|
|
78
91
|
homepage_uri: https://github.com/redis/redis-rb
|
|
79
|
-
source_code_uri: https://github.com/redis/redis-rb/tree/
|
|
92
|
+
source_code_uri: https://github.com/redis/redis-rb/tree/v6.0.0
|
|
80
93
|
rdoc_options: []
|
|
81
94
|
require_paths:
|
|
82
95
|
- lib
|
|
@@ -84,7 +97,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
84
97
|
requirements:
|
|
85
98
|
- - ">="
|
|
86
99
|
- !ruby/object:Gem::Version
|
|
87
|
-
version: 2.
|
|
100
|
+
version: 3.2.0
|
|
88
101
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
89
102
|
requirements:
|
|
90
103
|
- - ">="
|