redis 4.6.0 → 5.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +70 -1
- data/README.md +75 -146
- data/lib/redis/client.rb +77 -616
- data/lib/redis/commands/bitmaps.rb +4 -1
- data/lib/redis/commands/cluster.rb +1 -18
- data/lib/redis/commands/connection.rb +5 -10
- data/lib/redis/commands/geo.rb +3 -3
- data/lib/redis/commands/hashes.rb +9 -6
- data/lib/redis/commands/hyper_log_log.rb +1 -1
- data/lib/redis/commands/keys.rb +53 -27
- data/lib/redis/commands/lists.rb +19 -23
- data/lib/redis/commands/pubsub.rb +7 -25
- data/lib/redis/commands/server.rb +15 -15
- data/lib/redis/commands/sets.rb +43 -36
- data/lib/redis/commands/sorted_sets.rb +27 -13
- data/lib/redis/commands/streams.rb +39 -19
- data/lib/redis/commands/strings.rb +18 -17
- data/lib/redis/commands/transactions.rb +26 -3
- data/lib/redis/commands.rb +4 -9
- data/lib/redis/distributed.rb +100 -67
- data/lib/redis/errors.rb +15 -41
- data/lib/redis/hash_ring.rb +26 -26
- data/lib/redis/pipeline.rb +56 -203
- data/lib/redis/subscribe.rb +23 -15
- data/lib/redis/version.rb +1 -1
- data/lib/redis.rb +90 -178
- metadata +9 -53
- data/lib/redis/cluster/command.rb +0 -79
- data/lib/redis/cluster/command_loader.rb +0 -33
- data/lib/redis/cluster/key_slot_converter.rb +0 -72
- data/lib/redis/cluster/node.rb +0 -120
- data/lib/redis/cluster/node_key.rb +0 -31
- data/lib/redis/cluster/node_loader.rb +0 -37
- data/lib/redis/cluster/option.rb +0 -93
- data/lib/redis/cluster/slot.rb +0 -86
- data/lib/redis/cluster/slot_loader.rb +0 -49
- data/lib/redis/cluster.rb +0 -315
- data/lib/redis/connection/command_helper.rb +0 -41
- data/lib/redis/connection/hiredis.rb +0 -68
- data/lib/redis/connection/registry.rb +0 -13
- data/lib/redis/connection/ruby.rb +0 -431
- data/lib/redis/connection/synchrony.rb +0 -148
- data/lib/redis/connection.rb +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7b000349da8b2f7ae8ed14909175306d2c719a9873bf1dabe05c4719ae96d0da
|
4
|
+
data.tar.gz: fdcda2f50f9d33265f7ba24d3f29a2e078fabe5240e1eaf01efae51e0b83eb0b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 990972ebefe548f952cbf630708298f52c1856fa5a142a341f2548bd9173f03f0106d6b745c7e39cc5baa9f084d5fea5e4c0bf42cf1d42df94e404f3558d7816
|
7
|
+
data.tar.gz: e52615e0d5b7ca9d554ffc540fdfec524d2aed74ac1c7fc3c727d3096504aa81ff43a85b9e2ce7b21da87edf21ca5f92197171ff1b1c62fa85c7551839e9df83
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,74 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
+
# 5.0.6
|
4
|
+
|
5
|
+
- Wait for an extra `config.read_timeout` in blocking commands rather than an arbitrary 100ms. See #1175.
|
6
|
+
- Treat ReadOnlyError as ConnectionError. See #1168.
|
7
|
+
|
8
|
+
# 5.0.5
|
9
|
+
|
10
|
+
- Fix automatic disconnection when the process was forked. See #1157.
|
11
|
+
|
12
|
+
# 5.0.4
|
13
|
+
|
14
|
+
- Cast `ttl` argument to integer in `expire`, `setex` and a few others.
|
15
|
+
|
16
|
+
# 5.0.3
|
17
|
+
|
18
|
+
- Add `OutOfMemoryError` as a subclass of `CommandError`
|
19
|
+
|
20
|
+
# 5.0.2
|
21
|
+
|
22
|
+
- Fix `Redis#close` to properly reset the fork protection check.
|
23
|
+
|
24
|
+
# 5.0.1
|
25
|
+
|
26
|
+
- Added a fake `Redis::Connections.drivers` method to be compatible with older sidekiq versions.
|
27
|
+
|
28
|
+
# 5.0.0
|
29
|
+
|
30
|
+
- Eagerly and strictly cast Integer and Float parameters.
|
31
|
+
- Allow to call `subscribe`, `unsubscribe`, `psubscribe` and `punsubscribe` from a subscribed client. See #1131.
|
32
|
+
- Use `MD5` for hashing server nodes in `Redis::Distributed`. This should improve keys distribution among servers. See #1089.
|
33
|
+
- Changed `sadd` and `srem` to now always return an Integer.
|
34
|
+
- Added `sadd?` and `srem?` which always return a Boolean.
|
35
|
+
- Added support for `IDLE` paramter in `xpending`.
|
36
|
+
- Cluster support has been moved to a `redis-clustering` companion gem.
|
37
|
+
- `select` no longer record the current database. If the client has to reconnect after `select` was used, it will reconnect to the original database.
|
38
|
+
- Better support Float timeout in blocking commands. See #977.
|
39
|
+
- `Redis.new` will now raise an error if provided unknown options.
|
40
|
+
- Removed positional timeout in blocking commands (`BLPOP`, etc). Timeout now must be passed as an option: `r.blpop("key", timeout: 2.5)`
|
41
|
+
- Removed `logger` option.
|
42
|
+
- Removed `reconnect_delay_max` and `reconnect_delay`, you can pass precise sleep durations to `reconnect_attempts` instead.
|
43
|
+
- Require Ruby 2.5+.
|
44
|
+
- Removed the deprecated `queue` and `commit` methods. Use `pipelined` instead.
|
45
|
+
- Removed the deprecated `Redis::Future#==`.
|
46
|
+
- Removed the deprecated `pipelined` and `multi` signature. Commands now MUST be called on the block argument, not the original redis instance.
|
47
|
+
- Removed `Redis.current`. You shouldn't assume there is a single global Redis connection, use a connection pool instead,
|
48
|
+
and libaries using Redis should accept a Redis instance (or connection pool) as a config. E.g. `MyLibrary.redis = Redis.new(...)`.
|
49
|
+
- Removed the `synchrony` driver.
|
50
|
+
- Removed `Redis.exists_returns_integer`, it's now always enabled.
|
51
|
+
|
52
|
+
# 4.8.0
|
53
|
+
|
54
|
+
* Introduce `sadd?` and `srem?` as boolean returning versions of `sadd` and `srem`.
|
55
|
+
* Deprecate `sadd` and `srem` returning a boolean when called with a single argument.
|
56
|
+
To enable the redis 5.0 behavior you can set `Redis.sadd_returns_boolean = false`.
|
57
|
+
* Deprecate passing `timeout` as a positional argument in blocking commands (`brpop`, `blop`, etc).
|
58
|
+
|
59
|
+
# 4.7.1
|
60
|
+
|
61
|
+
* Gracefully handle OpenSSL 3.0 EOF Errors (`OpenSSL::SSL::SSLError: SSL_read: unexpected eof while reading`). See #1106
|
62
|
+
This happens frequently on heroku-22.
|
63
|
+
|
64
|
+
# 4.7.0
|
65
|
+
|
66
|
+
* Support single endpoint architecture with SSL/TLS in cluster mode. See #1086.
|
67
|
+
* `zrem` and `zadd` act as noop when provided an empty list of keys. See #1097.
|
68
|
+
* Support IPv6 URLs.
|
69
|
+
* Add `Redis#with` for better compatibility with `connection_pool` usage.
|
70
|
+
* Fix the block form of `multi` called inside `pipelined`. Previously the `MUTLI/EXEC` wouldn't be sent. See #1073.
|
71
|
+
|
3
72
|
# 4.6.0
|
4
73
|
|
5
74
|
* Deprecate `Redis.current`.
|
@@ -37,7 +106,7 @@
|
|
37
106
|
* `Redis#synchronize` is now private like it should always have been.
|
38
107
|
|
39
108
|
* Add `Redis.silence_deprecations=` to turn off deprecation warnings.
|
40
|
-
If you don't wish to see warnings yet, you can set `Redis.silence_deprecations =
|
109
|
+
If you don't wish to see warnings yet, you can set `Redis.silence_deprecations = true`.
|
41
110
|
It is however heavily recommended to fix them instead when possible.
|
42
111
|
* Add `Redis.raise_deprecations=` to turn deprecation warnings into errors.
|
43
112
|
This makes it easier to identitify the source of deprecated APIs usage.
|
data/README.md
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# redis-rb [![Build Status][gh-actions-image]][gh-actions-link] [![Inline docs][inchpages-image]][inchpages-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
|
|
@@ -38,10 +37,6 @@ redis = Redis.new(url: "redis://:p4ssw0rd@10.0.1.1:6380/15")
|
|
38
37
|
The client expects passwords with special chracters 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,6 +71,26 @@ 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
|
+
## Connection Pooling and Thread safety
|
75
|
+
|
76
|
+
The client does not provide connection pooling. Each `Redis` instance
|
77
|
+
has one and only one connection to the server, and use of this connection
|
78
|
+
is protected by a mutex.
|
79
|
+
|
80
|
+
As such it is heavilly recommended to use the [`connection_pool` gem](https://github.com/mperham/connection_pool), e.g.:
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
module MyApp
|
84
|
+
def self.redis
|
85
|
+
@redis ||= ConnectionPool::Wrapper.new do
|
86
|
+
Redis.new(url: ENV["REDIS_URL"])
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
MyApp.redis.incr("some-counter")
|
92
|
+
```
|
93
|
+
|
79
94
|
## Sentinel support
|
80
95
|
|
81
96
|
The client is able to perform automatic failover by using [Redis
|
@@ -111,64 +126,12 @@ If you want to [authenticate](https://redis.io/topics/sentinel#configuring-senti
|
|
111
126
|
SENTINELS = [{ host: '127.0.0.1', port: 26380, password: 'mysecret' },
|
112
127
|
{ host: '127.0.0.1', port: 26381, password: 'mysecret' }]
|
113
128
|
|
114
|
-
redis = Redis.new(
|
129
|
+
redis = Redis.new(name: 'mymaster', sentinels: SENTINELS, role: :master)
|
115
130
|
```
|
116
131
|
|
117
132
|
## Cluster support
|
118
133
|
|
119
|
-
|
120
|
-
|
121
|
-
```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.
|
137
|
-
|
138
|
-
```ruby
|
139
|
-
Redis.new(cluster: nodes, replica: true)
|
140
|
-
```
|
141
|
-
|
142
|
-
The calling code is responsible for [avoiding cross slot commands](https://redis.io/topics/cluster-spec#keys-distribution-model).
|
143
|
-
|
144
|
-
```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
|
-
```
|
153
|
-
|
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
|
-
## Storing objects
|
159
|
-
|
160
|
-
Redis "string" types can be used to store serialized Ruby objects, for
|
161
|
-
example with JSON:
|
162
|
-
|
163
|
-
```ruby
|
164
|
-
require "json"
|
165
|
-
|
166
|
-
redis.set "foo", [1, 2, 3].to_json
|
167
|
-
# => OK
|
168
|
-
|
169
|
-
JSON.parse(redis.get("foo"))
|
170
|
-
# => [1, 2, 3]
|
171
|
-
```
|
134
|
+
[Clustering](https://redis.io/topics/cluster-spec). is supported via the [`redis-clustering` gem](cluster/).
|
172
135
|
|
173
136
|
## Pipelining
|
174
137
|
|
@@ -191,6 +154,17 @@ end
|
|
191
154
|
# => ["OK", 1]
|
192
155
|
```
|
193
156
|
|
157
|
+
Commands must be called on the yielded objects. If you call methods
|
158
|
+
on the original client objects from inside a pipeline, they will be sent immediately:
|
159
|
+
|
160
|
+
```ruby
|
161
|
+
redis.pipelined do |pipeline|
|
162
|
+
pipeline.set "foo", "bar"
|
163
|
+
redis.incr "baz" # => 1
|
164
|
+
end
|
165
|
+
# => ["OK"]
|
166
|
+
```
|
167
|
+
|
194
168
|
### Executing commands atomically
|
195
169
|
|
196
170
|
You can use `MULTI/EXEC` to run a number of commands in an atomic
|
@@ -210,21 +184,22 @@ end
|
|
210
184
|
### Futures
|
211
185
|
|
212
186
|
Replies to commands in a pipeline can be accessed via the *futures* they
|
213
|
-
emit
|
187
|
+
emit. All calls on the pipeline object return a
|
214
188
|
`Future` object, which responds to the `#value` method. When the
|
215
189
|
pipeline has successfully executed, all futures are assigned their
|
216
190
|
respective replies and can be used.
|
217
191
|
|
218
192
|
```ruby
|
193
|
+
set = incr = nil
|
219
194
|
redis.pipelined do |pipeline|
|
220
|
-
|
221
|
-
|
195
|
+
set = pipeline.set "foo", "bar"
|
196
|
+
incr = pipeline.incr "baz"
|
222
197
|
end
|
223
198
|
|
224
|
-
|
199
|
+
set.value
|
225
200
|
# => "OK"
|
226
201
|
|
227
|
-
|
202
|
+
incr.value
|
228
203
|
# => 1
|
229
204
|
```
|
230
205
|
|
@@ -236,7 +211,7 @@ it can't connect to the server a `Redis::CannotConnectError` error will be raise
|
|
236
211
|
```ruby
|
237
212
|
begin
|
238
213
|
redis.ping
|
239
|
-
rescue
|
214
|
+
rescue Redis::BaseError => e
|
240
215
|
e.inspect
|
241
216
|
# => #<Redis::CannotConnectError: Timed out connecting to Redis on 10.0.1.1:6380>
|
242
217
|
|
@@ -283,55 +258,37 @@ If no message is received after 5 seconds, the client will unsubscribe.
|
|
283
258
|
|
284
259
|
## Reconnections
|
285
260
|
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
`reconnect_delay` and `reconnect_delay_max`.
|
261
|
+
**By default**, this gem will only **retry a connection once** and then fail, but
|
262
|
+
the client allows you to configure how many `reconnect_attempts` it should
|
263
|
+
complete before declaring a connection as failed.
|
290
264
|
|
291
265
|
```ruby
|
292
|
-
Redis.new(
|
293
|
-
|
294
|
-
:reconnect_delay => 1.5,
|
295
|
-
:reconnect_delay_max => 10.0,
|
296
|
-
)
|
266
|
+
Redis.new(reconnect_attempts: 0)
|
267
|
+
Redis.new(reconnect_attempts: 3)
|
297
268
|
```
|
298
269
|
|
299
|
-
|
300
|
-
|
301
|
-
between each attempt but it never waits longer than `reconnect_delay_max`.
|
302
|
-
|
303
|
-
This is the retry algorithm:
|
270
|
+
If you wish to wait between reconnection attempts, you can instead pass a list
|
271
|
+
of durations:
|
304
272
|
|
305
273
|
```ruby
|
306
|
-
|
274
|
+
Redis.new(reconnect_attempts: [
|
275
|
+
0, # retry immediately
|
276
|
+
0.25, # retry a second time after 250ms
|
277
|
+
1, # retry a third and final time after another 1s
|
278
|
+
])
|
307
279
|
```
|
308
280
|
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
#|Attempt wait time|Total wait time
|
313
|
-
:-:|:-:|:-:
|
314
|
-
1|1.5s|1.5s
|
315
|
-
2|3.0s|4.5s
|
316
|
-
3|6.0s|10.5s
|
317
|
-
4|10.0s|20.5s
|
318
|
-
5|10.0s|30.5s
|
319
|
-
6|10.0s|40.5s
|
320
|
-
7|10.0s|50.5s
|
321
|
-
8|10.0s|60.5s
|
322
|
-
9|10.0s|70.5s
|
323
|
-
10|10.0s|80.5s
|
324
|
-
|
325
|
-
So if the reconnection attempt #10 succeeds 70 seconds have elapsed trying
|
326
|
-
to reconnect, this is likely fine in long-running background processes, but if
|
327
|
-
you use Redis to drive your website you might want to have a lower
|
328
|
-
`reconnect_delay_max` or have less `reconnect_attempts`.
|
281
|
+
If you wish to disable reconnection only for some commands, you can use
|
282
|
+
`disable_reconnection`:
|
329
283
|
|
330
|
-
|
284
|
+
```ruby
|
285
|
+
redis.get("some-key") # this may be retried
|
286
|
+
redis.disable_reconnection do
|
287
|
+
redis.incr("some-counter") # this won't be retried.
|
288
|
+
end
|
289
|
+
```
|
331
290
|
|
332
|
-
|
333
|
-
when talking to Redis via a server-side proxy such as [stunnel], [hitch],
|
334
|
-
or [ghostunnel].
|
291
|
+
## SSL/TLS Support
|
335
292
|
|
336
293
|
To enable SSL support, pass the `:ssl => true` option when configuring the
|
337
294
|
Redis client, or pass in `:url => "rediss://..."` (like HTTPS for Redis).
|
@@ -366,13 +323,7 @@ redis = Redis.new(
|
|
366
323
|
)
|
367
324
|
```
|
368
325
|
|
369
|
-
[
|
370
|
-
[hitch]: https://hitch-tls.org/
|
371
|
-
[ghostunnel]: https://github.com/square/ghostunnel
|
372
|
-
[OpenSSL::SSL::SSLContext documentation]: http://ruby-doc.org/stdlib-2.3.0/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html
|
373
|
-
|
374
|
-
*NOTE:* SSL is only supported by the default "Ruby" driver
|
375
|
-
|
326
|
+
[OpenSSL::SSL::SSLContext documentation]: http://ruby-doc.org/stdlib-2.5.0/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html
|
376
327
|
|
377
328
|
## Expert-Mode Options
|
378
329
|
|
@@ -386,17 +337,9 @@ redis = Redis.new(
|
|
386
337
|
Improper use of `inherit_socket` will result in corrupted and/or incorrect
|
387
338
|
responses.
|
388
339
|
|
389
|
-
##
|
340
|
+
## hiredis binding
|
390
341
|
|
391
342
|
By default, redis-rb uses Ruby's socket library to talk with Redis.
|
392
|
-
To use an alternative connection driver it should be specified as option
|
393
|
-
when instantiating the client object. These instructions are only valid
|
394
|
-
for **redis-rb 3.0**. For instructions on how to use alternate drivers from
|
395
|
-
**redis-rb 2.2**, please refer to an [older README][readme-2.2.2].
|
396
|
-
|
397
|
-
[readme-2.2.2]: https://github.com/redis/redis-rb/blob/v2.2.2/README.md
|
398
|
-
|
399
|
-
### hiredis
|
400
343
|
|
401
344
|
The hiredis driver uses the connection facility of hiredis-rb. In turn,
|
402
345
|
hiredis-rb is a binding to the official hiredis client library. It
|
@@ -406,41 +349,27 @@ extension, JRuby is not supported (by default).
|
|
406
349
|
It is best to use hiredis when you have large replies (for example:
|
407
350
|
`LRANGE`, `SMEMBERS`, `ZRANGE`, etc.) and/or use big pipelines.
|
408
351
|
|
409
|
-
In your Gemfile, include hiredis
|
352
|
+
In your Gemfile, include `hiredis-client`:
|
410
353
|
|
411
354
|
```ruby
|
412
|
-
gem "redis"
|
413
|
-
gem "hiredis"
|
355
|
+
gem "redis"
|
356
|
+
gem "hiredis-client"
|
414
357
|
```
|
415
358
|
|
416
|
-
|
359
|
+
If your application doesn't call `Bundler.require`, you may have
|
360
|
+
to require it explictly:
|
417
361
|
|
418
362
|
```ruby
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
### synchrony
|
423
|
-
|
424
|
-
The synchrony driver adds support for [em-synchrony][em-synchrony].
|
425
|
-
This makes redis-rb work with EventMachine's asynchronous I/O, while not
|
426
|
-
changing the exposed API. The hiredis gem needs to be available as
|
427
|
-
well, because the synchrony driver uses hiredis for parsing the Redis
|
428
|
-
protocol.
|
429
|
-
|
430
|
-
[em-synchrony]: https://github.com/igrigorik/em-synchrony
|
363
|
+
require "hiredis-client"
|
364
|
+
````
|
431
365
|
|
432
|
-
|
433
|
-
|
434
|
-
```ruby
|
435
|
-
gem "redis", "~> 3.0.1"
|
436
|
-
gem "hiredis", "~> 0.4.5"
|
437
|
-
gem "em-synchrony"
|
438
|
-
```
|
366
|
+
This makes the hiredis driver the default.
|
439
367
|
|
440
|
-
|
368
|
+
If you want to be certain hiredis is being used, when instantiating
|
369
|
+
the client object, specify hiredis:
|
441
370
|
|
442
371
|
```ruby
|
443
|
-
redis = Redis.new(:
|
372
|
+
redis = Redis.new(driver: :hiredis)
|
444
373
|
```
|
445
374
|
|
446
375
|
## Testing
|