redis 4.8.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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +138 -1
  3. data/README.md +285 -169
  4. data/lib/redis/client.rb +116 -611
  5. data/lib/redis/commands/bitmaps.rb +14 -4
  6. data/lib/redis/commands/cluster.rb +1 -18
  7. data/lib/redis/commands/connection.rb +5 -10
  8. data/lib/redis/commands/geo.rb +109 -7
  9. data/lib/redis/commands/hashes.rb +179 -8
  10. data/lib/redis/commands/hyper_log_log.rb +1 -1
  11. data/lib/redis/commands/keys.rb +32 -24
  12. data/lib/redis/commands/lists.rb +167 -25
  13. data/lib/redis/commands/modules/json.rb +530 -0
  14. data/lib/redis/commands/modules/search/aggregation.rb +418 -0
  15. data/lib/redis/commands/modules/search/dialect.rb +14 -0
  16. data/lib/redis/commands/modules/search/field.rb +306 -0
  17. data/lib/redis/commands/modules/search/hybrid.rb +359 -0
  18. data/lib/redis/commands/modules/search/index.rb +351 -0
  19. data/lib/redis/commands/modules/search/index_definition.rb +114 -0
  20. data/lib/redis/commands/modules/search/miscellaneous.rb +607 -0
  21. data/lib/redis/commands/modules/search/query.rb +738 -0
  22. data/lib/redis/commands/modules/search/result.rb +488 -0
  23. data/lib/redis/commands/modules/search/schema.rb +211 -0
  24. data/lib/redis/commands/modules/search.rb +19 -0
  25. data/lib/redis/commands/pubsub.rb +34 -25
  26. data/lib/redis/commands/server.rb +15 -15
  27. data/lib/redis/commands/sets.rb +76 -40
  28. data/lib/redis/commands/sorted_sets.rb +128 -19
  29. data/lib/redis/commands/streams.rb +75 -28
  30. data/lib/redis/commands/strings.rb +18 -17
  31. data/lib/redis/commands/transactions.rb +7 -31
  32. data/lib/redis/commands.rb +39 -20
  33. data/lib/redis/distributed.rb +407 -73
  34. data/lib/redis/errors.rb +20 -50
  35. data/lib/redis/hash_ring.rb +26 -26
  36. data/lib/redis/lib_identity.rb +105 -0
  37. data/lib/redis/pipeline.rb +47 -222
  38. data/lib/redis/subscribe.rb +51 -15
  39. data/lib/redis/version.rb +1 -1
  40. data/lib/redis.rb +213 -188
  41. metadata +25 -59
  42. data/lib/redis/cluster/command.rb +0 -79
  43. data/lib/redis/cluster/command_loader.rb +0 -33
  44. data/lib/redis/cluster/key_slot_converter.rb +0 -72
  45. data/lib/redis/cluster/node.rb +0 -120
  46. data/lib/redis/cluster/node_key.rb +0 -31
  47. data/lib/redis/cluster/node_loader.rb +0 -34
  48. data/lib/redis/cluster/option.rb +0 -100
  49. data/lib/redis/cluster/slot.rb +0 -86
  50. data/lib/redis/cluster/slot_loader.rb +0 -46
  51. data/lib/redis/cluster.rb +0 -315
  52. data/lib/redis/connection/command_helper.rb +0 -41
  53. data/lib/redis/connection/hiredis.rb +0 -68
  54. data/lib/redis/connection/registry.rb +0 -13
  55. data/lib/redis/connection/ruby.rb +0 -437
  56. data/lib/redis/connection/synchrony.rb +0 -148
  57. data/lib/redis/connection.rb +0 -11
data/README.md CHANGED
@@ -1,7 +1,6 @@
1
- # redis-rb [![Build Status][gh-actions-image]][gh-actions-link] [![Inline docs][inchpages-image]][inchpages-link]
1
+ # redis-rb [![Build Status][gh-actions-image]][gh-actions-link] [![Inline docs][rdoc-master-image]][rdoc-master-link]
2
2
 
3
- A Ruby client that tries to match [Redis][redis-home]' API one-to-one, while still
4
- providing an idiomatic interface.
3
+ A Ruby client that tries to match [Redis][redis-home]' API one-to-one, while still providing an idiomatic interface.
5
4
 
6
5
  See [RubyDoc.info][rubydoc] for the API docs of the latest published gem.
7
6
 
@@ -35,13 +34,9 @@ You can also specify connection options as a [`redis://` URL][redis-url]:
35
34
  redis = Redis.new(url: "redis://:p4ssw0rd@10.0.1.1:6380/15")
36
35
  ```
37
36
 
38
- The client expects passwords with special chracters to be URL-encoded (i.e.
37
+ The client expects passwords with special characters to be URL-encoded (i.e.
39
38
  `CGI.escape(password)`).
40
39
 
41
- By default, the client will try to read the `REDIS_URL` environment variable
42
- and use that as URL to connect to. The above statement is therefore equivalent
43
- to setting this environment variable and calling `Redis.new` without arguments.
44
-
45
40
  To connect to Redis listening on a Unix socket, try:
46
41
 
47
42
  ```ruby
@@ -76,10 +71,121 @@ redis.get("mykey")
76
71
  All commands, their arguments, and return values are documented and
77
72
  available on [RubyDoc.info][rubydoc].
78
73
 
74
+ ## Language and server support
75
+
76
+ redis-rb targets actively supported runtimes on both the language and the server side:
77
+
78
+ - **Ruby:** Ruby 3.2 and newer. See the [Ruby maintenance branches][ruby-branches] page for
79
+ each version's status and dates.
80
+ - **Redis server:** the versions designated for support by Redis. See
81
+ [Supported Redis database versions][redis-versions].
82
+
83
+ ## Protocol (RESP3)
84
+
85
+ Starting in 6.0, the client negotiates the [RESP3 protocol][resp3] (`HELLO 3`)
86
+ by default. Command return values are unchanged from 5.x, with one exception:
87
+ `GEOPOS` and `GEOSEARCH`/`GEORADIUS` with `WITHCOORD` now return coordinates as
88
+ `Float` instead of `String`.
89
+
90
+ To keep the previous RESP2 behavior, pass `protocol: 2`:
91
+
92
+ ```ruby
93
+ redis = Redis.new(protocol: 2)
94
+ ```
95
+
96
+ Servers without RESP3 support (Redis < 6.0, or anything replying `NOPROTO`) are
97
+ detected on connect and the client transparently falls back to RESP2, so no
98
+ configuration is needed for older servers.
99
+
100
+ ### Why RESP3 is the default
101
+
102
+ RESP3's richer wire types let the parser deliver replies already in their final
103
+ Ruby shape. Under RESP2, structured replies arrive as flat arrays of bulk
104
+ strings and the client re-shapes them in Ruby: `HGETALL` turns a flat
105
+ `[field, value, field, value, ...]` array into a `Hash`, and sorted-set scores
106
+ are converted from `String` to `Float` pair by pair. Under RESP3 the server
107
+ tags these replies as native maps and doubles, so the final `Hash` and `Float`
108
+ values come straight out of the parser and the Ruby-side re-shaping pass
109
+ disappears entirely.
110
+
111
+ How much that saves depends on where parsing happens. In our benchmarks
112
+ ([bench/resp_comparison.rb](bench/resp_comparison.rb), Ruby 3.4, 100-element
113
+ replies), hash reads (`HGETALL`) consistently use ~10–25% less client CPU per
114
+ call on both drivers. With the [hiredis driver](#hiredis-binding), where
115
+ parsing runs in C, sorted-set reads with scores gain up to 16% throughput and
116
+ ~20% less CPU per call on top of that; with the pure-Ruby driver they are
117
+ unchanged, since the parser then spends in Ruby roughly what the re-shaping
118
+ pass used to cost. Simple string commands and stream commands are unaffected
119
+ either way — their reply shapes are the same in both protocols. In short:
120
+ RESP3 is never slower where it matters, and it pairs best with hiredis — that
121
+ combination moves all reply construction out of Ruby and into C.
122
+
123
+ Beyond performance, RESP3 unlocks protocol capabilities RESP2 simply doesn't
124
+ have. The most important is out-of-band **push messages**: the server can send
125
+ notifications on a connection without the client asking, which is the
126
+ foundation for server-assisted client-side caching (`CLIENT TRACKING`
127
+ invalidation events), pub/sub messages delivered over the regular command
128
+ connection instead of a dedicated one, and other server-initiated
129
+ notifications. Defaulting to RESP3 in 6.0 lays the groundwork for building
130
+ these features in future releases without another protocol migration.
131
+
132
+ See [the RESP3 migration guide](specs/migration-resp3.md) for full details.
133
+
134
+ ## Client identification
135
+
136
+ On connect the client identifies itself to the server with `CLIENT SETINFO`, so
137
+ `redis-rb` and its version are visible in `CLIENT LIST` and `CLIENT INFO`:
138
+
139
+ ```
140
+ lib-name=redis-rb lib-ver=<Redis::VERSION>
141
+ ```
142
+
143
+ Libraries built on top of `redis-rb` can add their own identity with
144
+ `driver_info:`, which is reported alongside it. The recommended suffix format
145
+ is `<name>_v<version>`, following the convention used by the official client
146
+ libraries:
147
+
148
+ ```ruby
149
+ Redis.new(driver_info: "my-gem_v#{MyGem::VERSION}")
150
+ # reported as: lib-name=redis-rb(my-gem_v1.0.0) lib-ver=<Redis::VERSION>
151
+ ```
152
+
153
+ `driver_info:` also accepts an array, joined with `;` (the conventional
154
+ delimiter for multiple suffixes). It extends the reported name rather than
155
+ replacing it, so `redis-rb` stays identifiable either way. Runs of characters
156
+ the server would reject (spaces, non-printable bytes) and of the parentheses
157
+ that delimit the suffix are each replaced with a single `_`, or dropped at the
158
+ edges of the value.
159
+
160
+ Servers older than 7.2 don't support `CLIENT SETINFO`; they reject it, the error
161
+ is ignored, and the connection is used as normal. If a proxy or server can't
162
+ tolerate the command at all, pass `driver_info: false` to disable client
163
+ identification entirely.
164
+
165
+ ## Connection Pooling and Thread safety
166
+
167
+ The client does not provide connection pooling. Each `Redis` instance
168
+ has one and only one connection to the server, and use of this connection
169
+ is protected by a mutex.
170
+
171
+ As such it is heavily recommended to use the [`connection_pool` gem](https://github.com/mperham/connection_pool), e.g.:
172
+
173
+ ```ruby
174
+ module MyApp
175
+ def self.redis
176
+ @redis ||= ConnectionPool::Wrapper.new do
177
+ Redis.new(url: ENV["REDIS_URL"])
178
+ end
179
+ end
180
+ end
181
+
182
+ MyApp.redis.incr("some-counter")
183
+ ```
184
+
79
185
  ## Sentinel support
80
186
 
81
187
  The client is able to perform automatic failover by using [Redis
82
- Sentinel](http://redis.io/topics/sentinel). Make sure to run Redis 2.8+
188
+ Sentinel](http://redis.io/topics/sentinel). Make sure to run Redis 2.8+
83
189
  if you want to use this feature.
84
190
 
85
191
  To connect using Sentinel, use:
@@ -88,7 +194,7 @@ To connect using Sentinel, use:
88
194
  SENTINELS = [{ host: "127.0.0.1", port: 26380 },
89
195
  { host: "127.0.0.1", port: 26381 }]
90
196
 
91
- redis = Redis.new(url: "redis://mymaster", sentinels: SENTINELS, role: :master)
197
+ redis = Redis.new(name: "mymaster", sentinels: SENTINELS, role: :master)
92
198
  ```
93
199
 
94
200
  * The master name identifies a group of Redis instances composed of a master
@@ -99,91 +205,50 @@ and `slave`. When the role is `slave`, the client will try to connect to a
99
205
  random slave of the specified master. If a role is not specified, the client
100
206
  will connect to the master.
101
207
 
102
- * When using the Sentinel support you need to specify a list of sentinels to
208
+ * When using Sentinel support, you need to specify a list of sentinels to
103
209
  connect to. The list does not need to enumerate all your Sentinel instances,
104
210
  but a few so that if one is down the client will try the next one. The client
105
211
  is able to remember the last Sentinel that was able to reply correctly and will
106
- use it for the next requests.
212
+ use it for the next request.
107
213
 
108
- If you want to [authenticate](https://redis.io/topics/sentinel#configuring-sentinel-instances-with-authentication) Sentinel itself, you must specify the `password` option per instance.
214
+ To [authenticate](https://redis.io/docs/management/sentinel/#configuring-sentinel-instances-with-authentication) with Sentinel itself, you can specify the `sentinel_username` and `sentinel_password`. Exclude the `sentinel_username` option if you're using password-only authentication.
109
215
 
110
216
  ```ruby
111
- SENTINELS = [{ host: '127.0.0.1', port: 26380, password: 'mysecret' },
112
- { host: '127.0.0.1', port: 26381, password: 'mysecret' }]
217
+ SENTINELS = [{ host: '127.0.0.1', port: 26380},
218
+ { host: '127.0.0.1', port: 26381}]
113
219
 
114
- redis = Redis.new(host: 'mymaster', sentinels: SENTINELS, role: :master)
220
+ redis = Redis.new(name: 'mymaster', sentinels: SENTINELS, sentinel_username: 'appuser', sentinel_password: 'mysecret', role: :master)
115
221
  ```
116
222
 
117
- ## Cluster support
118
-
119
- `redis-rb` supports [clustering](https://redis.io/topics/cluster-spec).
223
+ If you specify a username and/or password at the top level for your main Redis instance, Sentinel *will not* use those credentials.
120
224
 
121
225
  ```ruby
122
- # Nodes can be passed to the client as an array of connection URLs.
123
- nodes = (7000..7005).map { |port| "redis://127.0.0.1:#{port}" }
124
- redis = Redis.new(cluster: nodes)
125
-
126
- # You can also specify the options as a Hash. The options are the same as for a single server connection.
127
- (7000..7005).map { |port| { host: '127.0.0.1', port: port } }
128
- ```
129
-
130
- You can also specify only a subset of the nodes, and the client will discover the missing ones using the [CLUSTER NODES](https://redis.io/commands/cluster-nodes) command.
131
-
132
- ```ruby
133
- Redis.new(cluster: %w[redis://127.0.0.1:7000])
134
- ```
135
-
136
- If you want [the connection to be able to read from any replica](https://redis.io/commands/readonly), you must pass the `replica: true`. Note that this connection won't be usable to write keys.
226
+ # Use 'mysecret' to authenticate against the mymaster instance, but skip authentication for the sentinels:
227
+ SENTINELS = [{ host: '127.0.0.1', port: 26380 },
228
+ { host: '127.0.0.1', port: 26381 }]
137
229
 
138
- ```ruby
139
- Redis.new(cluster: nodes, replica: true)
230
+ redis = Redis.new(name: 'mymaster', sentinels: SENTINELS, role: :master, password: 'mysecret')
140
231
  ```
141
232
 
142
- The calling code is responsible for [avoiding cross slot commands](https://redis.io/topics/cluster-spec#keys-distribution-model).
233
+ So you have to provide Sentinel credentials and Redis explicitly even if they are the same.
143
234
 
144
235
  ```ruby
145
- redis = Redis.new(cluster: %w[redis://127.0.0.1:7000])
146
-
147
- redis.mget('key1', 'key2')
148
- #=> Redis::CommandError (CROSSSLOT Keys in request don't hash to the same slot)
149
-
150
- redis.mget('{key}1', '{key}2')
151
- #=> [nil, nil]
152
- ```
236
+ # Use 'mysecret' to authenticate against the mymaster instance and sentinel
237
+ SENTINELS = [{ host: '127.0.0.1', port: 26380 },
238
+ { host: '127.0.0.1', port: 26381 }]
153
239
 
154
- * The client automatically reconnects after a failover occurred, but the caller is responsible for handling errors while it is happening.
155
- * The client support permanent node failures, and will reroute requests to promoted slaves.
156
- * The client supports `MOVED` and `ASK` redirections transparently.
157
-
158
- ## Cluster mode with SSL/TLS
159
- Since Redis can return FQDN of nodes in reply to client since `7.*` with CLUSTER commands, we can use cluster feature with SSL/TLS connection like this:
160
-
161
- ```ruby
162
- Redis.new(cluster: %w[rediss://foo.example.com:6379])
240
+ redis = Redis.new(name: 'mymaster', sentinels: SENTINELS, role: :master, password: 'mysecret', sentinel_password: 'mysecret')
163
241
  ```
164
242
 
165
- On the other hand, in Redis versions prior to `6.*`, you can specify options like the following if cluster mode is enabled and client has to connect to nodes via single endpoint with SSL/TLS.
243
+ Also, the `name`, `password`, `username`, and `db` for the Redis instance can be passed as a URL:
166
244
 
167
245
  ```ruby
168
- Redis.new(cluster: %w[rediss://foo-endpoint.example.com:6379], fixed_hostname: 'foo-endpoint.example.com')
246
+ redis = Redis.new(url: "redis://appuser:mysecret@mymaster/10", sentinels: SENTINELS, role: :master)
169
247
  ```
170
248
 
171
- In case of the above architecture, if you don't pass the `fixed_hostname` option to the client and servers return IP addresses of nodes, the client may fail to verify certificates.
172
-
173
- ## Storing objects
174
-
175
- Redis "string" types can be used to store serialized Ruby objects, for
176
- example with JSON:
177
-
178
- ```ruby
179
- require "json"
180
-
181
- redis.set "foo", [1, 2, 3].to_json
182
- # => OK
249
+ ## Cluster support
183
250
 
184
- JSON.parse(redis.get("foo"))
185
- # => [1, 2, 3]
186
- ```
251
+ [Clustering](https://redis.io/topics/cluster-spec). is supported via the [`redis-clustering` gem](cluster/).
187
252
 
188
253
  ## Pipelining
189
254
 
@@ -206,6 +271,39 @@ end
206
271
  # => ["OK", 1]
207
272
  ```
208
273
 
274
+ Commands must be called on the yielded objects. If you call methods
275
+ on the original client objects from inside a pipeline, they will be sent immediately:
276
+
277
+ ```ruby
278
+ redis.pipelined do |pipeline|
279
+ pipeline.set "foo", "bar"
280
+ redis.incr "baz" # => 1
281
+ end
282
+ # => ["OK"]
283
+ ```
284
+
285
+ ### Exception management
286
+
287
+ The `exception` flag in the `#pipelined` is a feature that modifies the pipeline execution behavior. When set
288
+ to `false`, it doesn't raise an exception when a command error occurs. Instead, it allows the pipeline to execute all
289
+ commands, and any failed command will be available in the returned array. (Defaults to `true`)
290
+
291
+ ```ruby
292
+ results = redis.pipelined(exception: false) do |pipeline|
293
+ pipeline.set('key1', 'value1')
294
+ pipeline.lpush('key1', 'something') # This will fail
295
+ pipeline.set('key2', 'value2')
296
+ end
297
+ # results => ["OK", #<RedisClient::WrongTypeError: WRONGTYPE Operation against a key holding the wrong kind of value>, "OK"]
298
+
299
+ results.each do |result|
300
+ if result.is_a?(Redis::CommandError)
301
+ # Do something with the failed result
302
+ end
303
+ end
304
+ ```
305
+
306
+
209
307
  ### Executing commands atomically
210
308
 
211
309
  You can use `MULTI/EXEC` to run a number of commands in an atomic
@@ -225,24 +323,89 @@ end
225
323
  ### Futures
226
324
 
227
325
  Replies to commands in a pipeline can be accessed via the *futures* they
228
- emit (since redis-rb 3.0). All calls on the pipeline object return a
326
+ emit. All calls on the pipeline object return a
229
327
  `Future` object, which responds to the `#value` method. When the
230
328
  pipeline has successfully executed, all futures are assigned their
231
329
  respective replies and can be used.
232
330
 
233
331
  ```ruby
332
+ set = incr = nil
234
333
  redis.pipelined do |pipeline|
235
- @set = pipeline.set "foo", "bar"
236
- @incr = pipeline.incr "baz"
334
+ set = pipeline.set "foo", "bar"
335
+ incr = pipeline.incr "baz"
237
336
  end
238
337
 
239
- @set.value
338
+ set.value
240
339
  # => "OK"
241
340
 
242
- @incr.value
341
+ incr.value
243
342
  # => 1
244
343
  ```
245
344
 
345
+ ## Bulk hash ingestion (HIMPORT)
346
+
347
+ > **Experimental:** HIMPORT support is experimental. The client API (method
348
+ > signatures, reply aggregation on cluster, and the automatic re-prepare
349
+ > behavior) may change in a future minor release without a major version bump.
350
+
351
+ Redis 8.10 adds the `HIMPORT` command family for loading many hashes that share
352
+ the same set of field names: register the field names once with
353
+ `himport_prepare`, then create each hash by sending only its values. Keys
354
+ written this way are regular hashes — every hash command works on them.
355
+
356
+ ```ruby
357
+ redis.himport_prepare("users", ["name", "email", "age"])
358
+ redis.himport_set("user:1", "users", ["alice", "alice@example.com", "25"])
359
+ redis.himport_set("user:2", "users", ["bob", "bob@example.com", "30"])
360
+ redis.himport_discard("users") # => 1
361
+ ```
362
+
363
+ Values pair positionally with the prepared fields. Note that hash enumeration
364
+ order (`HGETALL`, `HKEYS`) is not guaranteed to match the prepare order.
365
+
366
+ ### Fieldsets are connection state
367
+
368
+ A prepared fieldset lives in the server-side session of the physical connection
369
+ that prepared it: it is invisible to other connections and destroyed by a
370
+ disconnect or `RESET`. A `himport_set` on a connection without the fieldset
371
+ fails with `ERR no such fieldset`.
372
+
373
+ Because a `Redis` instance transparently replaces a dead connection (see
374
+ [Reconnections](#reconnections)), the client keeps the last schema prepared for
375
+ each fieldset name and, when a `himport_set` reports the fieldset is gone,
376
+ re-prepares it and retries the command once. Explicitly discarded fieldsets are
377
+ never restored. To keep the fieldset lifecycle fully explicit instead, disable
378
+ the recovery:
379
+
380
+ ```ruby
381
+ redis = Redis.new(himport_auto_prepare: false)
382
+ ```
383
+
384
+ For the highest ingestion throughput, send the `PREPARE` and its `SET`s as one
385
+ pipeline — a single batch always executes on a single connection:
386
+
387
+ ```ruby
388
+ redis.pipelined do |pipeline|
389
+ pipeline.himport_prepare("users", ["name", "email", "age"])
390
+ rows.each { |id, row| pipeline.himport_set("user:#{id}", "users", row) }
391
+ end
392
+ ```
393
+
394
+ If the `PREPARE` in a batch fails, every `SET` in it fails with
395
+ `no such fieldset` — the `PREPARE` error is the root cause. Note that the
396
+ automatic re-prepare applies to direct calls only, not to commands inside
397
+ `pipelined`/`multi` blocks.
398
+
399
+ When using the `connection_pool` gem, each checkout may hand you a different
400
+ underlying connection: run `himport_prepare` and its `himport_set` calls within
401
+ one checkout (`pool.with { |redis| ... }`), ideally as one pipelined block.
402
+
403
+ With `Redis::Distributed`, `himport_prepare`, `himport_discard` and
404
+ `himport_discard_all` fan out to every ring node and return an array with one
405
+ reply per node; `himport_set` routes by key. With `Redis::Cluster`, the same
406
+ commands fan out to every master node and return a single aggregated reply,
407
+ matching the standalone API.
408
+
246
409
  ## Error Handling
247
410
 
248
411
  In general, if something goes wrong you'll get an exception. For example, if
@@ -251,7 +414,7 @@ it can't connect to the server a `Redis::CannotConnectError` error will be raise
251
414
  ```ruby
252
415
  begin
253
416
  redis.ping
254
- rescue StandardError => e
417
+ rescue Redis::BaseError => e
255
418
  e.inspect
256
419
  # => #<Redis::CannotConnectError: Timed out connecting to Redis on 10.0.1.1:6380>
257
420
 
@@ -265,6 +428,7 @@ See lib/redis/errors.rb for information about what exceptions are possible.
265
428
  ## Timeouts
266
429
 
267
430
  The client allows you to configure connect, read, and write timeouts.
431
+ Starting in version 5.0, the default for each is 1. Before that, it was 5.
268
432
  Passing a single `timeout` option will set all three values:
269
433
 
270
434
  ```ruby
@@ -298,56 +462,38 @@ If no message is received after 5 seconds, the client will unsubscribe.
298
462
 
299
463
  ## Reconnections
300
464
 
301
- The client allows you to configure how many `reconnect_attempts` it should
302
- complete before declaring a connection as failed. Furthermore, you may want
303
- to control the maximum duration between reconnection attempts with
304
- `reconnect_delay` and `reconnect_delay_max`.
465
+ **By default**, this gem will only **retry a connection once** and then fail, but
466
+ the client allows you to configure how many `reconnect_attempts` it should
467
+ complete before declaring a connection as failed.
305
468
 
306
469
  ```ruby
307
- Redis.new(
308
- :reconnect_attempts => 10,
309
- :reconnect_delay => 1.5,
310
- :reconnect_delay_max => 10.0,
311
- )
470
+ Redis.new(reconnect_attempts: 0)
471
+ Redis.new(reconnect_attempts: 3)
312
472
  ```
313
473
 
314
- The delay values are specified in seconds. With the above configuration, the
315
- client would attempt 10 reconnections, exponentially increasing the duration
316
- between each attempt but it never waits longer than `reconnect_delay_max`.
317
-
318
- This is the retry algorithm:
474
+ If you wish to wait between reconnection attempts, you can instead pass a list
475
+ of durations:
319
476
 
320
477
  ```ruby
321
- attempt_wait_time = [(reconnect_delay * 2**(attempt-1)), reconnect_delay_max].min
478
+ Redis.new(reconnect_attempts: [
479
+ 0, # retry immediately
480
+ 0.25, # retry a second time after 250ms
481
+ 1, # retry a third and final time after another 1s
482
+ ])
322
483
  ```
323
484
 
324
- **By default**, this gem will only **retry a connection once** and then fail, but with the
325
- above configuration the reconnection attempt would look like this:
326
-
327
- #|Attempt wait time|Total wait time
328
- :-:|:-:|:-:
329
- 1|1.5s|1.5s
330
- 2|3.0s|4.5s
331
- 3|6.0s|10.5s
332
- 4|10.0s|20.5s
333
- 5|10.0s|30.5s
334
- 6|10.0s|40.5s
335
- 7|10.0s|50.5s
336
- 8|10.0s|60.5s
337
- 9|10.0s|70.5s
338
- 10|10.0s|80.5s
485
+ If you wish to disable reconnection only for some commands, you can use
486
+ `disable_reconnection`:
339
487
 
340
- So if the reconnection attempt #10 succeeds 70 seconds have elapsed trying
341
- to reconnect, this is likely fine in long-running background processes, but if
342
- you use Redis to drive your website you might want to have a lower
343
- `reconnect_delay_max` or have less `reconnect_attempts`.
488
+ ```ruby
489
+ redis.get("some-key") # this may be retried
490
+ redis.disable_reconnection do
491
+ redis.incr("some-counter") # this won't be retried.
492
+ end
493
+ ```
344
494
 
345
495
  ## SSL/TLS Support
346
496
 
347
- This library supports natively terminating client side SSL/TLS connections
348
- when talking to Redis via a server-side proxy such as [stunnel], [hitch],
349
- or [ghostunnel].
350
-
351
497
  To enable SSL support, pass the `:ssl => true` option when configuring the
352
498
  Redis client, or pass in `:url => "rediss://..."` (like HTTPS for Redis).
353
499
  You will also need to pass in an `:ssl_params => { ... }` hash used to
@@ -381,13 +527,7 @@ redis = Redis.new(
381
527
  )
382
528
  ```
383
529
 
384
- [stunnel]: https://www.stunnel.org/
385
- [hitch]: https://hitch-tls.org/
386
- [ghostunnel]: https://github.com/square/ghostunnel
387
- [OpenSSL::SSL::SSLContext documentation]: http://ruby-doc.org/stdlib-2.3.0/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html
388
-
389
- *NOTE:* SSL is only supported by the default "Ruby" driver
390
-
530
+ [OpenSSL::SSL::SSLContext documentation]: http://ruby-doc.org/stdlib-2.5.0/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html
391
531
 
392
532
  ## Expert-Mode Options
393
533
 
@@ -401,17 +541,9 @@ redis = Redis.new(
401
541
  Improper use of `inherit_socket` will result in corrupted and/or incorrect
402
542
  responses.
403
543
 
404
- ## Alternate drivers
544
+ ## hiredis binding
405
545
 
406
546
  By default, redis-rb uses Ruby's socket library to talk with Redis.
407
- To use an alternative connection driver it should be specified as option
408
- when instantiating the client object. These instructions are only valid
409
- for **redis-rb 3.0**. For instructions on how to use alternate drivers from
410
- **redis-rb 2.2**, please refer to an [older README][readme-2.2.2].
411
-
412
- [readme-2.2.2]: https://github.com/redis/redis-rb/blob/v2.2.2/README.md
413
-
414
- ### hiredis
415
547
 
416
548
  The hiredis driver uses the connection facility of hiredis-rb. In turn,
417
549
  hiredis-rb is a binding to the official hiredis client library. It
@@ -421,48 +553,29 @@ extension, JRuby is not supported (by default).
421
553
  It is best to use hiredis when you have large replies (for example:
422
554
  `LRANGE`, `SMEMBERS`, `ZRANGE`, etc.) and/or use big pipelines.
423
555
 
424
- In your Gemfile, include hiredis:
556
+ In your Gemfile, include `hiredis-client`:
425
557
 
426
558
  ```ruby
427
- gem "redis", "~> 3.0.1"
428
- gem "hiredis", "~> 0.4.5"
559
+ gem "redis"
560
+ gem "hiredis-client"
429
561
  ```
430
562
 
431
- When instantiating the client object, specify hiredis:
563
+ If your application doesn't call `Bundler.require`, you may have
564
+ to require it explicitly:
432
565
 
433
566
  ```ruby
434
- redis = Redis.new(:driver => :hiredis)
435
- ```
436
-
437
- ### synchrony
567
+ require "hiredis-client"
568
+ ````
438
569
 
439
- The synchrony driver adds support for [em-synchrony][em-synchrony].
440
- This makes redis-rb work with EventMachine's asynchronous I/O, while not
441
- changing the exposed API. The hiredis gem needs to be available as
442
- well, because the synchrony driver uses hiredis for parsing the Redis
443
- protocol.
570
+ This makes the hiredis driver the default.
444
571
 
445
- [em-synchrony]: https://github.com/igrigorik/em-synchrony
446
-
447
- In your Gemfile, include em-synchrony and hiredis:
572
+ If you want to be certain hiredis is being used, when instantiating
573
+ the client object, specify hiredis:
448
574
 
449
575
  ```ruby
450
- gem "redis", "~> 3.0.1"
451
- gem "hiredis", "~> 0.4.5"
452
- gem "em-synchrony"
576
+ redis = Redis.new(driver: :hiredis)
453
577
  ```
454
578
 
455
- When instantiating the client object, specify synchrony:
456
-
457
- ```ruby
458
- redis = Redis.new(:driver => :synchrony)
459
- ```
460
-
461
- ## Testing
462
-
463
- This library is tested against recent Ruby and Redis versions.
464
- Check [Github Actions][gh-actions-link] for the exact versions supported.
465
-
466
579
  ## See Also
467
580
 
468
581
  - [async-redis](https://github.com/socketry/async-redis) — An [async](https://github.com/socketry/async) compatible Redis client.
@@ -480,11 +593,14 @@ client and evangelized Redis in Rubyland. Thank you, Ezra.
480
593
  requests.
481
594
 
482
595
 
483
- [inchpages-image]: https://inch-ci.org/github/redis/redis-rb.svg
484
- [inchpages-link]: https://inch-ci.org/github/redis/redis-rb
485
- [redis-commands]: https://redis.io/commands
486
- [redis-home]: https://redis.io
487
- [redis-url]: http://www.iana.org/assignments/uri-schemes/prov/redis
488
- [gh-actions-image]: https://github.com/redis/redis-rb/workflows/Test/badge.svg
489
- [gh-actions-link]: https://github.com/redis/redis-rb/actions
490
- [rubydoc]: http://www.rubydoc.info/gems/redis
596
+ [rdoc-master-image]: https://img.shields.io/badge/docs-rdoc.info-blue.svg
597
+ [rdoc-master-link]: https://rubydoc.info/github/redis/redis-rb
598
+ [redis-commands]: https://redis.io/commands
599
+ [redis-home]: https://redis.io
600
+ [redis-url]: https://www.iana.org/assignments/uri-schemes/prov/redis
601
+ [gh-actions-image]: https://github.com/redis/redis-rb/workflows/Test/badge.svg
602
+ [gh-actions-link]: https://github.com/redis/redis-rb/actions
603
+ [rubydoc]: https://rubydoc.info/gems/redis
604
+ [resp3]: https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md
605
+ [ruby-branches]: https://www.ruby-lang.org/en/downloads/branches/
606
+ [redis-versions]: https://redis.io/docs/latest/operate/rc/databases/version-management/#supported-database-versions