redis 3.3.5 → 4.1.4
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 +5 -5
- data/CHANGELOG.md +84 -2
- data/README.md +131 -76
- data/lib/redis.rb +912 -200
- data/lib/redis/client.rb +71 -29
- data/lib/redis/cluster.rb +291 -0
- data/lib/redis/cluster/command.rb +81 -0
- data/lib/redis/cluster/command_loader.rb +34 -0
- data/lib/redis/cluster/key_slot_converter.rb +72 -0
- data/lib/redis/cluster/node.rb +104 -0
- data/lib/redis/cluster/node_key.rb +31 -0
- data/lib/redis/cluster/node_loader.rb +37 -0
- data/lib/redis/cluster/option.rb +87 -0
- data/lib/redis/cluster/slot.rb +72 -0
- data/lib/redis/cluster/slot_loader.rb +50 -0
- data/lib/redis/connection.rb +3 -2
- data/lib/redis/connection/command_helper.rb +3 -8
- data/lib/redis/connection/hiredis.rb +3 -2
- data/lib/redis/connection/registry.rb +1 -0
- data/lib/redis/connection/ruby.rb +48 -32
- data/lib/redis/connection/synchrony.rb +13 -4
- data/lib/redis/distributed.rb +39 -15
- data/lib/redis/errors.rb +47 -0
- data/lib/redis/hash_ring.rb +21 -64
- data/lib/redis/pipeline.rb +54 -12
- data/lib/redis/subscribe.rb +1 -0
- data/lib/redis/version.rb +2 -1
- metadata +40 -198
- data/.gitignore +0 -16
- data/.travis.yml +0 -89
- data/.travis/Gemfile +0 -11
- data/.yardopts +0 -3
- data/Gemfile +0 -4
- data/Rakefile +0 -87
- data/benchmarking/logging.rb +0 -71
- data/benchmarking/pipeline.rb +0 -51
- data/benchmarking/speed.rb +0 -21
- data/benchmarking/suite.rb +0 -24
- data/benchmarking/worker.rb +0 -71
- data/examples/basic.rb +0 -15
- data/examples/consistency.rb +0 -114
- data/examples/dist_redis.rb +0 -43
- data/examples/incr-decr.rb +0 -17
- data/examples/list.rb +0 -26
- data/examples/pubsub.rb +0 -37
- data/examples/sentinel.rb +0 -41
- data/examples/sentinel/sentinel.conf +0 -9
- data/examples/sentinel/start +0 -49
- data/examples/sets.rb +0 -36
- data/examples/unicorn/config.ru +0 -3
- data/examples/unicorn/unicorn.rb +0 -20
- data/redis.gemspec +0 -44
- data/test/bitpos_test.rb +0 -69
- data/test/blocking_commands_test.rb +0 -42
- data/test/client_test.rb +0 -59
- data/test/command_map_test.rb +0 -30
- data/test/commands_on_hashes_test.rb +0 -21
- data/test/commands_on_hyper_log_log_test.rb +0 -21
- data/test/commands_on_lists_test.rb +0 -20
- data/test/commands_on_sets_test.rb +0 -77
- data/test/commands_on_sorted_sets_test.rb +0 -137
- data/test/commands_on_strings_test.rb +0 -101
- data/test/commands_on_value_types_test.rb +0 -133
- data/test/connection_handling_test.rb +0 -277
- data/test/connection_test.rb +0 -57
- data/test/db/.gitkeep +0 -0
- data/test/distributed_blocking_commands_test.rb +0 -46
- data/test/distributed_commands_on_hashes_test.rb +0 -10
- data/test/distributed_commands_on_hyper_log_log_test.rb +0 -33
- data/test/distributed_commands_on_lists_test.rb +0 -22
- data/test/distributed_commands_on_sets_test.rb +0 -83
- data/test/distributed_commands_on_sorted_sets_test.rb +0 -18
- data/test/distributed_commands_on_strings_test.rb +0 -59
- data/test/distributed_commands_on_value_types_test.rb +0 -95
- data/test/distributed_commands_requiring_clustering_test.rb +0 -164
- data/test/distributed_connection_handling_test.rb +0 -23
- data/test/distributed_internals_test.rb +0 -79
- data/test/distributed_key_tags_test.rb +0 -52
- data/test/distributed_persistence_control_commands_test.rb +0 -26
- data/test/distributed_publish_subscribe_test.rb +0 -92
- data/test/distributed_remote_server_control_commands_test.rb +0 -66
- data/test/distributed_scripting_test.rb +0 -102
- data/test/distributed_sorting_test.rb +0 -20
- data/test/distributed_test.rb +0 -58
- data/test/distributed_transactions_test.rb +0 -32
- data/test/encoding_test.rb +0 -18
- data/test/error_replies_test.rb +0 -59
- data/test/fork_safety_test.rb +0 -65
- data/test/helper.rb +0 -232
- data/test/helper_test.rb +0 -24
- data/test/internals_test.rb +0 -417
- data/test/lint/blocking_commands.rb +0 -150
- data/test/lint/hashes.rb +0 -162
- data/test/lint/hyper_log_log.rb +0 -60
- data/test/lint/lists.rb +0 -143
- data/test/lint/sets.rb +0 -140
- data/test/lint/sorted_sets.rb +0 -316
- data/test/lint/strings.rb +0 -260
- data/test/lint/value_types.rb +0 -122
- data/test/persistence_control_commands_test.rb +0 -26
- data/test/pipelining_commands_test.rb +0 -242
- data/test/publish_subscribe_test.rb +0 -282
- data/test/remote_server_control_commands_test.rb +0 -118
- data/test/scanning_test.rb +0 -413
- data/test/scripting_test.rb +0 -78
- data/test/sentinel_command_test.rb +0 -80
- data/test/sentinel_test.rb +0 -255
- data/test/sorting_test.rb +0 -59
- data/test/ssl_test.rb +0 -73
- data/test/support/connection/hiredis.rb +0 -1
- data/test/support/connection/ruby.rb +0 -1
- data/test/support/connection/synchrony.rb +0 -17
- data/test/support/redis_mock.rb +0 -130
- data/test/support/ssl/gen_certs.sh +0 -31
- data/test/support/ssl/trusted-ca.crt +0 -25
- data/test/support/ssl/trusted-ca.key +0 -27
- data/test/support/ssl/trusted-cert.crt +0 -81
- data/test/support/ssl/trusted-cert.key +0 -28
- data/test/support/ssl/untrusted-ca.crt +0 -26
- data/test/support/ssl/untrusted-ca.key +0 -27
- data/test/support/ssl/untrusted-cert.crt +0 -82
- data/test/support/ssl/untrusted-cert.key +0 -28
- data/test/support/wire/synchrony.rb +0 -24
- data/test/support/wire/thread.rb +0 -5
- data/test/synchrony_driver.rb +0 -88
- data/test/test.conf.erb +0 -9
- data/test/thread_safety_test.rb +0 -62
- data/test/transactions_test.rb +0 -264
- data/test/unknown_commands_test.rb +0 -14
- data/test/url_param_test.rb +0 -138
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 83f1f7270db68603d63e86ec43e68348cb5ccb2b4e6759642d89898566bdbaf6
|
4
|
+
data.tar.gz: 45c5bcc92629ec7d85cdc2b913e7922cd5425f2e6691891efc379aeec73026b3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 692dfc5c73c6410492589f38f279976a023f6a2ff13f7b1476806011eb387f41bed784bdeac746de5f4b990b6d22bf297b36dddc7b8e448a842241a389f50796
|
7
|
+
data.tar.gz: 55a9e305c7563f5dd7d38f50dc7b919967dbb0f6a131ebc5e1569f49f196ab458203b6594394fa9a33ea9e337b741113e781378113783683dd36b87196607b8f
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,90 @@
|
|
1
|
+
# Unreleased
|
2
|
+
|
3
|
+
# 4.1.4
|
4
|
+
|
5
|
+
* Alias `Redis#disconnect` as `#close`. See #901.
|
6
|
+
* Handle clusters with multiple slot ranges. See #894.
|
7
|
+
* Fix password authentication to a redis cluster. See #889.
|
8
|
+
* Handle recursive MOVED responses. See #882.
|
9
|
+
* Increase buffer size in the ruby connector. See #880.
|
10
|
+
* Fix thread safety of `Redis.queue`. See #878.
|
11
|
+
* Deprecate `Redis::Future#==` as it's likely to be a mistake. See #876.
|
12
|
+
|
13
|
+
# 4.1.3
|
14
|
+
|
15
|
+
* Fix the client hanging forever when connecting with SSL to a non-SSL server. See #835.
|
16
|
+
|
17
|
+
# 4.1.2
|
18
|
+
|
19
|
+
* Fix several authentication problems with sentinel. See #850 and #856.
|
20
|
+
* Explicitly drop Ruby 2.2 support.
|
21
|
+
|
22
|
+
|
23
|
+
# 4.1.1
|
24
|
+
|
25
|
+
* Fix error handling in multi blocks. See #754.
|
26
|
+
* Fix geoadd to accept arrays like georadius and georadiusbymember. See #841.
|
27
|
+
* Fix georadius command failing when long == lat. See #841.
|
28
|
+
* Fix timeout error in xread block: 0. See #837.
|
29
|
+
* Fix incompatibility issue with redis-objects. See #834.
|
30
|
+
* Properly handle Errno::EADDRNOTAVAIL on connect.
|
31
|
+
* Fix password authentication to sentinel instances. See #813.
|
32
|
+
|
33
|
+
# 4.1.0
|
34
|
+
|
35
|
+
* Add Redis Cluster support. See #716.
|
36
|
+
* Add streams support. See #799 and #811.
|
37
|
+
* Add ZPOP* support. See #812.
|
38
|
+
* Fix issues with integer-like objects as BPOP timeout
|
39
|
+
|
40
|
+
# 4.0.3
|
41
|
+
|
42
|
+
* Fix raising command error for first command in pipeline. See #788.
|
43
|
+
* Fix the gemspec to stop exposing a `build` executable. See #785.
|
44
|
+
* Add `:reconnect_delay` and `:reconnect_delay_max` options. See #778.
|
45
|
+
|
46
|
+
# 4.0.2
|
47
|
+
|
48
|
+
* Added `Redis#unlink`. See #766.
|
49
|
+
|
50
|
+
* `Redis.new` now accept a custom connector via `:connector`. See #591.
|
51
|
+
|
52
|
+
* `Redis#multi` no longer perform empty transactions. See #747.
|
53
|
+
|
54
|
+
* `Redis#hdel` now accepts hash keys as multiple arguments like `#del`. See #755.
|
55
|
+
|
56
|
+
* Allow to skip SSL verification. See #745.
|
57
|
+
|
58
|
+
* Add Geo commands: `geoadd`, `geohash`, `georadius`, `georadiusbymember`, `geopos`, `geodist`. See #730.
|
59
|
+
|
60
|
+
# 4.0.1
|
61
|
+
|
62
|
+
* `Redis::Distributed` now supports `mget` and `mapped_mget`. See #687.
|
63
|
+
|
64
|
+
* `Redis::Distributed` now supports `sscan` and `sscan_each`. See #572.
|
65
|
+
|
66
|
+
* `Redis#connection` returns a hash with connection information.
|
67
|
+
You shouldn't need to call `Redis#_client`, ever.
|
68
|
+
|
69
|
+
* `Redis#flushdb` and `Redis#flushall` now support the `:async` option. See #706.
|
70
|
+
|
71
|
+
|
72
|
+
# 4.0
|
73
|
+
|
74
|
+
* Removed `Redis.connect`. Use `Redis.new`.
|
75
|
+
|
76
|
+
* Removed `Redis#[]` and `Redis#[]=` aliases.
|
77
|
+
|
78
|
+
* Added support for `CLIENT` commands. The lower-level client can be
|
79
|
+
accessed via `Redis#_client`.
|
80
|
+
|
81
|
+
* Dropped official support for Ruby < 2.2.2.
|
82
|
+
|
1
83
|
# 3.3.5
|
2
84
|
|
3
85
|
* Fixed Ruby 1.8 compatibility after backporting `Redis#connection`. See #719.
|
4
86
|
|
5
|
-
# 3.3.4
|
87
|
+
# 3.3.4 (yanked)
|
6
88
|
|
7
89
|
* `Redis#connection` returns a hash with connection information.
|
8
90
|
You shouldn't need to call `Redis#_client`, ever.
|
@@ -13,7 +95,7 @@
|
|
13
95
|
|
14
96
|
# 3.3.2
|
15
97
|
|
16
|
-
* Added support for SPOP with COUNT. See #628.
|
98
|
+
* Added support for `SPOP` with COUNT. See #628.
|
17
99
|
|
18
100
|
* Fixed connection glitches when using SSL. See #644.
|
19
101
|
|
data/README.md
CHANGED
@@ -1,46 +1,17 @@
|
|
1
|
-
# redis-rb [![Build Status][travis-image]][travis-link] [![Inline docs][inchpages-image]][inchpages-link]
|
1
|
+
# redis-rb [![Build Status][travis-image]][travis-link] [![Inline docs][inchpages-image]][inchpages-link] 
|
2
2
|
|
3
|
-
[
|
4
|
-
|
5
|
-
[travis-home]: http://travis-ci.org/
|
6
|
-
[inchpages-image]: http://inch-ci.org/github/redis/redis-rb.png
|
7
|
-
[inchpages-link]: http://inch-ci.org/github/redis/redis-rb
|
3
|
+
A Ruby client that tries to match [Redis][redis-home]' API one-to-one, while still
|
4
|
+
providing an idiomatic interface.
|
8
5
|
|
9
|
-
A Ruby client library for [Redis][redis-home].
|
10
|
-
|
11
|
-
[redis-home]: http://redis.io
|
12
|
-
|
13
|
-
A Ruby client that tries to match Redis' API one-to-one, while still
|
14
|
-
providing an idiomatic interface. It features thread-safety, client-side
|
15
|
-
sharding, pipelining, and an obsession for performance.
|
16
|
-
|
17
|
-
## Upgrading from 2.x to 3.0
|
18
|
-
|
19
|
-
Please refer to the [CHANGELOG][changelog-3.0.0] for a summary of the
|
20
|
-
most important changes, as well as a full list of changes.
|
21
|
-
|
22
|
-
[changelog-3.0.0]: https://github.com/redis/redis-rb/blob/master/CHANGELOG.md#300
|
23
6
|
|
24
7
|
## Getting started
|
25
8
|
|
26
|
-
|
27
|
-
|
28
|
-
```
|
29
|
-
gem install redis
|
30
|
-
```
|
31
|
-
|
32
|
-
Or if you are using **bundler**, add
|
9
|
+
Install with:
|
33
10
|
|
34
11
|
```
|
35
|
-
|
12
|
+
$ gem install redis
|
36
13
|
```
|
37
14
|
|
38
|
-
to your `Gemfile`, and run `bundle install`
|
39
|
-
|
40
|
-
As of version 2.0 this client only targets Redis version 2.0 and higher.
|
41
|
-
You can use an older version of this client if you need to interface
|
42
|
-
with a Redis instance older than 2.0, but this is no longer supported.
|
43
|
-
|
44
15
|
You can connect to Redis by instantiating the `Redis` class:
|
45
16
|
|
46
17
|
```ruby
|
@@ -54,17 +25,15 @@ listening on `localhost`, port 6379. If you need to connect to a remote
|
|
54
25
|
server or a different port, try:
|
55
26
|
|
56
27
|
```ruby
|
57
|
-
redis = Redis.new(:
|
28
|
+
redis = Redis.new(host: "10.0.1.1", port: 6380, db: 15)
|
58
29
|
```
|
59
30
|
|
60
31
|
You can also specify connection options as a [`redis://` URL][redis-url]:
|
61
32
|
|
62
33
|
```ruby
|
63
|
-
redis = Redis.new(:
|
34
|
+
redis = Redis.new(url: "redis://:p4ssw0rd@10.0.1.1:6380/15")
|
64
35
|
```
|
65
36
|
|
66
|
-
[redis-url]: http://www.iana.org/assignments/uri-schemes/prov/redis
|
67
|
-
|
68
37
|
By default, the client will try to read the `REDIS_URL` environment variable
|
69
38
|
and use that as URL to connect to. The above statement is therefore equivalent
|
70
39
|
to setting this environment variable and calling `Redis.new` without arguments.
|
@@ -72,13 +41,13 @@ to setting this environment variable and calling `Redis.new` without arguments.
|
|
72
41
|
To connect to Redis listening on a Unix socket, try:
|
73
42
|
|
74
43
|
```ruby
|
75
|
-
redis = Redis.new(:
|
44
|
+
redis = Redis.new(path: "/tmp/redis.sock")
|
76
45
|
```
|
77
46
|
|
78
47
|
To connect to a password protected Redis instance, use:
|
79
48
|
|
80
49
|
```ruby
|
81
|
-
redis = Redis.new(:
|
50
|
+
redis = Redis.new(password: "mysecret")
|
82
51
|
```
|
83
52
|
|
84
53
|
The Redis class exports methods that are named identical to the commands
|
@@ -86,8 +55,6 @@ they execute. The arguments these methods accept are often identical to
|
|
86
55
|
the arguments specified on the [Redis website][redis-commands]. For
|
87
56
|
instance, the `SET` and `GET` commands can be called like this:
|
88
57
|
|
89
|
-
[redis-commands]: http://redis.io/commands
|
90
|
-
|
91
58
|
```ruby
|
92
59
|
redis.set("mykey", "hello world")
|
93
60
|
# => "OK"
|
@@ -96,24 +63,22 @@ redis.get("mykey")
|
|
96
63
|
# => "hello world"
|
97
64
|
```
|
98
65
|
|
99
|
-
All commands, their arguments and return values are documented
|
100
|
-
available on [
|
101
|
-
|
102
|
-
[rdoc]: http://rdoc.info/github/redis/redis-rb/
|
66
|
+
All commands, their arguments, and return values are documented and
|
67
|
+
available on [RubyDoc.info][rubydoc].
|
103
68
|
|
104
69
|
## Sentinel support
|
105
70
|
|
106
|
-
The client is able to perform automatic
|
71
|
+
The client is able to perform automatic failover by using [Redis
|
107
72
|
Sentinel](http://redis.io/topics/sentinel). Make sure to run Redis 2.8+
|
108
73
|
if you want to use this feature.
|
109
74
|
|
110
75
|
To connect using Sentinel, use:
|
111
76
|
|
112
77
|
```ruby
|
113
|
-
SENTINELS = [{:
|
114
|
-
{:
|
78
|
+
SENTINELS = [{ host: "127.0.0.1", port: 26380 },
|
79
|
+
{ host: "127.0.0.1", port: 26381 }]
|
115
80
|
|
116
|
-
redis = Redis.new(:
|
81
|
+
redis = Redis.new(url: "redis://mymaster", sentinels: SENTINELS, role: :master)
|
117
82
|
```
|
118
83
|
|
119
84
|
* The master name identifies a group of Redis instances composed of a master
|
@@ -130,6 +95,56 @@ but a few so that if one is down the client will try the next one. The client
|
|
130
95
|
is able to remember the last Sentinel that was able to reply correctly and will
|
131
96
|
use it for the next requests.
|
132
97
|
|
98
|
+
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.
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
SENTINELS = [{ host: '127.0.0.1', port: 26380, password: 'mysecret' },
|
102
|
+
{ host: '127.0.0.1', port: 26381, password: 'mysecret' }]
|
103
|
+
|
104
|
+
redis = Redis.new(host: 'mymaster', sentinels: SENTINELS, role: :master)
|
105
|
+
```
|
106
|
+
|
107
|
+
## Cluster support
|
108
|
+
|
109
|
+
`redis-rb` supports [clustering](https://redis.io/topics/cluster-spec).
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
# Nodes can be passed to the client as an array of connection URLs.
|
113
|
+
nodes = (7000..7005).map { |port| "redis://127.0.0.1:#{port}" }
|
114
|
+
redis = Redis.new(cluster: nodes)
|
115
|
+
|
116
|
+
# You can also specify the options as a Hash. The options are the same as for a single server connection.
|
117
|
+
(7000..7005).map { |port| { host: '127.0.0.1', port: port } }
|
118
|
+
```
|
119
|
+
|
120
|
+
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.
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
Redis.new(cluster: %w[redis://127.0.0.1:7000])
|
124
|
+
```
|
125
|
+
|
126
|
+
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.
|
127
|
+
|
128
|
+
```ruby
|
129
|
+
Redis.new(cluster: nodes, replica: true)
|
130
|
+
```
|
131
|
+
|
132
|
+
The calling code is responsible for [avoiding cross slot commands](https://redis.io/topics/cluster-spec#keys-distribution-model).
|
133
|
+
|
134
|
+
```ruby
|
135
|
+
redis = Redis.new(cluster: %w[redis://127.0.0.1:7000])
|
136
|
+
|
137
|
+
redis.mget('key1', 'key2')
|
138
|
+
#=> Redis::CommandError (CROSSSLOT Keys in request don't hash to the same slot)
|
139
|
+
|
140
|
+
redis.mget('{key}1', '{key}2')
|
141
|
+
#=> [nil, nil]
|
142
|
+
```
|
143
|
+
|
144
|
+
* The client automatically reconnects after a failover occurred, but the caller is responsible for handling errors while it is happening.
|
145
|
+
* The client support permanent node failures, and will reroute requests to promoted slaves.
|
146
|
+
* The client supports `MOVED` and `ASK` redirections transparently.
|
147
|
+
|
133
148
|
## Storing objects
|
134
149
|
|
135
150
|
Redis only stores strings as values. If you want to store an object, you
|
@@ -211,7 +226,7 @@ it can't connect to the server a `Redis::CannotConnectError` error will be raise
|
|
211
226
|
```ruby
|
212
227
|
begin
|
213
228
|
redis.ping
|
214
|
-
rescue
|
229
|
+
rescue StandardError => e
|
215
230
|
e.inspect
|
216
231
|
# => #<Redis::CannotConnectError: Timed out connecting to Redis on 10.0.1.1:6380>
|
217
232
|
|
@@ -255,6 +270,51 @@ end
|
|
255
270
|
|
256
271
|
If no message is received after 5 seconds, the client will unsubscribe.
|
257
272
|
|
273
|
+
## Reconnections
|
274
|
+
|
275
|
+
The client allows you to configure how many `reconnect_attempts` it should
|
276
|
+
complete before declaring a connection as failed. Furthermore, you may want
|
277
|
+
to control the maximum duration between reconnection attempts with
|
278
|
+
`reconnect_delay` and `reconnect_delay_max`.
|
279
|
+
|
280
|
+
```ruby
|
281
|
+
Redis.new(
|
282
|
+
:reconnect_attempts => 10,
|
283
|
+
:reconnect_delay => 1.5,
|
284
|
+
:reconnect_delay_max => 10.0,
|
285
|
+
)
|
286
|
+
```
|
287
|
+
|
288
|
+
The delay values are specified in seconds. With the above configuration, the
|
289
|
+
client would attempt 10 reconnections, exponentially increasing the duration
|
290
|
+
between each attempt but it never waits longer than `reconnect_delay_max`.
|
291
|
+
|
292
|
+
This is the retry algorithm:
|
293
|
+
|
294
|
+
```ruby
|
295
|
+
attempt_wait_time = [(reconnect_delay * 2**(attempt-1)), reconnect_delay_max].min
|
296
|
+
```
|
297
|
+
|
298
|
+
**By default**, this gem will only **retry a connection once** and then fail, but with the
|
299
|
+
above configuration the reconnection attempt would look like this:
|
300
|
+
|
301
|
+
#|Attempt wait time|Total wait time
|
302
|
+
:-:|:-:|:-:
|
303
|
+
1|1.5s|1.5s
|
304
|
+
2|3.0s|4.5s
|
305
|
+
3|6.0s|10.5s
|
306
|
+
4|10.0s|20.5s
|
307
|
+
5|10.0s|30.5s
|
308
|
+
6|10.0s|40.5s
|
309
|
+
7|10.0s|50.5s
|
310
|
+
8|10.0s|60.5s
|
311
|
+
9|10.0s|70.5s
|
312
|
+
10|10.0s|80.5s
|
313
|
+
|
314
|
+
So if the reconnection attempt #10 succeeds 70 seconds have elapsed trying
|
315
|
+
to reconnect, this is likely fine in long-running background processes, but if
|
316
|
+
you use Redis to drive your website you might want to have a lower
|
317
|
+
`reconnect_delay_max` or have less `reconnect_attempts`.
|
258
318
|
|
259
319
|
## SSL/TLS Support
|
260
320
|
|
@@ -374,37 +434,32 @@ redis = Redis.new(:driver => :synchrony)
|
|
374
434
|
|
375
435
|
## Testing
|
376
436
|
|
377
|
-
This library is tested
|
378
|
-
|
437
|
+
This library is tested against recent Ruby and Redis versions.
|
438
|
+
Check [Travis][travis-link] for the exact versions supported.
|
379
439
|
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
* MRI 2.1 (drivers: ruby, hiredis, synchrony)
|
384
|
-
* MRI 2.2 (drivers: ruby, hiredis, synchrony)
|
385
|
-
* MRI 2.3 (drivers: ruby, hiredis, synchrony)
|
386
|
-
* JRuby 1.7 (1.8 mode) (drivers: ruby)
|
387
|
-
* JRuby 1.7 (1.9 mode) (drivers: ruby)
|
440
|
+
## See Also
|
441
|
+
|
442
|
+
- [async-redis](https://github.com/socketry/async-redis) — An [async](https://github.com/socketry/async) compatible Redis client.
|
388
443
|
|
389
444
|
## Contributors
|
390
445
|
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
* Taylor Weibley
|
396
|
-
* Matthew Clark
|
397
|
-
* Brian McKinney
|
398
|
-
* Luca Guidi
|
399
|
-
* Salvatore Sanfilippo
|
400
|
-
* Chris Wanstrath
|
401
|
-
* Damian Janowski
|
402
|
-
* Michel Martens
|
403
|
-
* Nick Quaranto
|
404
|
-
* Pieter Noordhuis
|
405
|
-
* Ilya Grigorik
|
446
|
+
Several people contributed to redis-rb, but we would like to especially
|
447
|
+
mention Ezra Zygmuntowicz. Ezra introduced the Ruby community to many
|
448
|
+
new cool technologies, like Redis. He wrote the first version of this
|
449
|
+
client and evangelized Redis in Rubyland. Thank you, Ezra.
|
406
450
|
|
407
451
|
## Contributing
|
408
452
|
|
409
453
|
[Fork the project](https://github.com/redis/redis-rb) and send pull
|
410
454
|
requests. You can also ask for help at `#redis-rb` on Freenode.
|
455
|
+
|
456
|
+
|
457
|
+
[inchpages-image]: https://inch-ci.org/github/redis/redis-rb.svg
|
458
|
+
[inchpages-link]: https://inch-ci.org/github/redis/redis-rb
|
459
|
+
[redis-commands]: https://redis.io/commands
|
460
|
+
[redis-home]: https://redis.io
|
461
|
+
[redis-url]: http://www.iana.org/assignments/uri-schemes/prov/redis
|
462
|
+
[travis-home]: https://travis-ci.org/
|
463
|
+
[travis-image]: https://secure.travis-ci.org/redis/redis-rb.svg?branch=master
|
464
|
+
[travis-link]: https://travis-ci.org/redis/redis-rb
|
465
|
+
[rubydoc]: http://www.rubydoc.info/gems/redis
|
data/lib/redis.rb
CHANGED
@@ -1,21 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "monitor"
|
2
|
-
|
4
|
+
require_relative "redis/errors"
|
3
5
|
|
4
6
|
class Redis
|
5
|
-
|
6
|
-
def self.deprecate(message, trace = caller[0])
|
7
|
-
$stderr.puts "\n#{message} (in #{trace})"
|
8
|
-
end
|
9
|
-
|
10
|
-
attr :client
|
11
|
-
|
12
|
-
# @deprecated The preferred way to create a new client object is using `#new`.
|
13
|
-
# This method does not actually establish a connection to Redis,
|
14
|
-
# in contrary to what you might expect.
|
15
|
-
def self.connect(options = {})
|
16
|
-
new(options)
|
17
|
-
end
|
18
|
-
|
19
7
|
def self.current
|
20
8
|
@current ||= Redis.new
|
21
9
|
end
|
@@ -31,24 +19,29 @@ class Redis
|
|
31
19
|
# @param [Hash] options
|
32
20
|
# @option options [String] :url (value of the environment variable REDIS_URL) a Redis URL, for a TCP connection: `redis://:[password]@[hostname]:[port]/[db]` (password, port and database are optional), for a unix socket connection: `unix://[path to Redis socket]`. This overrides all other options.
|
33
21
|
# @option options [String] :host ("127.0.0.1") server hostname
|
34
|
-
# @option options [
|
22
|
+
# @option options [Integer] :port (6379) server port
|
35
23
|
# @option options [String] :path path to server socket (overrides host and port)
|
36
24
|
# @option options [Float] :timeout (5.0) timeout in seconds
|
37
25
|
# @option options [Float] :connect_timeout (same as timeout) timeout for initial connect in seconds
|
38
26
|
# @option options [String] :password Password to authenticate against server
|
39
|
-
# @option options [
|
27
|
+
# @option options [Integer] :db (0) Database to select after initial connect
|
40
28
|
# @option options [Symbol] :driver Driver to use, currently supported: `:ruby`, `:hiredis`, `:synchrony`
|
41
29
|
# @option options [String] :id ID for the client connection, assigns name to current connection by sending `CLIENT SETNAME`
|
42
|
-
# @option options [Hash,
|
43
|
-
# @option options [
|
30
|
+
# @option options [Hash, Integer] :tcp_keepalive Keepalive values, if Integer `intvl` and `probe` are calculated based on the value, if Hash `time`, `intvl` and `probes` can be specified as a Integer
|
31
|
+
# @option options [Integer] :reconnect_attempts Number of attempts trying to connect
|
44
32
|
# @option options [Boolean] :inherit_socket (false) Whether to use socket in forked process or not
|
45
33
|
# @option options [Array] :sentinels List of sentinels to contact
|
46
34
|
# @option options [Symbol] :role (:master) Role to fetch via Sentinel, either `:master` or `:slave`
|
35
|
+
# @option options [Array<String, Hash{Symbol => String, Integer}>] :cluster List of cluster nodes to contact
|
36
|
+
# @option options [Boolean] :replica Whether to use readonly replica nodes in Redis Cluster or not
|
37
|
+
# @option options [Class] :connector Class of custom connector
|
47
38
|
#
|
48
39
|
# @return [Redis] a new client instance
|
49
40
|
def initialize(options = {})
|
50
41
|
@options = options.dup
|
51
|
-
@
|
42
|
+
@cluster_mode = options.key?(:cluster)
|
43
|
+
client = @cluster_mode ? Cluster : Client
|
44
|
+
@original_client = @client = client.new(options)
|
52
45
|
@queue = Hash.new { |h, k| h[k] = [] }
|
53
46
|
|
54
47
|
super() # Monitor#initialize
|
@@ -102,7 +95,9 @@ class Redis
|
|
102
95
|
# See http://redis.io/topics/pipelining for more details.
|
103
96
|
#
|
104
97
|
def queue(*command)
|
105
|
-
|
98
|
+
synchronize do
|
99
|
+
@queue[Thread.current.object_id] << command
|
100
|
+
end
|
106
101
|
end
|
107
102
|
|
108
103
|
# Sends all commands in the queue.
|
@@ -112,13 +107,22 @@ class Redis
|
|
112
107
|
def commit
|
113
108
|
synchronize do |client|
|
114
109
|
begin
|
115
|
-
|
110
|
+
pipeline = Pipeline.new(client)
|
111
|
+
@queue[Thread.current.object_id].each do |command|
|
112
|
+
pipeline.call(command)
|
113
|
+
end
|
114
|
+
|
115
|
+
client.call_pipelined(pipeline)
|
116
116
|
ensure
|
117
117
|
@queue.delete(Thread.current.object_id)
|
118
118
|
end
|
119
119
|
end
|
120
120
|
end
|
121
121
|
|
122
|
+
def _client
|
123
|
+
@client
|
124
|
+
end
|
125
|
+
|
122
126
|
# Authenticate to the server.
|
123
127
|
#
|
124
128
|
# @param [String] password must match the password specified in the
|
@@ -132,7 +136,7 @@ class Redis
|
|
132
136
|
|
133
137
|
# Change the selected database for the current connection.
|
134
138
|
#
|
135
|
-
# @param [
|
139
|
+
# @param [Integer] db zero-based index of the DB to use (0 to 15)
|
136
140
|
# @return [String] `OK`
|
137
141
|
def select(db)
|
138
142
|
synchronize do |client|
|
@@ -143,10 +147,11 @@ class Redis
|
|
143
147
|
|
144
148
|
# Ping the server.
|
145
149
|
#
|
150
|
+
# @param [optional, String] message
|
146
151
|
# @return [String] `PONG`
|
147
|
-
def ping
|
152
|
+
def ping(message = nil)
|
148
153
|
synchronize do |client|
|
149
|
-
client.call([:ping])
|
154
|
+
client.call([:ping, message].compact)
|
150
155
|
end
|
151
156
|
end
|
152
157
|
|
@@ -209,9 +214,28 @@ class Redis
|
|
209
214
|
end
|
210
215
|
end
|
211
216
|
|
217
|
+
# Manage client connections.
|
218
|
+
#
|
219
|
+
# @param [String, Symbol] subcommand e.g. `kill`, `list`, `getname`, `setname`
|
220
|
+
# @return [String, Hash] depends on subcommand
|
221
|
+
def client(subcommand = nil, *args)
|
222
|
+
synchronize do |client|
|
223
|
+
client.call([:client, subcommand] + args) do |reply|
|
224
|
+
if subcommand.to_s == "list"
|
225
|
+
reply.lines.map do |line|
|
226
|
+
entries = line.chomp.split(/[ =]/)
|
227
|
+
Hash[entries.each_slice(2).to_a]
|
228
|
+
end
|
229
|
+
else
|
230
|
+
reply
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
212
236
|
# Return the number of keys in the selected database.
|
213
237
|
#
|
214
|
-
# @return [
|
238
|
+
# @return [Integer]
|
215
239
|
def dbsize
|
216
240
|
synchronize do |client|
|
217
241
|
client.call([:dbsize])
|
@@ -226,19 +250,31 @@ class Redis
|
|
226
250
|
|
227
251
|
# Remove all keys from all databases.
|
228
252
|
#
|
253
|
+
# @param [Hash] options
|
254
|
+
# - `:async => Boolean`: async flush (default: false)
|
229
255
|
# @return [String] `OK`
|
230
|
-
def flushall
|
256
|
+
def flushall(options = nil)
|
231
257
|
synchronize do |client|
|
232
|
-
|
258
|
+
if options && options[:async]
|
259
|
+
client.call([:flushall, :async])
|
260
|
+
else
|
261
|
+
client.call([:flushall])
|
262
|
+
end
|
233
263
|
end
|
234
264
|
end
|
235
265
|
|
236
266
|
# Remove all keys from the current database.
|
237
267
|
#
|
268
|
+
# @param [Hash] options
|
269
|
+
# - `:async => Boolean`: async flush (default: false)
|
238
270
|
# @return [String] `OK`
|
239
|
-
def flushdb
|
271
|
+
def flushdb(options = nil)
|
240
272
|
synchronize do |client|
|
241
|
-
|
273
|
+
if options && options[:async]
|
274
|
+
client.call([:flushdb, :async])
|
275
|
+
else
|
276
|
+
client.call([:flushdb])
|
277
|
+
end
|
242
278
|
end
|
243
279
|
end
|
244
280
|
|
@@ -250,9 +286,7 @@ class Redis
|
|
250
286
|
synchronize do |client|
|
251
287
|
client.call([:info, cmd].compact) do |reply|
|
252
288
|
if reply.kind_of?(String)
|
253
|
-
reply =
|
254
|
-
line.split(":", 2) unless line =~ /^(#|$)/
|
255
|
-
end.compact]
|
289
|
+
reply = HashifyInfo.call(reply)
|
256
290
|
|
257
291
|
if cmd && cmd.to_s == "commandstats"
|
258
292
|
# Extract nested hashes for INFO COMMANDSTATS
|
@@ -270,7 +304,7 @@ class Redis
|
|
270
304
|
|
271
305
|
# Get the UNIX time stamp of the last successful save to disk.
|
272
306
|
#
|
273
|
-
# @return [
|
307
|
+
# @return [Integer]
|
274
308
|
def lastsave
|
275
309
|
synchronize do |client|
|
276
310
|
client.call([:lastsave])
|
@@ -322,8 +356,8 @@ class Redis
|
|
322
356
|
# Interact with the slowlog (get, len, reset)
|
323
357
|
#
|
324
358
|
# @param [String] subcommand e.g. `get`, `len`, `reset`
|
325
|
-
# @param [
|
326
|
-
# @return [Array<String>,
|
359
|
+
# @param [Integer] length maximum number of entries to return
|
360
|
+
# @return [Array<String>, Integer, String] depends on subcommand
|
327
361
|
def slowlog(subcommand, length=nil)
|
328
362
|
synchronize do |client|
|
329
363
|
args = [:slowlog, subcommand]
|
@@ -344,7 +378,7 @@ class Redis
|
|
344
378
|
# @example
|
345
379
|
# r.time # => [ 1333093196, 606806 ]
|
346
380
|
#
|
347
|
-
# @return [Array<
|
381
|
+
# @return [Array<Integer>] tuple of seconds since UNIX epoch and
|
348
382
|
# microseconds in the current second
|
349
383
|
def time
|
350
384
|
synchronize do |client|
|
@@ -367,7 +401,7 @@ class Redis
|
|
367
401
|
# Set a key's time to live in seconds.
|
368
402
|
#
|
369
403
|
# @param [String] key
|
370
|
-
# @param [
|
404
|
+
# @param [Integer] seconds time to live
|
371
405
|
# @return [Boolean] whether the timeout was set or not
|
372
406
|
def expire(key, seconds)
|
373
407
|
synchronize do |client|
|
@@ -378,7 +412,7 @@ class Redis
|
|
378
412
|
# Set the expiration for a key as a UNIX timestamp.
|
379
413
|
#
|
380
414
|
# @param [String] key
|
381
|
-
# @param [
|
415
|
+
# @param [Integer] unix_time expiry time specified as a UNIX timestamp
|
382
416
|
# @return [Boolean] whether the timeout was set or not
|
383
417
|
def expireat(key, unix_time)
|
384
418
|
synchronize do |client|
|
@@ -389,7 +423,7 @@ class Redis
|
|
389
423
|
# Get the time to live (in seconds) for a key.
|
390
424
|
#
|
391
425
|
# @param [String] key
|
392
|
-
# @return [
|
426
|
+
# @return [Integer] remaining time to live in seconds.
|
393
427
|
#
|
394
428
|
# In Redis 2.6 or older the command returns -1 if the key does not exist or if
|
395
429
|
# the key exist but has no associated expire.
|
@@ -407,7 +441,7 @@ class Redis
|
|
407
441
|
# Set a key's time to live in milliseconds.
|
408
442
|
#
|
409
443
|
# @param [String] key
|
410
|
-
# @param [
|
444
|
+
# @param [Integer] milliseconds time to live
|
411
445
|
# @return [Boolean] whether the timeout was set or not
|
412
446
|
def pexpire(key, milliseconds)
|
413
447
|
synchronize do |client|
|
@@ -418,7 +452,7 @@ class Redis
|
|
418
452
|
# Set the expiration for a key as number of milliseconds from UNIX Epoch.
|
419
453
|
#
|
420
454
|
# @param [String] key
|
421
|
-
# @param [
|
455
|
+
# @param [Integer] ms_unix_time expiry time specified as number of milliseconds from UNIX Epoch.
|
422
456
|
# @return [Boolean] whether the timeout was set or not
|
423
457
|
def pexpireat(key, ms_unix_time)
|
424
458
|
synchronize do |client|
|
@@ -429,7 +463,7 @@ class Redis
|
|
429
463
|
# Get the time to live (in milliseconds) for a key.
|
430
464
|
#
|
431
465
|
# @param [String] key
|
432
|
-
# @return [
|
466
|
+
# @return [Integer] remaining time to live in milliseconds
|
433
467
|
# In Redis 2.6 or older the command returns -1 if the key does not exist or if
|
434
468
|
# the key exist but has no associated expire.
|
435
469
|
#
|
@@ -458,43 +492,64 @@ class Redis
|
|
458
492
|
# @param [String] key
|
459
493
|
# @param [String] ttl
|
460
494
|
# @param [String] serialized_value
|
495
|
+
# @param [Hash] options
|
496
|
+
# - `:replace => Boolean`: if false, raises an error if key already exists
|
497
|
+
# @raise [Redis::CommandError]
|
461
498
|
# @return [String] `"OK"`
|
462
|
-
def restore(key, ttl, serialized_value)
|
499
|
+
def restore(key, ttl, serialized_value, options = {})
|
500
|
+
args = [:restore, key, ttl, serialized_value]
|
501
|
+
args << 'REPLACE' if options[:replace]
|
502
|
+
|
463
503
|
synchronize do |client|
|
464
|
-
client.call(
|
504
|
+
client.call(args)
|
465
505
|
end
|
466
506
|
end
|
467
507
|
|
468
508
|
# Transfer a key from the connected instance to another instance.
|
469
509
|
#
|
470
|
-
# @param [String] key
|
510
|
+
# @param [String, Array<String>] key
|
471
511
|
# @param [Hash] options
|
472
512
|
# - `:host => String`: host of instance to migrate to
|
473
513
|
# - `:port => Integer`: port of instance to migrate to
|
474
514
|
# - `:db => Integer`: database to migrate to (default: same as source)
|
475
515
|
# - `:timeout => Integer`: timeout (default: same as connection timeout)
|
516
|
+
# - `:copy => Boolean`: Do not remove the key from the local instance.
|
517
|
+
# - `:replace => Boolean`: Replace existing key on the remote instance.
|
476
518
|
# @return [String] `"OK"`
|
477
519
|
def migrate(key, options)
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
520
|
+
args = [:migrate]
|
521
|
+
args << (options[:host] || raise(':host not specified'))
|
522
|
+
args << (options[:port] || raise(':port not specified'))
|
523
|
+
args << (key.is_a?(String) ? key : '')
|
524
|
+
args << (options[:db] || @client.db).to_i
|
525
|
+
args << (options[:timeout] || @client.timeout).to_i
|
526
|
+
args << 'COPY' if options[:copy]
|
527
|
+
args << 'REPLACE' if options[:replace]
|
528
|
+
args += ['KEYS', *key] if key.is_a?(Array)
|
482
529
|
|
483
|
-
synchronize
|
484
|
-
client.call([:migrate, host, port, key, db, timeout])
|
485
|
-
end
|
530
|
+
synchronize { |client| client.call(args) }
|
486
531
|
end
|
487
532
|
|
488
533
|
# Delete one or more keys.
|
489
534
|
#
|
490
535
|
# @param [String, Array<String>] keys
|
491
|
-
# @return [
|
536
|
+
# @return [Integer] number of keys that were deleted
|
492
537
|
def del(*keys)
|
493
538
|
synchronize do |client|
|
494
539
|
client.call([:del] + keys)
|
495
540
|
end
|
496
541
|
end
|
497
542
|
|
543
|
+
# Unlink one or more keys.
|
544
|
+
#
|
545
|
+
# @param [String, Array<String>] keys
|
546
|
+
# @return [Integer] number of keys that were unlinked
|
547
|
+
def unlink(*keys)
|
548
|
+
synchronize do |client|
|
549
|
+
client.call([:unlink] + keys)
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
498
553
|
# Determine if a key exists.
|
499
554
|
#
|
500
555
|
# @param [String] key
|
@@ -538,7 +593,7 @@ class Redis
|
|
538
593
|
# # => "bar"
|
539
594
|
#
|
540
595
|
# @param [String] key
|
541
|
-
# @param [
|
596
|
+
# @param [Integer] db
|
542
597
|
# @return [Boolean] whether the key was moved or not
|
543
598
|
def move(key, db)
|
544
599
|
synchronize do |client|
|
@@ -602,7 +657,7 @@ class Redis
|
|
602
657
|
# - `:order => String`: combination of `ASC`, `DESC` and optionally `ALPHA`
|
603
658
|
# - `:store => String`: key to store the result at
|
604
659
|
#
|
605
|
-
# @return [Array<String>, Array<Array<String>>,
|
660
|
+
# @return [Array<String>, Array<Array<String>>, Integer]
|
606
661
|
# - when `:get` is not specified, or holds a single element, an array of elements
|
607
662
|
# - when `:get` is specified, and holds more than one element, an array of
|
608
663
|
# elements where every element is an array with the result for every
|
@@ -656,7 +711,7 @@ class Redis
|
|
656
711
|
# # => 4
|
657
712
|
#
|
658
713
|
# @param [String] key
|
659
|
-
# @return [
|
714
|
+
# @return [Integer] value after decrementing it
|
660
715
|
def decr(key)
|
661
716
|
synchronize do |client|
|
662
717
|
client.call([:decr, key])
|
@@ -670,8 +725,8 @@ class Redis
|
|
670
725
|
# # => 0
|
671
726
|
#
|
672
727
|
# @param [String] key
|
673
|
-
# @param [
|
674
|
-
# @return [
|
728
|
+
# @param [Integer] decrement
|
729
|
+
# @return [Integer] value after decrementing it
|
675
730
|
def decrby(key, decrement)
|
676
731
|
synchronize do |client|
|
677
732
|
client.call([:decrby, key, decrement])
|
@@ -685,7 +740,7 @@ class Redis
|
|
685
740
|
# # => 6
|
686
741
|
#
|
687
742
|
# @param [String] key
|
688
|
-
# @return [
|
743
|
+
# @return [Integer] value after incrementing it
|
689
744
|
def incr(key)
|
690
745
|
synchronize do |client|
|
691
746
|
client.call([:incr, key])
|
@@ -699,8 +754,8 @@ class Redis
|
|
699
754
|
# # => 10
|
700
755
|
#
|
701
756
|
# @param [String] key
|
702
|
-
# @param [
|
703
|
-
# @return [
|
757
|
+
# @param [Integer] increment
|
758
|
+
# @return [Integer] value after incrementing it
|
704
759
|
def incrby(key, increment)
|
705
760
|
synchronize do |client|
|
706
761
|
client.call([:incrby, key, increment])
|
@@ -727,8 +782,8 @@ class Redis
|
|
727
782
|
# @param [String] key
|
728
783
|
# @param [String] value
|
729
784
|
# @param [Hash] options
|
730
|
-
# - `:ex =>
|
731
|
-
# - `:px =>
|
785
|
+
# - `:ex => Integer`: Set the specified expire time, in seconds.
|
786
|
+
# - `:px => Integer`: Set the specified expire time, in milliseconds.
|
732
787
|
# - `:nx => true`: Only set the key if it does not already exist.
|
733
788
|
# - `:xx => true`: Only set the key if it already exist.
|
734
789
|
# @return [String, Boolean] `"OK"` or true, false if `:nx => true` or `:xx => true`
|
@@ -756,12 +811,10 @@ class Redis
|
|
756
811
|
end
|
757
812
|
end
|
758
813
|
|
759
|
-
alias :[]= :set
|
760
|
-
|
761
814
|
# Set the time to live in seconds of a key.
|
762
815
|
#
|
763
816
|
# @param [String] key
|
764
|
-
# @param [
|
817
|
+
# @param [Integer] ttl
|
765
818
|
# @param [String] value
|
766
819
|
# @return [String] `"OK"`
|
767
820
|
def setex(key, ttl, value)
|
@@ -773,7 +826,7 @@ class Redis
|
|
773
826
|
# Set the time to live in milliseconds of a key.
|
774
827
|
#
|
775
828
|
# @param [String] key
|
776
|
-
# @param [
|
829
|
+
# @param [Integer] ttl
|
777
830
|
# @param [String] value
|
778
831
|
# @return [String] `"OK"`
|
779
832
|
def psetex(key, ttl, value)
|
@@ -863,12 +916,10 @@ class Redis
|
|
863
916
|
end
|
864
917
|
end
|
865
918
|
|
866
|
-
alias :[] :get
|
867
|
-
|
868
919
|
# Get the values of all the given keys.
|
869
920
|
#
|
870
921
|
# @example
|
871
|
-
# redis.mget("key1", "
|
922
|
+
# redis.mget("key1", "key2")
|
872
923
|
# # => ["v1", "v2"]
|
873
924
|
#
|
874
925
|
# @param [Array<String>] keys
|
@@ -904,9 +955,9 @@ class Redis
|
|
904
955
|
# Overwrite part of a string at key starting at the specified offset.
|
905
956
|
#
|
906
957
|
# @param [String] key
|
907
|
-
# @param [
|
958
|
+
# @param [Integer] offset byte offset
|
908
959
|
# @param [String] value
|
909
|
-
# @return [
|
960
|
+
# @return [Integer] length of the string after it was modified
|
910
961
|
def setrange(key, offset, value)
|
911
962
|
synchronize do |client|
|
912
963
|
client.call([:setrange, key, offset, value.to_s])
|
@@ -916,10 +967,10 @@ class Redis
|
|
916
967
|
# Get a substring of the string stored at a key.
|
917
968
|
#
|
918
969
|
# @param [String] key
|
919
|
-
# @param [
|
920
|
-
# @param [
|
970
|
+
# @param [Integer] start zero-based start offset
|
971
|
+
# @param [Integer] stop zero-based end offset. Use -1 for representing
|
921
972
|
# the end of the string
|
922
|
-
# @return [
|
973
|
+
# @return [Integer] `0` or `1`
|
923
974
|
def getrange(key, start, stop)
|
924
975
|
synchronize do |client|
|
925
976
|
client.call([:getrange, key, start, stop])
|
@@ -929,9 +980,9 @@ class Redis
|
|
929
980
|
# Sets or clears the bit at offset in the string value stored at key.
|
930
981
|
#
|
931
982
|
# @param [String] key
|
932
|
-
# @param [
|
933
|
-
# @param [
|
934
|
-
# @return [
|
983
|
+
# @param [Integer] offset bit offset
|
984
|
+
# @param [Integer] value bit value `0` or `1`
|
985
|
+
# @return [Integer] the original bit value stored at `offset`
|
935
986
|
def setbit(key, offset, value)
|
936
987
|
synchronize do |client|
|
937
988
|
client.call([:setbit, key, offset, value])
|
@@ -941,8 +992,8 @@ class Redis
|
|
941
992
|
# Returns the bit value at offset in the string value stored at key.
|
942
993
|
#
|
943
994
|
# @param [String] key
|
944
|
-
# @param [
|
945
|
-
# @return [
|
995
|
+
# @param [Integer] offset bit offset
|
996
|
+
# @return [Integer] `0` or `1`
|
946
997
|
def getbit(key, offset)
|
947
998
|
synchronize do |client|
|
948
999
|
client.call([:getbit, key, offset])
|
@@ -953,7 +1004,7 @@ class Redis
|
|
953
1004
|
#
|
954
1005
|
# @param [String] key
|
955
1006
|
# @param [String] value value to append
|
956
|
-
# @return [
|
1007
|
+
# @return [Integer] length of the string after appending
|
957
1008
|
def append(key, value)
|
958
1009
|
synchronize do |client|
|
959
1010
|
client.call([:append, key, value])
|
@@ -963,9 +1014,9 @@ class Redis
|
|
963
1014
|
# Count the number of set bits in a range of the string value stored at key.
|
964
1015
|
#
|
965
1016
|
# @param [String] key
|
966
|
-
# @param [
|
967
|
-
# @param [
|
968
|
-
# @return [
|
1017
|
+
# @param [Integer] start start index
|
1018
|
+
# @param [Integer] stop stop index
|
1019
|
+
# @return [Integer] the number of bits set to 1
|
969
1020
|
def bitcount(key, start = 0, stop = -1)
|
970
1021
|
synchronize do |client|
|
971
1022
|
client.call([:bitcount, key, start, stop])
|
@@ -977,7 +1028,7 @@ class Redis
|
|
977
1028
|
# @param [String] operation e.g. `and`, `or`, `xor`, `not`
|
978
1029
|
# @param [String] destkey destination key
|
979
1030
|
# @param [String, Array<String>] keys one or more source keys to perform `operation`
|
980
|
-
# @return [
|
1031
|
+
# @return [Integer] the length of the string stored in `destkey`
|
981
1032
|
def bitop(operation, destkey, *keys)
|
982
1033
|
synchronize do |client|
|
983
1034
|
client.call([:bitop, operation, destkey] + keys)
|
@@ -987,10 +1038,10 @@ class Redis
|
|
987
1038
|
# Return the position of the first bit set to 1 or 0 in a string.
|
988
1039
|
#
|
989
1040
|
# @param [String] key
|
990
|
-
# @param [
|
991
|
-
# @param [
|
992
|
-
# @param [
|
993
|
-
# @return [
|
1041
|
+
# @param [Integer] bit whether to look for the first 1 or 0 bit
|
1042
|
+
# @param [Integer] start start index
|
1043
|
+
# @param [Integer] stop stop index
|
1044
|
+
# @return [Integer] the position of the first 1/0 bit.
|
994
1045
|
# -1 if looking for 1 and it is not found or start and stop are given.
|
995
1046
|
def bitpos(key, bit, start=nil, stop=nil)
|
996
1047
|
if stop and not start
|
@@ -1020,7 +1071,7 @@ class Redis
|
|
1020
1071
|
# Get the length of the value stored in a key.
|
1021
1072
|
#
|
1022
1073
|
# @param [String] key
|
1023
|
-
# @return [
|
1074
|
+
# @return [Integer] the length of the value stored in the key, or 0
|
1024
1075
|
# if the key does not exist
|
1025
1076
|
def strlen(key)
|
1026
1077
|
synchronize do |client|
|
@@ -1031,7 +1082,7 @@ class Redis
|
|
1031
1082
|
# Get the length of a list.
|
1032
1083
|
#
|
1033
1084
|
# @param [String] key
|
1034
|
-
# @return [
|
1085
|
+
# @return [Integer]
|
1035
1086
|
def llen(key)
|
1036
1087
|
synchronize do |client|
|
1037
1088
|
client.call([:llen, key])
|
@@ -1041,8 +1092,8 @@ class Redis
|
|
1041
1092
|
# Prepend one or more values to a list, creating the list if it doesn't exist
|
1042
1093
|
#
|
1043
1094
|
# @param [String] key
|
1044
|
-
# @param [String, Array] value string value, or array of string values to push
|
1045
|
-
# @return [
|
1095
|
+
# @param [String, Array<String>] value string value, or array of string values to push
|
1096
|
+
# @return [Integer] the length of the list after the push operation
|
1046
1097
|
def lpush(key, value)
|
1047
1098
|
synchronize do |client|
|
1048
1099
|
client.call([:lpush, key, value])
|
@@ -1053,7 +1104,7 @@ class Redis
|
|
1053
1104
|
#
|
1054
1105
|
# @param [String] key
|
1055
1106
|
# @param [String] value
|
1056
|
-
# @return [
|
1107
|
+
# @return [Integer] the length of the list after the push operation
|
1057
1108
|
def lpushx(key, value)
|
1058
1109
|
synchronize do |client|
|
1059
1110
|
client.call([:lpushx, key, value])
|
@@ -1063,8 +1114,8 @@ class Redis
|
|
1063
1114
|
# Append one or more values to a list, creating the list if it doesn't exist
|
1064
1115
|
#
|
1065
1116
|
# @param [String] key
|
1066
|
-
# @param [String] value
|
1067
|
-
# @return [
|
1117
|
+
# @param [String, Array<String>] value string value, or array of string values to push
|
1118
|
+
# @return [Integer] the length of the list after the push operation
|
1068
1119
|
def rpush(key, value)
|
1069
1120
|
synchronize do |client|
|
1070
1121
|
client.call([:rpush, key, value])
|
@@ -1075,7 +1126,7 @@ class Redis
|
|
1075
1126
|
#
|
1076
1127
|
# @param [String] key
|
1077
1128
|
# @param [String] value
|
1078
|
-
# @return [
|
1129
|
+
# @return [Integer] the length of the list after the push operation
|
1079
1130
|
def rpushx(key, value)
|
1080
1131
|
synchronize do |client|
|
1081
1132
|
client.call([:rpushx, key, value])
|
@@ -1113,28 +1164,27 @@ class Redis
|
|
1113
1164
|
end
|
1114
1165
|
end
|
1115
1166
|
|
1116
|
-
def _bpop(cmd, args)
|
1117
|
-
|
1118
|
-
|
1119
|
-
case args.last
|
1120
|
-
when Hash
|
1167
|
+
def _bpop(cmd, args, &blk)
|
1168
|
+
timeout = if args.last.is_a?(Hash)
|
1121
1169
|
options = args.pop
|
1122
|
-
|
1170
|
+
options[:timeout]
|
1171
|
+
elsif args.last.respond_to?(:to_int)
|
1123
1172
|
# Issue deprecation notice in obnoxious mode...
|
1124
|
-
|
1173
|
+
args.pop.to_int
|
1125
1174
|
end
|
1126
1175
|
|
1176
|
+
timeout ||= 0
|
1177
|
+
|
1127
1178
|
if args.size > 1
|
1128
1179
|
# Issue deprecation notice in obnoxious mode...
|
1129
1180
|
end
|
1130
1181
|
|
1131
1182
|
keys = args.flatten
|
1132
|
-
timeout = options[:timeout] || 0
|
1133
1183
|
|
1134
1184
|
synchronize do |client|
|
1135
1185
|
command = [cmd, keys, timeout]
|
1136
1186
|
timeout += client.timeout if timeout > 0
|
1137
|
-
client.call_with_timeout(command, timeout)
|
1187
|
+
client.call_with_timeout(command, timeout, &blk)
|
1138
1188
|
end
|
1139
1189
|
end
|
1140
1190
|
|
@@ -1154,7 +1204,7 @@ class Redis
|
|
1154
1204
|
# @param [String, Array<String>] keys one or more keys to perform the
|
1155
1205
|
# blocking pop on
|
1156
1206
|
# @param [Hash] options
|
1157
|
-
# - `:timeout =>
|
1207
|
+
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
1158
1208
|
#
|
1159
1209
|
# @return [nil, [String, String]]
|
1160
1210
|
# - `nil` when the operation timed out
|
@@ -1168,7 +1218,7 @@ class Redis
|
|
1168
1218
|
# @param [String, Array<String>] keys one or more keys to perform the
|
1169
1219
|
# blocking pop on
|
1170
1220
|
# @param [Hash] options
|
1171
|
-
# - `:timeout =>
|
1221
|
+
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
1172
1222
|
#
|
1173
1223
|
# @return [nil, [String, String]]
|
1174
1224
|
# - `nil` when the operation timed out
|
@@ -1185,7 +1235,7 @@ class Redis
|
|
1185
1235
|
# @param [String] source source key
|
1186
1236
|
# @param [String] destination destination key
|
1187
1237
|
# @param [Hash] options
|
1188
|
-
# - `:timeout =>
|
1238
|
+
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
1189
1239
|
#
|
1190
1240
|
# @return [nil, String]
|
1191
1241
|
# - `nil` when the operation timed out
|
@@ -1209,7 +1259,7 @@ class Redis
|
|
1209
1259
|
# Get an element from a list by its index.
|
1210
1260
|
#
|
1211
1261
|
# @param [String] key
|
1212
|
-
# @param [
|
1262
|
+
# @param [Integer] index
|
1213
1263
|
# @return [String]
|
1214
1264
|
def lindex(key, index)
|
1215
1265
|
synchronize do |client|
|
@@ -1223,7 +1273,7 @@ class Redis
|
|
1223
1273
|
# @param [String, Symbol] where `BEFORE` or `AFTER`
|
1224
1274
|
# @param [String] pivot reference element
|
1225
1275
|
# @param [String] value
|
1226
|
-
# @return [
|
1276
|
+
# @return [Integer] length of the list after the insert operation, or `-1`
|
1227
1277
|
# when the element `pivot` was not found
|
1228
1278
|
def linsert(key, where, pivot, value)
|
1229
1279
|
synchronize do |client|
|
@@ -1234,8 +1284,8 @@ class Redis
|
|
1234
1284
|
# Get a range of elements from a list.
|
1235
1285
|
#
|
1236
1286
|
# @param [String] key
|
1237
|
-
# @param [
|
1238
|
-
# @param [
|
1287
|
+
# @param [Integer] start start index
|
1288
|
+
# @param [Integer] stop stop index
|
1239
1289
|
# @return [Array<String>]
|
1240
1290
|
def lrange(key, start, stop)
|
1241
1291
|
synchronize do |client|
|
@@ -1246,12 +1296,12 @@ class Redis
|
|
1246
1296
|
# Remove elements from a list.
|
1247
1297
|
#
|
1248
1298
|
# @param [String] key
|
1249
|
-
# @param [
|
1299
|
+
# @param [Integer] count number of elements to remove. Use a positive
|
1250
1300
|
# value to remove the first `count` occurrences of `value`. A negative
|
1251
1301
|
# value to remove the last `count` occurrences of `value`. Or zero, to
|
1252
1302
|
# remove all occurrences of `value` from the list.
|
1253
1303
|
# @param [String] value
|
1254
|
-
# @return [
|
1304
|
+
# @return [Integer] the number of removed elements
|
1255
1305
|
def lrem(key, count, value)
|
1256
1306
|
synchronize do |client|
|
1257
1307
|
client.call([:lrem, key, count, value])
|
@@ -1261,7 +1311,7 @@ class Redis
|
|
1261
1311
|
# Set the value of an element in a list by its index.
|
1262
1312
|
#
|
1263
1313
|
# @param [String] key
|
1264
|
-
# @param [
|
1314
|
+
# @param [Integer] index
|
1265
1315
|
# @param [String] value
|
1266
1316
|
# @return [String] `OK`
|
1267
1317
|
def lset(key, index, value)
|
@@ -1273,8 +1323,8 @@ class Redis
|
|
1273
1323
|
# Trim a list to the specified range.
|
1274
1324
|
#
|
1275
1325
|
# @param [String] key
|
1276
|
-
# @param [
|
1277
|
-
# @param [
|
1326
|
+
# @param [Integer] start start index
|
1327
|
+
# @param [Integer] stop stop index
|
1278
1328
|
# @return [String] `OK`
|
1279
1329
|
def ltrim(key, start, stop)
|
1280
1330
|
synchronize do |client|
|
@@ -1285,7 +1335,7 @@ class Redis
|
|
1285
1335
|
# Get the number of members in a set.
|
1286
1336
|
#
|
1287
1337
|
# @param [String] key
|
1288
|
-
# @return [
|
1338
|
+
# @return [Integer]
|
1289
1339
|
def scard(key)
|
1290
1340
|
synchronize do |client|
|
1291
1341
|
client.call([:scard, key])
|
@@ -1296,8 +1346,8 @@ class Redis
|
|
1296
1346
|
#
|
1297
1347
|
# @param [String] key
|
1298
1348
|
# @param [String, Array<String>] member one member, or array of members
|
1299
|
-
# @return [Boolean,
|
1300
|
-
# holding whether or not adding the member succeeded, or `
|
1349
|
+
# @return [Boolean, Integer] `Boolean` when a single member is specified,
|
1350
|
+
# holding whether or not adding the member succeeded, or `Integer` when an
|
1301
1351
|
# array of members is specified, holding the number of members that were
|
1302
1352
|
# successfully added
|
1303
1353
|
def sadd(key, member)
|
@@ -1318,8 +1368,8 @@ class Redis
|
|
1318
1368
|
#
|
1319
1369
|
# @param [String] key
|
1320
1370
|
# @param [String, Array<String>] member one member, or array of members
|
1321
|
-
# @return [Boolean,
|
1322
|
-
# holding whether or not removing the member succeeded, or `
|
1371
|
+
# @return [Boolean, Integer] `Boolean` when a single member is specified,
|
1372
|
+
# holding whether or not removing the member succeeded, or `Integer` when an
|
1323
1373
|
# array of members is specified, holding the number of members that were
|
1324
1374
|
# successfully removed
|
1325
1375
|
def srem(key, member)
|
@@ -1340,7 +1390,7 @@ class Redis
|
|
1340
1390
|
#
|
1341
1391
|
# @param [String] key
|
1342
1392
|
# @return [String]
|
1343
|
-
# @param [
|
1393
|
+
# @param [Integer] count
|
1344
1394
|
def spop(key, count = nil)
|
1345
1395
|
synchronize do |client|
|
1346
1396
|
if count.nil?
|
@@ -1354,7 +1404,7 @@ class Redis
|
|
1354
1404
|
# Get one or more random members from a set.
|
1355
1405
|
#
|
1356
1406
|
# @param [String] key
|
1357
|
-
# @param [
|
1407
|
+
# @param [Integer] count
|
1358
1408
|
# @return [String]
|
1359
1409
|
def srandmember(key, count = nil)
|
1360
1410
|
synchronize do |client|
|
@@ -1413,7 +1463,7 @@ class Redis
|
|
1413
1463
|
#
|
1414
1464
|
# @param [String] destination destination key
|
1415
1465
|
# @param [String, Array<String>] keys keys pointing to sets to subtract
|
1416
|
-
# @return [
|
1466
|
+
# @return [Integer] number of elements in the resulting set
|
1417
1467
|
def sdiffstore(destination, *keys)
|
1418
1468
|
synchronize do |client|
|
1419
1469
|
client.call([:sdiffstore, destination] + keys)
|
@@ -1434,7 +1484,7 @@ class Redis
|
|
1434
1484
|
#
|
1435
1485
|
# @param [String] destination destination key
|
1436
1486
|
# @param [String, Array<String>] keys keys pointing to sets to intersect
|
1437
|
-
# @return [
|
1487
|
+
# @return [Integer] number of elements in the resulting set
|
1438
1488
|
def sinterstore(destination, *keys)
|
1439
1489
|
synchronize do |client|
|
1440
1490
|
client.call([:sinterstore, destination] + keys)
|
@@ -1455,7 +1505,7 @@ class Redis
|
|
1455
1505
|
#
|
1456
1506
|
# @param [String] destination destination key
|
1457
1507
|
# @param [String, Array<String>] keys keys pointing to sets to unify
|
1458
|
-
# @return [
|
1508
|
+
# @return [Integer] number of elements in the resulting set
|
1459
1509
|
def sunionstore(destination, *keys)
|
1460
1510
|
synchronize do |client|
|
1461
1511
|
client.call([:sunionstore, destination] + keys)
|
@@ -1469,7 +1519,7 @@ class Redis
|
|
1469
1519
|
# # => 4
|
1470
1520
|
#
|
1471
1521
|
# @param [String] key
|
1472
|
-
# @return [
|
1522
|
+
# @return [Integer]
|
1473
1523
|
def zcard(key)
|
1474
1524
|
synchronize do |client|
|
1475
1525
|
client.call([:zcard, key])
|
@@ -1500,10 +1550,10 @@ class Redis
|
|
1500
1550
|
# - `:incr => true`: When this option is specified ZADD acts like
|
1501
1551
|
# ZINCRBY; only one score-element pair can be specified in this mode
|
1502
1552
|
#
|
1503
|
-
# @return [Boolean,
|
1553
|
+
# @return [Boolean, Integer, Float]
|
1504
1554
|
# - `Boolean` when a single pair is specified, holding whether or not it was
|
1505
1555
|
# **added** to the sorted set.
|
1506
|
-
# - `
|
1556
|
+
# - `Integer` when an array of pairs is specified, holding the number of
|
1507
1557
|
# pairs that were **added** to the sorted set.
|
1508
1558
|
# - `Float` when option :incr is specified, holding the score of the member
|
1509
1559
|
# after incrementing it.
|
@@ -1566,10 +1616,10 @@ class Redis
|
|
1566
1616
|
# - a single member
|
1567
1617
|
# - an array of members
|
1568
1618
|
#
|
1569
|
-
# @return [Boolean,
|
1619
|
+
# @return [Boolean, Integer]
|
1570
1620
|
# - `Boolean` when a single member is specified, holding whether or not it
|
1571
1621
|
# was removed from the sorted set
|
1572
|
-
# - `
|
1622
|
+
# - `Integer` when an array of pairs is specified, holding the number of
|
1573
1623
|
# members that were removed to the sorted set
|
1574
1624
|
def zrem(key, member)
|
1575
1625
|
synchronize do |client|
|
@@ -1585,6 +1635,90 @@ class Redis
|
|
1585
1635
|
end
|
1586
1636
|
end
|
1587
1637
|
|
1638
|
+
# Removes and returns up to count members with the highest scores in the sorted set stored at key.
|
1639
|
+
#
|
1640
|
+
# @example Popping a member
|
1641
|
+
# redis.zpopmax('zset')
|
1642
|
+
# #=> ['b', 2.0]
|
1643
|
+
# @example With count option
|
1644
|
+
# redis.zpopmax('zset', 2)
|
1645
|
+
# #=> [['b', 2.0], ['a', 1.0]]
|
1646
|
+
#
|
1647
|
+
# @params key [String] a key of the sorted set
|
1648
|
+
# @params count [Integer] a number of members
|
1649
|
+
#
|
1650
|
+
# @return [Array<String, Float>] element and score pair if count is not specified
|
1651
|
+
# @return [Array<Array<String, Float>>] list of popped elements and scores
|
1652
|
+
def zpopmax(key, count = nil)
|
1653
|
+
synchronize do |client|
|
1654
|
+
members = client.call([:zpopmax, key, count].compact, &FloatifyPairs)
|
1655
|
+
count.to_i > 1 ? members : members.first
|
1656
|
+
end
|
1657
|
+
end
|
1658
|
+
|
1659
|
+
# Removes and returns up to count members with the lowest scores in the sorted set stored at key.
|
1660
|
+
#
|
1661
|
+
# @example Popping a member
|
1662
|
+
# redis.zpopmin('zset')
|
1663
|
+
# #=> ['a', 1.0]
|
1664
|
+
# @example With count option
|
1665
|
+
# redis.zpopmin('zset', 2)
|
1666
|
+
# #=> [['a', 1.0], ['b', 2.0]]
|
1667
|
+
#
|
1668
|
+
# @params key [String] a key of the sorted set
|
1669
|
+
# @params count [Integer] a number of members
|
1670
|
+
#
|
1671
|
+
# @return [Array<String, Float>] element and score pair if count is not specified
|
1672
|
+
# @return [Array<Array<String, Float>>] list of popped elements and scores
|
1673
|
+
def zpopmin(key, count = nil)
|
1674
|
+
synchronize do |client|
|
1675
|
+
members = client.call([:zpopmin, key, count].compact, &FloatifyPairs)
|
1676
|
+
count.to_i > 1 ? members : members.first
|
1677
|
+
end
|
1678
|
+
end
|
1679
|
+
|
1680
|
+
# Removes and returns up to count members with the highest scores in the sorted set stored at keys,
|
1681
|
+
# or block until one is available.
|
1682
|
+
#
|
1683
|
+
# @example Popping a member from a sorted set
|
1684
|
+
# redis.bzpopmax('zset', 1)
|
1685
|
+
# #=> ['zset', 'b', 2.0]
|
1686
|
+
# @example Popping a member from multiple sorted sets
|
1687
|
+
# redis.bzpopmax('zset1', 'zset2', 1)
|
1688
|
+
# #=> ['zset1', 'b', 2.0]
|
1689
|
+
#
|
1690
|
+
# @params keys [Array<String>] one or multiple keys of the sorted sets
|
1691
|
+
# @params timeout [Integer] the maximum number of seconds to block
|
1692
|
+
#
|
1693
|
+
# @return [Array<String, String, Float>] a touple of key, member and score
|
1694
|
+
# @return [nil] when no element could be popped and the timeout expired
|
1695
|
+
def bzpopmax(*args)
|
1696
|
+
_bpop(:bzpopmax, args) do |reply|
|
1697
|
+
reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
|
1698
|
+
end
|
1699
|
+
end
|
1700
|
+
|
1701
|
+
# Removes and returns up to count members with the lowest scores in the sorted set stored at keys,
|
1702
|
+
# or block until one is available.
|
1703
|
+
#
|
1704
|
+
# @example Popping a member from a sorted set
|
1705
|
+
# redis.bzpopmin('zset', 1)
|
1706
|
+
# #=> ['zset', 'a', 1.0]
|
1707
|
+
# @example Popping a member from multiple sorted sets
|
1708
|
+
# redis.bzpopmin('zset1', 'zset2', 1)
|
1709
|
+
# #=> ['zset1', 'a', 1.0]
|
1710
|
+
#
|
1711
|
+
# @params keys [Array<String>] one or multiple keys of the sorted sets
|
1712
|
+
# @params timeout [Integer] the maximum number of seconds to block
|
1713
|
+
#
|
1714
|
+
# @return [Array<String, String, Float>] a touple of key, member and score
|
1715
|
+
# @return [nil] when no element could be popped and the timeout expired
|
1716
|
+
def bzpopmin(*args)
|
1717
|
+
_bpop(:bzpopmin, args) do |reply|
|
1718
|
+
reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
|
1719
|
+
end
|
1720
|
+
end
|
1721
|
+
|
1588
1722
|
# Get the score associated with the given member in a sorted set.
|
1589
1723
|
#
|
1590
1724
|
# @example Get the score for member "a"
|
@@ -1610,8 +1744,8 @@ class Redis
|
|
1610
1744
|
# # => [["a", 32.0], ["b", 64.0]]
|
1611
1745
|
#
|
1612
1746
|
# @param [String] key
|
1613
|
-
# @param [
|
1614
|
-
# @param [
|
1747
|
+
# @param [Integer] start start index
|
1748
|
+
# @param [Integer] stop stop index
|
1615
1749
|
# @param [Hash] options
|
1616
1750
|
# - `:with_scores => true`: include scores in output
|
1617
1751
|
#
|
@@ -1663,7 +1797,7 @@ class Redis
|
|
1663
1797
|
#
|
1664
1798
|
# @param [String] key
|
1665
1799
|
# @param [String] member
|
1666
|
-
# @return [
|
1800
|
+
# @return [Integer]
|
1667
1801
|
def zrank(key, member)
|
1668
1802
|
synchronize do |client|
|
1669
1803
|
client.call([:zrank, key, member])
|
@@ -1675,7 +1809,7 @@ class Redis
|
|
1675
1809
|
#
|
1676
1810
|
# @param [String] key
|
1677
1811
|
# @param [String] member
|
1678
|
-
# @return [
|
1812
|
+
# @return [Integer]
|
1679
1813
|
def zrevrank(key, member)
|
1680
1814
|
synchronize do |client|
|
1681
1815
|
client.call([:zrevrank, key, member])
|
@@ -1692,15 +1826,39 @@ class Redis
|
|
1692
1826
|
# # => 5
|
1693
1827
|
#
|
1694
1828
|
# @param [String] key
|
1695
|
-
# @param [
|
1696
|
-
# @param [
|
1697
|
-
# @return [
|
1829
|
+
# @param [Integer] start start index
|
1830
|
+
# @param [Integer] stop stop index
|
1831
|
+
# @return [Integer] number of members that were removed
|
1698
1832
|
def zremrangebyrank(key, start, stop)
|
1699
1833
|
synchronize do |client|
|
1700
1834
|
client.call([:zremrangebyrank, key, start, stop])
|
1701
1835
|
end
|
1702
1836
|
end
|
1703
1837
|
|
1838
|
+
# Count the members, with the same score in a sorted set, within the given lexicographical range.
|
1839
|
+
#
|
1840
|
+
# @example Count members matching a
|
1841
|
+
# redis.zlexcount("zset", "[a", "[a\xff")
|
1842
|
+
# # => 1
|
1843
|
+
# @example Count members matching a-z
|
1844
|
+
# redis.zlexcount("zset", "[a", "[z\xff")
|
1845
|
+
# # => 26
|
1846
|
+
#
|
1847
|
+
# @param [String] key
|
1848
|
+
# @param [String] min
|
1849
|
+
# - inclusive minimum is specified by prefixing `(`
|
1850
|
+
# - exclusive minimum is specified by prefixing `[`
|
1851
|
+
# @param [String] max
|
1852
|
+
# - inclusive maximum is specified by prefixing `(`
|
1853
|
+
# - exclusive maximum is specified by prefixing `[`
|
1854
|
+
#
|
1855
|
+
# @return [Integer] number of members within the specified lexicographical range
|
1856
|
+
def zlexcount(key, min, max)
|
1857
|
+
synchronize do |client|
|
1858
|
+
client.call([:zlexcount, key, min, max])
|
1859
|
+
end
|
1860
|
+
end
|
1861
|
+
|
1704
1862
|
# Return a range of members with the same score in a sorted set, by lexicographical ordering
|
1705
1863
|
#
|
1706
1864
|
# @example Retrieve members matching a
|
@@ -1848,7 +2006,7 @@ class Redis
|
|
1848
2006
|
# @param [String] max
|
1849
2007
|
# - inclusive maximum score is specified verbatim
|
1850
2008
|
# - exclusive maximum score is specified by prefixing `(`
|
1851
|
-
# @return [
|
2009
|
+
# @return [Integer] number of members that were removed
|
1852
2010
|
def zremrangebyscore(key, min, max)
|
1853
2011
|
synchronize do |client|
|
1854
2012
|
client.call([:zremrangebyscore, key, min, max])
|
@@ -1871,7 +2029,7 @@ class Redis
|
|
1871
2029
|
# @param [String] max
|
1872
2030
|
# - inclusive maximum score is specified verbatim
|
1873
2031
|
# - exclusive maximum score is specified by prefixing `(`
|
1874
|
-
# @return [
|
2032
|
+
# @return [Integer] number of members in within the specified range
|
1875
2033
|
def zcount(key, min, max)
|
1876
2034
|
synchronize do |client|
|
1877
2035
|
client.call([:zcount, key, min, max])
|
@@ -1891,7 +2049,7 @@ class Redis
|
|
1891
2049
|
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
1892
2050
|
# sorted sets
|
1893
2051
|
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
1894
|
-
# @return [
|
2052
|
+
# @return [Integer] number of elements in the resulting sorted set
|
1895
2053
|
def zinterstore(destination, keys, options = {})
|
1896
2054
|
args = []
|
1897
2055
|
|
@@ -1918,7 +2076,7 @@ class Redis
|
|
1918
2076
|
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
1919
2077
|
# sorted sets
|
1920
2078
|
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
1921
|
-
# @return [
|
2079
|
+
# @return [Integer] number of elements in the resulting sorted set
|
1922
2080
|
def zunionstore(destination, keys, options = {})
|
1923
2081
|
args = []
|
1924
2082
|
|
@@ -1936,7 +2094,7 @@ class Redis
|
|
1936
2094
|
# Get the number of fields in a hash.
|
1937
2095
|
#
|
1938
2096
|
# @param [String] key
|
1939
|
-
# @return [
|
2097
|
+
# @return [Integer] number of fields in the hash
|
1940
2098
|
def hlen(key)
|
1941
2099
|
synchronize do |client|
|
1942
2100
|
client.call([:hlen, key])
|
@@ -2052,10 +2210,10 @@ class Redis
|
|
2052
2210
|
#
|
2053
2211
|
# @param [String] key
|
2054
2212
|
# @param [String, Array<String>] field
|
2055
|
-
# @return [
|
2056
|
-
def hdel(key,
|
2213
|
+
# @return [Integer] the number of fields that were removed from the hash
|
2214
|
+
def hdel(key, *fields)
|
2057
2215
|
synchronize do |client|
|
2058
|
-
client.call([:hdel, key,
|
2216
|
+
client.call([:hdel, key, *fields])
|
2059
2217
|
end
|
2060
2218
|
end
|
2061
2219
|
|
@@ -2074,8 +2232,8 @@ class Redis
|
|
2074
2232
|
#
|
2075
2233
|
# @param [String] key
|
2076
2234
|
# @param [String] field
|
2077
|
-
# @param [
|
2078
|
-
# @return [
|
2235
|
+
# @param [Integer] increment
|
2236
|
+
# @return [Integer] value of the field after incrementing it
|
2079
2237
|
def hincrby(key, field, increment)
|
2080
2238
|
synchronize do |client|
|
2081
2239
|
client.call([:hincrby, key, field, increment])
|
@@ -2253,7 +2411,8 @@ class Redis
|
|
2253
2411
|
def pipelined
|
2254
2412
|
synchronize do |client|
|
2255
2413
|
begin
|
2256
|
-
|
2414
|
+
pipeline = Pipeline.new(@client)
|
2415
|
+
original, @client = @client, pipeline
|
2257
2416
|
yield(self)
|
2258
2417
|
original.call_pipeline(@client)
|
2259
2418
|
ensure
|
@@ -2298,7 +2457,7 @@ class Redis
|
|
2298
2457
|
client.call([:multi])
|
2299
2458
|
else
|
2300
2459
|
begin
|
2301
|
-
pipeline = Pipeline::Multi.new
|
2460
|
+
pipeline = Pipeline::Multi.new(@client)
|
2302
2461
|
original, @client = @client, pipeline
|
2303
2462
|
yield(self)
|
2304
2463
|
original.call_pipeline(pipeline)
|
@@ -2642,7 +2801,7 @@ class Redis
|
|
2642
2801
|
# union of the HyperLogLogs contained in the keys.
|
2643
2802
|
#
|
2644
2803
|
# @param [String, Array<String>] keys
|
2645
|
-
# @return [
|
2804
|
+
# @return [Integer]
|
2646
2805
|
def pfcount(*keys)
|
2647
2806
|
synchronize do |client|
|
2648
2807
|
client.call([:pfcount] + keys)
|
@@ -2661,6 +2820,407 @@ class Redis
|
|
2661
2820
|
end
|
2662
2821
|
end
|
2663
2822
|
|
2823
|
+
# Adds the specified geospatial items (latitude, longitude, name) to the specified key
|
2824
|
+
#
|
2825
|
+
# @param [String] key
|
2826
|
+
# @param [Array] member arguemnts for member or members: longitude, latitude, name
|
2827
|
+
# @return [Integer] number of elements added to the sorted set
|
2828
|
+
def geoadd(key, *member)
|
2829
|
+
synchronize do |client|
|
2830
|
+
client.call([:geoadd, key, *member])
|
2831
|
+
end
|
2832
|
+
end
|
2833
|
+
|
2834
|
+
# Returns geohash string representing position for specified members of the specified key.
|
2835
|
+
#
|
2836
|
+
# @param [String] key
|
2837
|
+
# @param [String, Array<String>] member one member or array of members
|
2838
|
+
# @return [Array<String, nil>] returns array containg geohash string if member is present, nil otherwise
|
2839
|
+
def geohash(key, member)
|
2840
|
+
synchronize do |client|
|
2841
|
+
client.call([:geohash, key, member])
|
2842
|
+
end
|
2843
|
+
end
|
2844
|
+
|
2845
|
+
|
2846
|
+
# Query a sorted set representing a geospatial index to fetch members matching a
|
2847
|
+
# given maximum distance from a point
|
2848
|
+
#
|
2849
|
+
# @param [Array] args key, longitude, latitude, radius, unit(m|km|ft|mi)
|
2850
|
+
# @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest or the farthest to the nearest relative to the center
|
2851
|
+
# @param [Integer] count limit the results to the first N matching items
|
2852
|
+
# @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
|
2853
|
+
# @return [Array<String>] may be changed with `options`
|
2854
|
+
|
2855
|
+
def georadius(*args, **geoptions)
|
2856
|
+
geoarguments = _geoarguments(*args, **geoptions)
|
2857
|
+
|
2858
|
+
synchronize do |client|
|
2859
|
+
client.call([:georadius, *geoarguments])
|
2860
|
+
end
|
2861
|
+
end
|
2862
|
+
|
2863
|
+
# Query a sorted set representing a geospatial index to fetch members matching a
|
2864
|
+
# given maximum distance from an already existing member
|
2865
|
+
#
|
2866
|
+
# @param [Array] args key, member, radius, unit(m|km|ft|mi)
|
2867
|
+
# @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest or the farthest to the nearest relative to the center
|
2868
|
+
# @param [Integer] count limit the results to the first N matching items
|
2869
|
+
# @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
|
2870
|
+
# @return [Array<String>] may be changed with `options`
|
2871
|
+
|
2872
|
+
def georadiusbymember(*args, **geoptions)
|
2873
|
+
geoarguments = _geoarguments(*args, **geoptions)
|
2874
|
+
|
2875
|
+
synchronize do |client|
|
2876
|
+
client.call([:georadiusbymember, *geoarguments])
|
2877
|
+
end
|
2878
|
+
end
|
2879
|
+
|
2880
|
+
# Returns longitude and latitude of members of a geospatial index
|
2881
|
+
#
|
2882
|
+
# @param [String] key
|
2883
|
+
# @param [String, Array<String>] member one member or array of members
|
2884
|
+
# @return [Array<Array<String>, nil>] returns array of elements, where each element is either array of longitude and latitude or nil
|
2885
|
+
def geopos(key, member)
|
2886
|
+
synchronize do |client|
|
2887
|
+
client.call([:geopos, key, member])
|
2888
|
+
end
|
2889
|
+
end
|
2890
|
+
|
2891
|
+
# Returns the distance between two members of a geospatial index
|
2892
|
+
#
|
2893
|
+
# @param [String ]key
|
2894
|
+
# @param [Array<String>] members
|
2895
|
+
# @param ['m', 'km', 'mi', 'ft'] unit
|
2896
|
+
# @return [String, nil] returns distance in spefied unit if both members present, nil otherwise.
|
2897
|
+
def geodist(key, member1, member2, unit = 'm')
|
2898
|
+
synchronize do |client|
|
2899
|
+
client.call([:geodist, key, member1, member2, unit])
|
2900
|
+
end
|
2901
|
+
end
|
2902
|
+
|
2903
|
+
# Returns the stream information each subcommand.
|
2904
|
+
#
|
2905
|
+
# @example stream
|
2906
|
+
# redis.xinfo(:stream, 'mystream')
|
2907
|
+
# @example groups
|
2908
|
+
# redis.xinfo(:groups, 'mystream')
|
2909
|
+
# @example consumers
|
2910
|
+
# redis.xinfo(:consumers, 'mystream', 'mygroup')
|
2911
|
+
#
|
2912
|
+
# @param subcommand [String] e.g. `stream` `groups` `consumers`
|
2913
|
+
# @param key [String] the stream key
|
2914
|
+
# @param group [String] the consumer group name, required if subcommand is `consumers`
|
2915
|
+
#
|
2916
|
+
# @return [Hash] information of the stream if subcommand is `stream`
|
2917
|
+
# @return [Array<Hash>] information of the consumer groups if subcommand is `groups`
|
2918
|
+
# @return [Array<Hash>] information of the consumers if subcommand is `consumers`
|
2919
|
+
def xinfo(subcommand, key, group = nil)
|
2920
|
+
args = [:xinfo, subcommand, key, group].compact
|
2921
|
+
synchronize do |client|
|
2922
|
+
client.call(args) do |reply|
|
2923
|
+
case subcommand.to_s.downcase
|
2924
|
+
when 'stream' then Hashify.call(reply)
|
2925
|
+
when 'groups', 'consumers' then reply.map { |arr| Hashify.call(arr) }
|
2926
|
+
else reply
|
2927
|
+
end
|
2928
|
+
end
|
2929
|
+
end
|
2930
|
+
end
|
2931
|
+
|
2932
|
+
# Add new entry to the stream.
|
2933
|
+
#
|
2934
|
+
# @example Without options
|
2935
|
+
# redis.xadd('mystream', f1: 'v1', f2: 'v2')
|
2936
|
+
# @example With options
|
2937
|
+
# redis.xadd('mystream', { f1: 'v1', f2: 'v2' }, id: '0-0', maxlen: 1000, approximate: true)
|
2938
|
+
#
|
2939
|
+
# @param key [String] the stream key
|
2940
|
+
# @param entry [Hash] one or multiple field-value pairs
|
2941
|
+
# @param opts [Hash] several options for `XADD` command
|
2942
|
+
#
|
2943
|
+
# @option opts [String] :id the entry id, default value is `*`, it means auto generation
|
2944
|
+
# @option opts [Integer] :maxlen max length of entries
|
2945
|
+
# @option opts [Boolean] :approximate whether to add `~` modifier of maxlen or not
|
2946
|
+
#
|
2947
|
+
# @return [String] the entry id
|
2948
|
+
def xadd(key, entry, opts = {})
|
2949
|
+
args = [:xadd, key]
|
2950
|
+
args.concat(['MAXLEN', (opts[:approximate] ? '~' : nil), opts[:maxlen]].compact) if opts[:maxlen]
|
2951
|
+
args << (opts[:id] || '*')
|
2952
|
+
args.concat(entry.to_a.flatten)
|
2953
|
+
synchronize { |client| client.call(args) }
|
2954
|
+
end
|
2955
|
+
|
2956
|
+
# Trims older entries of the stream if needed.
|
2957
|
+
#
|
2958
|
+
# @example Without options
|
2959
|
+
# redis.xtrim('mystream', 1000)
|
2960
|
+
# @example With options
|
2961
|
+
# redis.xtrim('mystream', 1000, approximate: true)
|
2962
|
+
#
|
2963
|
+
# @param key [String] the stream key
|
2964
|
+
# @param mexlen [Integer] max length of entries
|
2965
|
+
# @param approximate [Boolean] whether to add `~` modifier of maxlen or not
|
2966
|
+
#
|
2967
|
+
# @return [Integer] the number of entries actually deleted
|
2968
|
+
def xtrim(key, maxlen, approximate: false)
|
2969
|
+
args = [:xtrim, key, 'MAXLEN', (approximate ? '~' : nil), maxlen].compact
|
2970
|
+
synchronize { |client| client.call(args) }
|
2971
|
+
end
|
2972
|
+
|
2973
|
+
# Delete entries by entry ids.
|
2974
|
+
#
|
2975
|
+
# @example With splatted entry ids
|
2976
|
+
# redis.xdel('mystream', '0-1', '0-2')
|
2977
|
+
# @example With arrayed entry ids
|
2978
|
+
# redis.xdel('mystream', ['0-1', '0-2'])
|
2979
|
+
#
|
2980
|
+
# @param key [String] the stream key
|
2981
|
+
# @param ids [Array<String>] one or multiple entry ids
|
2982
|
+
#
|
2983
|
+
# @return [Integer] the number of entries actually deleted
|
2984
|
+
def xdel(key, *ids)
|
2985
|
+
args = [:xdel, key].concat(ids.flatten)
|
2986
|
+
synchronize { |client| client.call(args) }
|
2987
|
+
end
|
2988
|
+
|
2989
|
+
# Fetches entries of the stream in ascending order.
|
2990
|
+
#
|
2991
|
+
# @example Without options
|
2992
|
+
# redis.xrange('mystream')
|
2993
|
+
# @example With a specific start
|
2994
|
+
# redis.xrange('mystream', '0-1')
|
2995
|
+
# @example With a specific start and end
|
2996
|
+
# redis.xrange('mystream', '0-1', '0-3')
|
2997
|
+
# @example With count options
|
2998
|
+
# redis.xrange('mystream', count: 10)
|
2999
|
+
#
|
3000
|
+
# @param key [String] the stream key
|
3001
|
+
# @param start [String] first entry id of range, default value is `-`
|
3002
|
+
# @param end [String] last entry id of range, default value is `+`
|
3003
|
+
# @param count [Integer] the number of entries as limit
|
3004
|
+
#
|
3005
|
+
# @return [Array<Array<String, Hash>>] the ids and entries pairs
|
3006
|
+
def xrange(key, start = '-', _end = '+', count: nil)
|
3007
|
+
args = [:xrange, key, start, _end]
|
3008
|
+
args.concat(['COUNT', count]) if count
|
3009
|
+
synchronize { |client| client.call(args, &HashifyStreamEntries) }
|
3010
|
+
end
|
3011
|
+
|
3012
|
+
# Fetches entries of the stream in descending order.
|
3013
|
+
#
|
3014
|
+
# @example Without options
|
3015
|
+
# redis.xrevrange('mystream')
|
3016
|
+
# @example With a specific end
|
3017
|
+
# redis.xrevrange('mystream', '0-3')
|
3018
|
+
# @example With a specific end and start
|
3019
|
+
# redis.xrevrange('mystream', '0-3', '0-1')
|
3020
|
+
# @example With count options
|
3021
|
+
# redis.xrevrange('mystream', count: 10)
|
3022
|
+
#
|
3023
|
+
# @param key [String] the stream key
|
3024
|
+
# @param end [String] first entry id of range, default value is `+`
|
3025
|
+
# @param start [String] last entry id of range, default value is `-`
|
3026
|
+
# @params count [Integer] the number of entries as limit
|
3027
|
+
#
|
3028
|
+
# @return [Array<Array<String, Hash>>] the ids and entries pairs
|
3029
|
+
def xrevrange(key, _end = '+', start = '-', count: nil)
|
3030
|
+
args = [:xrevrange, key, _end, start]
|
3031
|
+
args.concat(['COUNT', count]) if count
|
3032
|
+
synchronize { |client| client.call(args, &HashifyStreamEntries) }
|
3033
|
+
end
|
3034
|
+
|
3035
|
+
# Returns the number of entries inside a stream.
|
3036
|
+
#
|
3037
|
+
# @example With key
|
3038
|
+
# redis.xlen('mystream')
|
3039
|
+
#
|
3040
|
+
# @param key [String] the stream key
|
3041
|
+
#
|
3042
|
+
# @return [Integer] the number of entries
|
3043
|
+
def xlen(key)
|
3044
|
+
synchronize { |client| client.call([:xlen, key]) }
|
3045
|
+
end
|
3046
|
+
|
3047
|
+
# Fetches entries from one or multiple streams. Optionally blocking.
|
3048
|
+
#
|
3049
|
+
# @example With a key
|
3050
|
+
# redis.xread('mystream', '0-0')
|
3051
|
+
# @example With multiple keys
|
3052
|
+
# redis.xread(%w[mystream1 mystream2], %w[0-0 0-0])
|
3053
|
+
# @example With count option
|
3054
|
+
# redis.xread('mystream', '0-0', count: 2)
|
3055
|
+
# @example With block option
|
3056
|
+
# redis.xread('mystream', '$', block: 1000)
|
3057
|
+
#
|
3058
|
+
# @param keys [Array<String>] one or multiple stream keys
|
3059
|
+
# @param ids [Array<String>] one or multiple entry ids
|
3060
|
+
# @param count [Integer] the number of entries as limit per stream
|
3061
|
+
# @param block [Integer] the number of milliseconds as blocking timeout
|
3062
|
+
#
|
3063
|
+
# @return [Hash{String => Hash{String => Hash}}] the entries
|
3064
|
+
def xread(keys, ids, count: nil, block: nil)
|
3065
|
+
args = [:xread]
|
3066
|
+
args << 'COUNT' << count if count
|
3067
|
+
args << 'BLOCK' << block.to_i if block
|
3068
|
+
_xread(args, keys, ids, block)
|
3069
|
+
end
|
3070
|
+
|
3071
|
+
# Manages the consumer group of the stream.
|
3072
|
+
#
|
3073
|
+
# @example With `create` subcommand
|
3074
|
+
# redis.xgroup(:create, 'mystream', 'mygroup', '$')
|
3075
|
+
# @example With `setid` subcommand
|
3076
|
+
# redis.xgroup(:setid, 'mystream', 'mygroup', '$')
|
3077
|
+
# @example With `destroy` subcommand
|
3078
|
+
# redis.xgroup(:destroy, 'mystream', 'mygroup')
|
3079
|
+
# @example With `delconsumer` subcommand
|
3080
|
+
# redis.xgroup(:delconsumer, 'mystream', 'mygroup', 'consumer1')
|
3081
|
+
#
|
3082
|
+
# @param subcommand [String] `create` `setid` `destroy` `delconsumer`
|
3083
|
+
# @param key [String] the stream key
|
3084
|
+
# @param group [String] the consumer group name
|
3085
|
+
# @param id_or_consumer [String]
|
3086
|
+
# * the entry id or `$`, required if subcommand is `create` or `setid`
|
3087
|
+
# * the consumer name, required if subcommand is `delconsumer`
|
3088
|
+
# @param mkstream [Boolean] whether to create an empty stream automatically or not
|
3089
|
+
#
|
3090
|
+
# @return [String] `OK` if subcommand is `create` or `setid`
|
3091
|
+
# @return [Integer] effected count if subcommand is `destroy` or `delconsumer`
|
3092
|
+
def xgroup(subcommand, key, group, id_or_consumer = nil, mkstream: false)
|
3093
|
+
args = [:xgroup, subcommand, key, group, id_or_consumer, (mkstream ? 'MKSTREAM' : nil)].compact
|
3094
|
+
synchronize { |client| client.call(args) }
|
3095
|
+
end
|
3096
|
+
|
3097
|
+
# Fetches a subset of the entries from one or multiple streams related with the consumer group.
|
3098
|
+
# Optionally blocking.
|
3099
|
+
#
|
3100
|
+
# @example With a key
|
3101
|
+
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>')
|
3102
|
+
# @example With multiple keys
|
3103
|
+
# redis.xreadgroup('mygroup', 'consumer1', %w[mystream1 mystream2], %w[> >])
|
3104
|
+
# @example With count option
|
3105
|
+
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', count: 2)
|
3106
|
+
# @example With block option
|
3107
|
+
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', block: 1000)
|
3108
|
+
# @example With noack option
|
3109
|
+
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', noack: true)
|
3110
|
+
#
|
3111
|
+
# @param group [String] the consumer group name
|
3112
|
+
# @param consumer [String] the consumer name
|
3113
|
+
# @param keys [Array<String>] one or multiple stream keys
|
3114
|
+
# @param ids [Array<String>] one or multiple entry ids
|
3115
|
+
# @param opts [Hash] several options for `XREADGROUP` command
|
3116
|
+
#
|
3117
|
+
# @option opts [Integer] :count the number of entries as limit
|
3118
|
+
# @option opts [Integer] :block the number of milliseconds as blocking timeout
|
3119
|
+
# @option opts [Boolean] :noack whether message loss is acceptable or not
|
3120
|
+
#
|
3121
|
+
# @return [Hash{String => Hash{String => Hash}}] the entries
|
3122
|
+
def xreadgroup(group, consumer, keys, ids, opts = {})
|
3123
|
+
args = [:xreadgroup, 'GROUP', group, consumer]
|
3124
|
+
args << 'COUNT' << opts[:count] if opts[:count]
|
3125
|
+
args << 'BLOCK' << opts[:block].to_i if opts[:block]
|
3126
|
+
args << 'NOACK' if opts[:noack]
|
3127
|
+
_xread(args, keys, ids, opts[:block])
|
3128
|
+
end
|
3129
|
+
|
3130
|
+
# Removes one or multiple entries from the pending entries list of a stream consumer group.
|
3131
|
+
#
|
3132
|
+
# @example With a entry id
|
3133
|
+
# redis.xack('mystream', 'mygroup', '1526569495631-0')
|
3134
|
+
# @example With splatted entry ids
|
3135
|
+
# redis.xack('mystream', 'mygroup', '0-1', '0-2')
|
3136
|
+
# @example With arrayed entry ids
|
3137
|
+
# redis.xack('mystream', 'mygroup', %w[0-1 0-2])
|
3138
|
+
#
|
3139
|
+
# @param key [String] the stream key
|
3140
|
+
# @param group [String] the consumer group name
|
3141
|
+
# @param ids [Array<String>] one or multiple entry ids
|
3142
|
+
#
|
3143
|
+
# @return [Integer] the number of entries successfully acknowledged
|
3144
|
+
def xack(key, group, *ids)
|
3145
|
+
args = [:xack, key, group].concat(ids.flatten)
|
3146
|
+
synchronize { |client| client.call(args) }
|
3147
|
+
end
|
3148
|
+
|
3149
|
+
# Changes the ownership of a pending entry
|
3150
|
+
#
|
3151
|
+
# @example With splatted entry ids
|
3152
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-1', '0-2')
|
3153
|
+
# @example With arrayed entry ids
|
3154
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2])
|
3155
|
+
# @example With idle option
|
3156
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], idle: 1000)
|
3157
|
+
# @example With time option
|
3158
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], time: 1542866959000)
|
3159
|
+
# @example With retrycount option
|
3160
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], retrycount: 10)
|
3161
|
+
# @example With force option
|
3162
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], force: true)
|
3163
|
+
# @example With justid option
|
3164
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], justid: true)
|
3165
|
+
#
|
3166
|
+
# @param key [String] the stream key
|
3167
|
+
# @param group [String] the consumer group name
|
3168
|
+
# @param consumer [String] the consumer name
|
3169
|
+
# @param min_idle_time [Integer] the number of milliseconds
|
3170
|
+
# @param ids [Array<String>] one or multiple entry ids
|
3171
|
+
# @param opts [Hash] several options for `XCLAIM` command
|
3172
|
+
#
|
3173
|
+
# @option opts [Integer] :idle the number of milliseconds as last time it was delivered of the entry
|
3174
|
+
# @option opts [Integer] :time the number of milliseconds as a specific Unix Epoch time
|
3175
|
+
# @option opts [Integer] :retrycount the number of retry counter
|
3176
|
+
# @option opts [Boolean] :force whether to create the pending entry to the pending entries list or not
|
3177
|
+
# @option opts [Boolean] :justid whether to fetch just an array of entry ids or not
|
3178
|
+
#
|
3179
|
+
# @return [Hash{String => Hash}] the entries successfully claimed
|
3180
|
+
# @return [Array<String>] the entry ids successfully claimed if justid option is `true`
|
3181
|
+
def xclaim(key, group, consumer, min_idle_time, *ids, **opts)
|
3182
|
+
args = [:xclaim, key, group, consumer, min_idle_time].concat(ids.flatten)
|
3183
|
+
args.concat(['IDLE', opts[:idle].to_i]) if opts[:idle]
|
3184
|
+
args.concat(['TIME', opts[:time].to_i]) if opts[:time]
|
3185
|
+
args.concat(['RETRYCOUNT', opts[:retrycount]]) if opts[:retrycount]
|
3186
|
+
args << 'FORCE' if opts[:force]
|
3187
|
+
args << 'JUSTID' if opts[:justid]
|
3188
|
+
blk = opts[:justid] ? Noop : HashifyStreamEntries
|
3189
|
+
synchronize { |client| client.call(args, &blk) }
|
3190
|
+
end
|
3191
|
+
|
3192
|
+
# Fetches not acknowledging pending entries
|
3193
|
+
#
|
3194
|
+
# @example With key and group
|
3195
|
+
# redis.xpending('mystream', 'mygroup')
|
3196
|
+
# @example With range options
|
3197
|
+
# redis.xpending('mystream', 'mygroup', '-', '+', 10)
|
3198
|
+
# @example With range and consumer options
|
3199
|
+
# redis.xpending('mystream', 'mygroup', '-', '+', 10, 'consumer1')
|
3200
|
+
#
|
3201
|
+
# @param key [String] the stream key
|
3202
|
+
# @param group [String] the consumer group name
|
3203
|
+
# @param start [String] start first entry id of range
|
3204
|
+
# @param end [String] end last entry id of range
|
3205
|
+
# @param count [Integer] count the number of entries as limit
|
3206
|
+
# @param consumer [String] the consumer name
|
3207
|
+
#
|
3208
|
+
# @return [Hash] the summary of pending entries
|
3209
|
+
# @return [Array<Hash>] the pending entries details if options were specified
|
3210
|
+
def xpending(key, group, *args)
|
3211
|
+
command_args = [:xpending, key, group]
|
3212
|
+
case args.size
|
3213
|
+
when 0, 3, 4
|
3214
|
+
command_args.concat(args)
|
3215
|
+
else
|
3216
|
+
raise ArgumentError, "wrong number of arguments (given #{args.size + 2}, expected 2, 5 or 6)"
|
3217
|
+
end
|
3218
|
+
|
3219
|
+
summary_needed = args.empty?
|
3220
|
+
blk = summary_needed ? HashifyStreamPendings : HashifyStreamPendingDetails
|
3221
|
+
synchronize { |client| client.call(command_args, &blk) }
|
3222
|
+
end
|
3223
|
+
|
2664
3224
|
# Interact with the sentinel command (masters, master, slaves, failover)
|
2665
3225
|
#
|
2666
3226
|
# @param [String] subcommand e.g. `masters`, `master`, `slaves`
|
@@ -2688,6 +3248,41 @@ class Redis
|
|
2688
3248
|
end
|
2689
3249
|
end
|
2690
3250
|
|
3251
|
+
# Sends `CLUSTER *` command to random node and returns its reply.
|
3252
|
+
#
|
3253
|
+
# @see https://redis.io/commands#cluster Reference of cluster command
|
3254
|
+
#
|
3255
|
+
# @param subcommand [String, Symbol] the subcommand of cluster command
|
3256
|
+
# e.g. `:slots`, `:nodes`, `:slaves`, `:info`
|
3257
|
+
#
|
3258
|
+
# @return [Object] depends on the subcommand
|
3259
|
+
def cluster(subcommand, *args)
|
3260
|
+
subcommand = subcommand.to_s.downcase
|
3261
|
+
block = case subcommand
|
3262
|
+
when 'slots' then HashifyClusterSlots
|
3263
|
+
when 'nodes' then HashifyClusterNodes
|
3264
|
+
when 'slaves' then HashifyClusterSlaves
|
3265
|
+
when 'info' then HashifyInfo
|
3266
|
+
else Noop
|
3267
|
+
end
|
3268
|
+
|
3269
|
+
# @see https://github.com/antirez/redis/blob/unstable/src/redis-trib.rb#L127 raw reply expected
|
3270
|
+
block = Noop unless @cluster_mode
|
3271
|
+
|
3272
|
+
synchronize do |client|
|
3273
|
+
client.call([:cluster, subcommand] + args, &block)
|
3274
|
+
end
|
3275
|
+
end
|
3276
|
+
|
3277
|
+
# Sends `ASKING` command to random node and returns its reply.
|
3278
|
+
#
|
3279
|
+
# @see https://redis.io/topics/cluster-spec#ask-redirection ASK redirection
|
3280
|
+
#
|
3281
|
+
# @return [String] `'OK'`
|
3282
|
+
def asking
|
3283
|
+
synchronize { |client| client.call(%i[asking]) }
|
3284
|
+
end
|
3285
|
+
|
2691
3286
|
def id
|
2692
3287
|
@original_client.id
|
2693
3288
|
end
|
@@ -2701,12 +3296,14 @@ class Redis
|
|
2701
3296
|
end
|
2702
3297
|
|
2703
3298
|
def connection
|
3299
|
+
return @original_client.connection_info if @cluster_mode
|
3300
|
+
|
2704
3301
|
{
|
2705
|
-
:
|
2706
|
-
:
|
2707
|
-
:
|
2708
|
-
:
|
2709
|
-
:
|
3302
|
+
host: @original_client.host,
|
3303
|
+
port: @original_client.port,
|
3304
|
+
db: @original_client.db,
|
3305
|
+
id: @original_client.id,
|
3306
|
+
location: @original_client.location
|
2710
3307
|
}
|
2711
3308
|
end
|
2712
3309
|
|
@@ -2721,48 +3318,144 @@ private
|
|
2721
3318
|
# Commands returning 1 for true and 0 for false may be executed in a pipeline
|
2722
3319
|
# where the method call will return nil. Propagate the nil instead of falsely
|
2723
3320
|
# returning false.
|
2724
|
-
Boolify =
|
2725
|
-
|
2726
|
-
|
2727
|
-
|
3321
|
+
Boolify = lambda { |value|
|
3322
|
+
case value
|
3323
|
+
when 1
|
3324
|
+
true
|
3325
|
+
when 0
|
3326
|
+
false
|
3327
|
+
else
|
3328
|
+
value
|
3329
|
+
end
|
3330
|
+
}
|
2728
3331
|
|
2729
|
-
BoolifySet =
|
2730
|
-
|
2731
|
-
|
2732
|
-
|
2733
|
-
|
2734
|
-
|
2735
|
-
|
2736
|
-
|
3332
|
+
BoolifySet = lambda { |value|
|
3333
|
+
case value
|
3334
|
+
when "OK"
|
3335
|
+
true
|
3336
|
+
when nil
|
3337
|
+
false
|
3338
|
+
else
|
3339
|
+
value
|
3340
|
+
end
|
3341
|
+
}
|
2737
3342
|
|
2738
|
-
Hashify =
|
2739
|
-
|
2740
|
-
|
2741
|
-
|
2742
|
-
|
2743
|
-
|
2744
|
-
|
2745
|
-
|
3343
|
+
Hashify = lambda { |value|
|
3344
|
+
if value.respond_to?(:each_slice)
|
3345
|
+
value.each_slice(2).to_h
|
3346
|
+
else
|
3347
|
+
value
|
3348
|
+
end
|
3349
|
+
}
|
3350
|
+
|
3351
|
+
Floatify = lambda { |value|
|
3352
|
+
case value
|
3353
|
+
when "inf"
|
3354
|
+
Float::INFINITY
|
3355
|
+
when "-inf"
|
3356
|
+
-Float::INFINITY
|
3357
|
+
when String
|
3358
|
+
Float(value)
|
3359
|
+
else
|
3360
|
+
value
|
3361
|
+
end
|
3362
|
+
}
|
2746
3363
|
|
2747
|
-
|
2748
|
-
|
2749
|
-
|
2750
|
-
|
2751
|
-
|
2752
|
-
|
2753
|
-
|
2754
|
-
|
2755
|
-
|
3364
|
+
FloatifyPairs = lambda { |value|
|
3365
|
+
return value unless value.respond_to?(:each_slice)
|
3366
|
+
|
3367
|
+
value.each_slice(2).map do |member, score|
|
3368
|
+
[member, Floatify.call(score)]
|
3369
|
+
end
|
3370
|
+
}
|
3371
|
+
|
3372
|
+
HashifyInfo = lambda { |reply|
|
3373
|
+
lines = reply.split("\r\n").grep_v(/^(#|$)/)
|
3374
|
+
lines.map! { |line| line.split(':', 2) }
|
3375
|
+
lines.compact!
|
3376
|
+
lines.to_h
|
3377
|
+
}
|
3378
|
+
|
3379
|
+
HashifyStreams = lambda { |reply|
|
3380
|
+
case reply
|
3381
|
+
when nil
|
3382
|
+
{}
|
3383
|
+
else
|
3384
|
+
reply.map { |key, entries| [key, HashifyStreamEntries.call(entries)] }.to_h
|
3385
|
+
end
|
3386
|
+
}
|
3387
|
+
|
3388
|
+
HashifyStreamEntries = lambda { |reply|
|
3389
|
+
reply.map do |entry_id, values|
|
3390
|
+
[entry_id, values.each_slice(2).to_h]
|
3391
|
+
end
|
3392
|
+
}
|
3393
|
+
|
3394
|
+
HashifyStreamPendings = lambda { |reply|
|
3395
|
+
{
|
3396
|
+
'size' => reply[0],
|
3397
|
+
'min_entry_id' => reply[1],
|
3398
|
+
'max_entry_id' => reply[2],
|
3399
|
+
'consumers' => reply[3].nil? ? {} : reply[3].to_h
|
2756
3400
|
}
|
3401
|
+
}
|
2757
3402
|
|
2758
|
-
|
2759
|
-
|
2760
|
-
|
2761
|
-
|
2762
|
-
|
2763
|
-
|
2764
|
-
|
3403
|
+
HashifyStreamPendingDetails = lambda { |reply|
|
3404
|
+
reply.map do |arr|
|
3405
|
+
{
|
3406
|
+
'entry_id' => arr[0],
|
3407
|
+
'consumer' => arr[1],
|
3408
|
+
'elapsed' => arr[2],
|
3409
|
+
'count' => arr[3]
|
3410
|
+
}
|
3411
|
+
end
|
3412
|
+
}
|
3413
|
+
|
3414
|
+
HashifyClusterNodeInfo = lambda { |str|
|
3415
|
+
arr = str.split(' ')
|
3416
|
+
{
|
3417
|
+
'node_id' => arr[0],
|
3418
|
+
'ip_port' => arr[1],
|
3419
|
+
'flags' => arr[2].split(','),
|
3420
|
+
'master_node_id' => arr[3],
|
3421
|
+
'ping_sent' => arr[4],
|
3422
|
+
'pong_recv' => arr[5],
|
3423
|
+
'config_epoch' => arr[6],
|
3424
|
+
'link_state' => arr[7],
|
3425
|
+
'slots' => arr[8].nil? ? nil : Range.new(*arr[8].split('-'))
|
2765
3426
|
}
|
3427
|
+
}
|
3428
|
+
|
3429
|
+
HashifyClusterSlots = lambda { |reply|
|
3430
|
+
reply.map do |arr|
|
3431
|
+
first_slot, last_slot = arr[0..1]
|
3432
|
+
master = { 'ip' => arr[2][0], 'port' => arr[2][1], 'node_id' => arr[2][2] }
|
3433
|
+
replicas = arr[3..-1].map { |r| { 'ip' => r[0], 'port' => r[1], 'node_id' => r[2] } }
|
3434
|
+
{
|
3435
|
+
'start_slot' => first_slot,
|
3436
|
+
'end_slot' => last_slot,
|
3437
|
+
'master' => master,
|
3438
|
+
'replicas' => replicas
|
3439
|
+
}
|
3440
|
+
end
|
3441
|
+
}
|
3442
|
+
|
3443
|
+
HashifyClusterNodes = lambda { |reply|
|
3444
|
+
reply.split(/[\r\n]+/).map { |str| HashifyClusterNodeInfo.call(str) }
|
3445
|
+
}
|
3446
|
+
|
3447
|
+
HashifyClusterSlaves = lambda { |reply|
|
3448
|
+
reply.map { |str| HashifyClusterNodeInfo.call(str) }
|
3449
|
+
}
|
3450
|
+
|
3451
|
+
Noop = ->(reply) { reply }
|
3452
|
+
|
3453
|
+
def _geoarguments(*args, options: nil, sort: nil, count: nil)
|
3454
|
+
args.push sort if sort
|
3455
|
+
args.push 'count', count if count
|
3456
|
+
args.push options if options
|
3457
|
+
args
|
3458
|
+
end
|
2766
3459
|
|
2767
3460
|
def _subscription(method, timeout, channels, block)
|
2768
3461
|
return @client.call([method] + channels) if subscribed?
|
@@ -2779,10 +3472,29 @@ private
|
|
2779
3472
|
end
|
2780
3473
|
end
|
2781
3474
|
|
3475
|
+
def _xread(args, keys, ids, blocking_timeout_msec)
|
3476
|
+
keys = keys.is_a?(Array) ? keys : [keys]
|
3477
|
+
ids = ids.is_a?(Array) ? ids : [ids]
|
3478
|
+
args << 'STREAMS'
|
3479
|
+
args.concat(keys)
|
3480
|
+
args.concat(ids)
|
3481
|
+
|
3482
|
+
synchronize do |client|
|
3483
|
+
if blocking_timeout_msec.nil?
|
3484
|
+
client.call(args, &HashifyStreams)
|
3485
|
+
elsif blocking_timeout_msec.to_f.zero?
|
3486
|
+
client.call_without_timeout(args, &HashifyStreams)
|
3487
|
+
else
|
3488
|
+
timeout = client.timeout.to_f + blocking_timeout_msec.to_f / 1000.0
|
3489
|
+
client.call_with_timeout(args, timeout, &HashifyStreams)
|
3490
|
+
end
|
3491
|
+
end
|
3492
|
+
end
|
2782
3493
|
end
|
2783
3494
|
|
2784
|
-
|
2785
|
-
|
2786
|
-
|
2787
|
-
|
2788
|
-
|
3495
|
+
require_relative "redis/version"
|
3496
|
+
require_relative "redis/connection"
|
3497
|
+
require_relative "redis/client"
|
3498
|
+
require_relative "redis/cluster"
|
3499
|
+
require_relative "redis/pipeline"
|
3500
|
+
require_relative "redis/subscribe"
|