redis 4.0.0.rc1 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +8 -6
- data/.travis/Gemfile +3 -1
- data/CHANGELOG.md +6 -2
- data/README.md +5 -5
- data/bors.toml +14 -0
- data/examples/dist_redis.rb +43 -0
- data/lib/redis.rb +37 -6
- data/lib/redis/client.rb +5 -6
- data/lib/redis/connection/ruby.rb +1 -1
- data/lib/redis/connection/synchrony.rb +9 -1
- data/lib/redis/distributed.rb +873 -0
- data/lib/redis/hash_ring.rb +88 -0
- data/lib/redis/version.rb +1 -1
- data/redis.gemspec +3 -1
- data/test/blocking_commands_test.rb +2 -145
- data/test/commands_on_hashes_test.rb +2 -157
- data/test/commands_on_hyper_log_log_test.rb +2 -53
- data/test/commands_on_lists_test.rb +2 -138
- data/test/commands_on_sets_test.rb +2 -135
- data/test/commands_on_sorted_sets_test.rb +13 -307
- data/test/commands_on_strings_test.rb +2 -241
- data/test/commands_on_value_types_test.rb +2 -117
- data/test/distributed_blocking_commands_test.rb +44 -0
- data/test/distributed_commands_on_hashes_test.rb +8 -0
- data/test/distributed_commands_on_hyper_log_log_test.rb +31 -0
- data/test/distributed_commands_on_lists_test.rb +20 -0
- data/test/distributed_commands_on_sets_test.rb +81 -0
- data/test/distributed_commands_on_sorted_sets_test.rb +16 -0
- data/test/distributed_commands_on_strings_test.rb +57 -0
- data/test/distributed_commands_on_value_types_test.rb +93 -0
- data/test/distributed_commands_requiring_clustering_test.rb +162 -0
- data/test/distributed_connection_handling_test.rb +21 -0
- data/test/distributed_internals_test.rb +68 -0
- data/test/distributed_key_tags_test.rb +50 -0
- data/test/distributed_persistence_control_commands_test.rb +24 -0
- data/test/distributed_publish_subscribe_test.rb +90 -0
- data/test/distributed_remote_server_control_commands_test.rb +64 -0
- data/test/distributed_scripting_test.rb +100 -0
- data/test/distributed_sorting_test.rb +18 -0
- data/test/distributed_test.rb +56 -0
- data/test/distributed_transactions_test.rb +30 -0
- data/test/helper.rb +22 -0
- data/test/internals_test.rb +7 -13
- data/test/lint/blocking_commands.rb +150 -0
- data/test/lint/hashes.rb +162 -0
- data/test/lint/hyper_log_log.rb +60 -0
- data/test/lint/lists.rb +143 -0
- data/test/lint/sets.rb +140 -0
- data/test/lint/sorted_sets.rb +316 -0
- data/test/lint/strings.rb +246 -0
- data/test/lint/value_types.rb +130 -0
- data/test/remote_server_control_commands_test.rb +1 -1
- metadata +66 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0f10522316b60319f14cbbf00cf0c82f13092997
|
|
4
|
+
data.tar.gz: 1aab75c09b750f2f2f5038cc14366b3c79e0e01c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 78b8a72a6bc475aa55543abf515b3758af1a696a3f1adcaa9019898bf99acf0162383964880b1e6d07113205d0129ed84563de44a4680f9f15cc5893c44efae0
|
|
7
|
+
data.tar.gz: ad7ed9b0a0afb0a6c85bf5cd9cada2081c64b3b25856e0343bef61a62e2f4c7f376f42c32013092ec4dde55ed4182c6b11d1f0ace54167f49a597181abdbcf29
|
data/.travis.yml
CHANGED
|
@@ -2,13 +2,10 @@ language: ruby
|
|
|
2
2
|
|
|
3
3
|
script: make test
|
|
4
4
|
|
|
5
|
-
before_install:
|
|
6
|
-
- rvm get stable
|
|
7
|
-
|
|
8
5
|
rvm:
|
|
9
|
-
- 2.
|
|
10
|
-
- 2.
|
|
11
|
-
- 2.
|
|
6
|
+
- 2.2.2
|
|
7
|
+
- 2.3.3
|
|
8
|
+
- 2.4.1
|
|
12
9
|
- jruby-9
|
|
13
10
|
- rbx-3
|
|
14
11
|
|
|
@@ -16,6 +13,9 @@ gemfile: ".travis/Gemfile"
|
|
|
16
13
|
|
|
17
14
|
sudo: false
|
|
18
15
|
|
|
16
|
+
before_script:
|
|
17
|
+
- if (ruby -e "exit RUBY_VERSION.to_f >= 2.4"); then export RUBYOPT="--enable-frozen-string-literal"; fi; echo $RUBYOPT
|
|
18
|
+
|
|
19
19
|
env:
|
|
20
20
|
global:
|
|
21
21
|
- VERBOSE=true
|
|
@@ -31,6 +31,8 @@ env:
|
|
|
31
31
|
|
|
32
32
|
branches:
|
|
33
33
|
only:
|
|
34
|
+
- staging
|
|
35
|
+
- trying
|
|
34
36
|
- master
|
|
35
37
|
|
|
36
38
|
matrix:
|
data/.travis/Gemfile
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -4,11 +4,15 @@
|
|
|
4
4
|
|
|
5
5
|
* Removed `Redis#[]` and `Redis#[]=` aliases.
|
|
6
6
|
|
|
7
|
-
* Removed `Redis::Distributed`.
|
|
8
|
-
|
|
9
7
|
* Added support for `CLIENT` commands. The lower-level client can be
|
|
10
8
|
accessed via `Redis#_client`.
|
|
11
9
|
|
|
10
|
+
* Dropped official support for Ruby < 2.2.2.
|
|
11
|
+
|
|
12
|
+
# 3.3.3
|
|
13
|
+
|
|
14
|
+
* Improved timeout handling after dropping Timeout module.
|
|
15
|
+
|
|
12
16
|
# 3.3.2
|
|
13
17
|
|
|
14
18
|
* Added support for `SPOP` with COUNT. See #628.
|
data/README.md
CHANGED
|
@@ -355,12 +355,12 @@ client and evangelized Redis in Rubyland. Thank you, Ezra.
|
|
|
355
355
|
requests. You can also ask for help at `#redis-rb` on Freenode.
|
|
356
356
|
|
|
357
357
|
|
|
358
|
-
[inchpages-image]: https://inch-ci.org/github/redis/redis-rb.
|
|
358
|
+
[inchpages-image]: https://inch-ci.org/github/redis/redis-rb.svg
|
|
359
359
|
[inchpages-link]: https://inch-ci.org/github/redis/redis-rb
|
|
360
|
-
[redis-commands]:
|
|
361
|
-
[redis-home]:
|
|
360
|
+
[redis-commands]: https://redis.io/commands
|
|
361
|
+
[redis-home]: https://redis.io
|
|
362
362
|
[redis-url]: http://www.iana.org/assignments/uri-schemes/prov/redis
|
|
363
363
|
[travis-home]: https://travis-ci.org/
|
|
364
|
-
[travis-image]: https://secure.travis-ci.org/redis/redis-rb.
|
|
364
|
+
[travis-image]: https://secure.travis-ci.org/redis/redis-rb.svg?branch=master
|
|
365
365
|
[travis-link]: https://travis-ci.org/redis/redis-rb
|
|
366
|
-
[rubydoc]:
|
|
366
|
+
[rubydoc]: https://www.rubydoc.info/gems/redis
|
data/bors.toml
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Gate on Travis CI
|
|
2
|
+
status = ["continuous-integration/travis-ci/push"]
|
|
3
|
+
|
|
4
|
+
# Set bors's timeout to 6 hours
|
|
5
|
+
#
|
|
6
|
+
# bors's timeout should always be twice a long as the test suite takes.
|
|
7
|
+
# This is to allow Travis to fast-fail a test; if one of the builders
|
|
8
|
+
# immediately reports a failure, then bors will move on to the next batch,
|
|
9
|
+
# leaving the slower builders to work through the already-doomed run and
|
|
10
|
+
# the next one.
|
|
11
|
+
#
|
|
12
|
+
# At the time it was written, a run had taken 3 hours.
|
|
13
|
+
# bors's default timeout is 4 hours.
|
|
14
|
+
timeout_sec = 14400
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require "redis"
|
|
2
|
+
require "redis/distributed"
|
|
3
|
+
|
|
4
|
+
r = Redis::Distributed.new %w[redis://localhost:6379 redis://localhost:6380 redis://localhost:6381 redis://localhost:6382]
|
|
5
|
+
|
|
6
|
+
r.flushdb
|
|
7
|
+
|
|
8
|
+
r['urmom'] = 'urmom'
|
|
9
|
+
r['urdad'] = 'urdad'
|
|
10
|
+
r['urmom1'] = 'urmom1'
|
|
11
|
+
r['urdad1'] = 'urdad1'
|
|
12
|
+
r['urmom2'] = 'urmom2'
|
|
13
|
+
r['urdad2'] = 'urdad2'
|
|
14
|
+
r['urmom3'] = 'urmom3'
|
|
15
|
+
r['urdad3'] = 'urdad3'
|
|
16
|
+
p r['urmom']
|
|
17
|
+
p r['urdad']
|
|
18
|
+
p r['urmom1']
|
|
19
|
+
p r['urdad1']
|
|
20
|
+
p r['urmom2']
|
|
21
|
+
p r['urdad2']
|
|
22
|
+
p r['urmom3']
|
|
23
|
+
p r['urdad3']
|
|
24
|
+
|
|
25
|
+
r.rpush 'listor', 'foo1'
|
|
26
|
+
r.rpush 'listor', 'foo2'
|
|
27
|
+
r.rpush 'listor', 'foo3'
|
|
28
|
+
r.rpush 'listor', 'foo4'
|
|
29
|
+
r.rpush 'listor', 'foo5'
|
|
30
|
+
|
|
31
|
+
p r.rpop('listor')
|
|
32
|
+
p r.rpop('listor')
|
|
33
|
+
p r.rpop('listor')
|
|
34
|
+
p r.rpop('listor')
|
|
35
|
+
p r.rpop('listor')
|
|
36
|
+
|
|
37
|
+
puts "key distribution:"
|
|
38
|
+
|
|
39
|
+
r.ring.nodes.each do |node|
|
|
40
|
+
p [node.client, node.keys("*")]
|
|
41
|
+
end
|
|
42
|
+
r.flushdb
|
|
43
|
+
p r.keys('*')
|
data/lib/redis.rb
CHANGED
|
@@ -134,10 +134,11 @@ class Redis
|
|
|
134
134
|
|
|
135
135
|
# Ping the server.
|
|
136
136
|
#
|
|
137
|
+
# @param [optional, String] message
|
|
137
138
|
# @return [String] `PONG`
|
|
138
|
-
def ping
|
|
139
|
+
def ping(message = nil)
|
|
139
140
|
synchronize do |client|
|
|
140
|
-
client.call([:ping])
|
|
141
|
+
client.call([:ping, message].compact)
|
|
141
142
|
end
|
|
142
143
|
end
|
|
143
144
|
|
|
@@ -468,10 +469,16 @@ class Redis
|
|
|
468
469
|
# @param [String] key
|
|
469
470
|
# @param [String] ttl
|
|
470
471
|
# @param [String] serialized_value
|
|
472
|
+
# @param [Hash] options
|
|
473
|
+
# - `:replace => Boolean`: if false, raises an error if key already exists
|
|
474
|
+
# @raise [Redis::CommandError]
|
|
471
475
|
# @return [String] `"OK"`
|
|
472
|
-
def restore(key, ttl, serialized_value)
|
|
476
|
+
def restore(key, ttl, serialized_value, options = {})
|
|
477
|
+
args = [:restore, key, ttl, serialized_value]
|
|
478
|
+
args << 'REPLACE' if options[:replace]
|
|
479
|
+
|
|
473
480
|
synchronize do |client|
|
|
474
|
-
client.call(
|
|
481
|
+
client.call(args)
|
|
475
482
|
end
|
|
476
483
|
end
|
|
477
484
|
|
|
@@ -1047,7 +1054,7 @@ class Redis
|
|
|
1047
1054
|
# Prepend one or more values to a list, creating the list if it doesn't exist
|
|
1048
1055
|
#
|
|
1049
1056
|
# @param [String] key
|
|
1050
|
-
# @param [String, Array] value string value, or array of string values to push
|
|
1057
|
+
# @param [String, Array<String>] value string value, or array of string values to push
|
|
1051
1058
|
# @return [Fixnum] the length of the list after the push operation
|
|
1052
1059
|
def lpush(key, value)
|
|
1053
1060
|
synchronize do |client|
|
|
@@ -1069,7 +1076,7 @@ class Redis
|
|
|
1069
1076
|
# Append one or more values to a list, creating the list if it doesn't exist
|
|
1070
1077
|
#
|
|
1071
1078
|
# @param [String] key
|
|
1072
|
-
# @param [String] value
|
|
1079
|
+
# @param [String, Array<String>] value string value, or array of string values to push
|
|
1073
1080
|
# @return [Fixnum] the length of the list after the push operation
|
|
1074
1081
|
def rpush(key, value)
|
|
1075
1082
|
synchronize do |client|
|
|
@@ -1707,6 +1714,30 @@ class Redis
|
|
|
1707
1714
|
end
|
|
1708
1715
|
end
|
|
1709
1716
|
|
|
1717
|
+
# Count the members, with the same score in a sorted set, within the given lexicographical range.
|
|
1718
|
+
#
|
|
1719
|
+
# @example Count members matching a
|
|
1720
|
+
# redis.zlexcount("zset", "[a", "[a\xff")
|
|
1721
|
+
# # => 1
|
|
1722
|
+
# @example Count members matching a-z
|
|
1723
|
+
# redis.zlexcount("zset", "[a", "[z\xff")
|
|
1724
|
+
# # => 26
|
|
1725
|
+
#
|
|
1726
|
+
# @param [String] key
|
|
1727
|
+
# @param [String] min
|
|
1728
|
+
# - inclusive minimum is specified by prefixing `(`
|
|
1729
|
+
# - exclusive minimum is specified by prefixing `[`
|
|
1730
|
+
# @param [String] max
|
|
1731
|
+
# - inclusive maximum is specified by prefixing `(`
|
|
1732
|
+
# - exclusive maximum is specified by prefixing `[`
|
|
1733
|
+
#
|
|
1734
|
+
# @return [Fixnum] number of members within the specified lexicographical range
|
|
1735
|
+
def zlexcount(key, min, max)
|
|
1736
|
+
synchronize do |client|
|
|
1737
|
+
client.call([:zlexcount, key, min, max])
|
|
1738
|
+
end
|
|
1739
|
+
end
|
|
1740
|
+
|
|
1710
1741
|
# Return a range of members with the same score in a sorted set, by lexicographical ordering
|
|
1711
1742
|
#
|
|
1712
1743
|
# @example Retrieve members matching a
|
data/lib/redis/client.rb
CHANGED
|
@@ -21,9 +21,7 @@ class Redis
|
|
|
21
21
|
:inherit_socket => false
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
Marshal.load(Marshal.dump(@options))
|
|
26
|
-
end
|
|
24
|
+
attr_reader :options
|
|
27
25
|
|
|
28
26
|
def scheme
|
|
29
27
|
@options[:scheme]
|
|
@@ -340,6 +338,7 @@ class Redis
|
|
|
340
338
|
Errno::EHOSTDOWN,
|
|
341
339
|
Errno::EHOSTUNREACH,
|
|
342
340
|
Errno::ENETUNREACH,
|
|
341
|
+
Errno::ENOENT,
|
|
343
342
|
Errno::ETIMEDOUT
|
|
344
343
|
|
|
345
344
|
raise CannotConnectError, "Error connecting to Redis on #{location} (#{$!.class})"
|
|
@@ -451,12 +450,12 @@ class Redis
|
|
|
451
450
|
case options[:tcp_keepalive]
|
|
452
451
|
when Hash
|
|
453
452
|
[:time, :intvl, :probes].each do |key|
|
|
454
|
-
unless options[:tcp_keepalive][key].is_a?(
|
|
455
|
-
raise "Expected the #{key.inspect} key in :tcp_keepalive to be
|
|
453
|
+
unless options[:tcp_keepalive][key].is_a?(Integer)
|
|
454
|
+
raise "Expected the #{key.inspect} key in :tcp_keepalive to be an Integer"
|
|
456
455
|
end
|
|
457
456
|
end
|
|
458
457
|
|
|
459
|
-
when
|
|
458
|
+
when Integer
|
|
460
459
|
if options[:tcp_keepalive] >= 60
|
|
461
460
|
options[:tcp_keepalive] = {:time => options[:tcp_keepalive] - 20, :intvl => 10, :probes => 2}
|
|
462
461
|
|
|
@@ -72,7 +72,15 @@ class Redis
|
|
|
72
72
|
|
|
73
73
|
def self.connect(config)
|
|
74
74
|
if config[:scheme] == "unix"
|
|
75
|
-
|
|
75
|
+
begin
|
|
76
|
+
conn = EventMachine.connect_unix_domain(config[:path], RedisClient)
|
|
77
|
+
rescue RuntimeError => e
|
|
78
|
+
if e.message == "no connection"
|
|
79
|
+
raise Errno::ECONNREFUSED
|
|
80
|
+
else
|
|
81
|
+
raise e
|
|
82
|
+
end
|
|
83
|
+
end
|
|
76
84
|
elsif config[:scheme] == "rediss" || config[:ssl]
|
|
77
85
|
raise NotImplementedError, "SSL not supported by synchrony driver"
|
|
78
86
|
else
|
|
@@ -0,0 +1,873 @@
|
|
|
1
|
+
require_relative "hash_ring"
|
|
2
|
+
|
|
3
|
+
class Redis
|
|
4
|
+
class Distributed
|
|
5
|
+
|
|
6
|
+
class CannotDistribute < RuntimeError
|
|
7
|
+
def initialize(command)
|
|
8
|
+
@command = command
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def message
|
|
12
|
+
"#{@command.to_s.upcase} cannot be used in Redis::Distributed because the keys involved need to be on the same server or because we cannot guarantee that the operation will be atomic."
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
attr_reader :ring
|
|
17
|
+
|
|
18
|
+
def initialize(node_configs, options = {})
|
|
19
|
+
@tag = options[:tag] || /^\{(.+?)\}/
|
|
20
|
+
@ring = options[:ring] || HashRing.new
|
|
21
|
+
@node_configs = node_configs.dup
|
|
22
|
+
@default_options = options.dup
|
|
23
|
+
node_configs.each { |node_config| add_node(node_config) }
|
|
24
|
+
@subscribed_node = nil
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def node_for(key)
|
|
28
|
+
@ring.get_node(key_tag(key.to_s) || key.to_s)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def nodes
|
|
32
|
+
@ring.nodes
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def add_node(options)
|
|
36
|
+
options = { :url => options } if options.is_a?(String)
|
|
37
|
+
options = @default_options.merge(options)
|
|
38
|
+
@ring.add_node Redis.new( options )
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Change the selected database for the current connection.
|
|
42
|
+
def select(db)
|
|
43
|
+
on_each_node :select, db
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Ping the server.
|
|
47
|
+
def ping
|
|
48
|
+
on_each_node :ping
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Echo the given string.
|
|
52
|
+
def echo(value)
|
|
53
|
+
on_each_node :echo, value
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Close the connection.
|
|
57
|
+
def quit
|
|
58
|
+
on_each_node :quit
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Asynchronously save the dataset to disk.
|
|
62
|
+
def bgsave
|
|
63
|
+
on_each_node :bgsave
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Return the number of keys in the selected database.
|
|
67
|
+
def dbsize
|
|
68
|
+
on_each_node :dbsize
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Remove all keys from all databases.
|
|
72
|
+
def flushall
|
|
73
|
+
on_each_node :flushall
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Remove all keys from the current database.
|
|
77
|
+
def flushdb
|
|
78
|
+
on_each_node :flushdb
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Get information and statistics about the server.
|
|
82
|
+
def info(cmd = nil)
|
|
83
|
+
on_each_node :info, cmd
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Get the UNIX time stamp of the last successful save to disk.
|
|
87
|
+
def lastsave
|
|
88
|
+
on_each_node :lastsave
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Listen for all requests received by the server in real time.
|
|
92
|
+
def monitor
|
|
93
|
+
raise NotImplementedError
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Synchronously save the dataset to disk.
|
|
97
|
+
def save
|
|
98
|
+
on_each_node :save
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Get server time: an UNIX timestamp and the elapsed microseconds in the current second.
|
|
102
|
+
def time
|
|
103
|
+
on_each_node :time
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Remove the expiration from a key.
|
|
107
|
+
def persist(key)
|
|
108
|
+
node_for(key).persist(key)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Set a key's time to live in seconds.
|
|
112
|
+
def expire(key, seconds)
|
|
113
|
+
node_for(key).expire(key, seconds)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Set the expiration for a key as a UNIX timestamp.
|
|
117
|
+
def expireat(key, unix_time)
|
|
118
|
+
node_for(key).expireat(key, unix_time)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Get the time to live (in seconds) for a key.
|
|
122
|
+
def ttl(key)
|
|
123
|
+
node_for(key).ttl(key)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Set a key's time to live in milliseconds.
|
|
127
|
+
def pexpire(key, milliseconds)
|
|
128
|
+
node_for(key).pexpire(key, milliseconds)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Set the expiration for a key as number of milliseconds from UNIX Epoch.
|
|
132
|
+
def pexpireat(key, ms_unix_time)
|
|
133
|
+
node_for(key).pexpireat(key, ms_unix_time)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Get the time to live (in milliseconds) for a key.
|
|
137
|
+
def pttl(key)
|
|
138
|
+
node_for(key).pttl(key)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Return a serialized version of the value stored at a key.
|
|
142
|
+
def dump(key)
|
|
143
|
+
node_for(key).dump(key)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Create a key using the serialized value, previously obtained using DUMP.
|
|
147
|
+
def restore(key, ttl, serialized_value, options = {})
|
|
148
|
+
node_for(key).restore(key, ttl, serialized_value, options)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Transfer a key from the connected instance to another instance.
|
|
152
|
+
def migrate(key, options)
|
|
153
|
+
raise CannotDistribute, :migrate
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Delete a key.
|
|
157
|
+
def del(*args)
|
|
158
|
+
keys_per_node = args.group_by { |key| node_for(key) }
|
|
159
|
+
keys_per_node.inject(0) do |sum, (node, keys)|
|
|
160
|
+
sum + node.del(*keys)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Determine if a key exists.
|
|
165
|
+
def exists(key)
|
|
166
|
+
node_for(key).exists(key)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Find all keys matching the given pattern.
|
|
170
|
+
def keys(glob = "*")
|
|
171
|
+
on_each_node(:keys, glob).flatten
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Move a key to another database.
|
|
175
|
+
def move(key, db)
|
|
176
|
+
node_for(key).move(key, db)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Return a random key from the keyspace.
|
|
180
|
+
def randomkey
|
|
181
|
+
raise CannotDistribute, :randomkey
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Rename a key.
|
|
185
|
+
def rename(old_name, new_name)
|
|
186
|
+
ensure_same_node(:rename, [old_name, new_name]) do |node|
|
|
187
|
+
node.rename(old_name, new_name)
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Rename a key, only if the new key does not exist.
|
|
192
|
+
def renamenx(old_name, new_name)
|
|
193
|
+
ensure_same_node(:renamenx, [old_name, new_name]) do |node|
|
|
194
|
+
node.renamenx(old_name, new_name)
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Sort the elements in a list, set or sorted set.
|
|
199
|
+
def sort(key, options = {})
|
|
200
|
+
keys = [key, options[:by], options[:store], *Array(options[:get])].compact
|
|
201
|
+
|
|
202
|
+
ensure_same_node(:sort, keys) do |node|
|
|
203
|
+
node.sort(key, options)
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Determine the type stored at key.
|
|
208
|
+
def type(key)
|
|
209
|
+
node_for(key).type(key)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Decrement the integer value of a key by one.
|
|
213
|
+
def decr(key)
|
|
214
|
+
node_for(key).decr(key)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Decrement the integer value of a key by the given number.
|
|
218
|
+
def decrby(key, decrement)
|
|
219
|
+
node_for(key).decrby(key, decrement)
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Increment the integer value of a key by one.
|
|
223
|
+
def incr(key)
|
|
224
|
+
node_for(key).incr(key)
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# Increment the integer value of a key by the given integer number.
|
|
228
|
+
def incrby(key, increment)
|
|
229
|
+
node_for(key).incrby(key, increment)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Increment the numeric value of a key by the given float number.
|
|
233
|
+
def incrbyfloat(key, increment)
|
|
234
|
+
node_for(key).incrbyfloat(key, increment)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Set the string value of a key.
|
|
238
|
+
def set(key, value, options = {})
|
|
239
|
+
node_for(key).set(key, value, options)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Set the time to live in seconds of a key.
|
|
243
|
+
def setex(key, ttl, value)
|
|
244
|
+
node_for(key).setex(key, ttl, value)
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# Set the time to live in milliseconds of a key.
|
|
248
|
+
def psetex(key, ttl, value)
|
|
249
|
+
node_for(key).psetex(key, ttl, value)
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# Set the value of a key, only if the key does not exist.
|
|
253
|
+
def setnx(key, value)
|
|
254
|
+
node_for(key).setnx(key, value)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# Set multiple keys to multiple values.
|
|
258
|
+
def mset(*args)
|
|
259
|
+
raise CannotDistribute, :mset
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def mapped_mset(hash)
|
|
263
|
+
raise CannotDistribute, :mapped_mset
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# Set multiple keys to multiple values, only if none of the keys exist.
|
|
267
|
+
def msetnx(*args)
|
|
268
|
+
raise CannotDistribute, :msetnx
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def mapped_msetnx(hash)
|
|
272
|
+
raise CannotDistribute, :mapped_msetnx
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# Get the value of a key.
|
|
276
|
+
def get(key)
|
|
277
|
+
node_for(key).get(key)
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
# Get the values of all the given keys.
|
|
281
|
+
def mget(*keys)
|
|
282
|
+
raise CannotDistribute, :mget
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def mapped_mget(*keys)
|
|
286
|
+
raise CannotDistribute, :mapped_mget
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# Overwrite part of a string at key starting at the specified offset.
|
|
290
|
+
def setrange(key, offset, value)
|
|
291
|
+
node_for(key).setrange(key, offset, value)
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# Get a substring of the string stored at a key.
|
|
295
|
+
def getrange(key, start, stop)
|
|
296
|
+
node_for(key).getrange(key, start, stop)
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# Sets or clears the bit at offset in the string value stored at key.
|
|
300
|
+
def setbit(key, offset, value)
|
|
301
|
+
node_for(key).setbit(key, offset, value)
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
# Returns the bit value at offset in the string value stored at key.
|
|
305
|
+
def getbit(key, offset)
|
|
306
|
+
node_for(key).getbit(key, offset)
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# Append a value to a key.
|
|
310
|
+
def append(key, value)
|
|
311
|
+
node_for(key).append(key, value)
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# Count the number of set bits in a range of the string value stored at key.
|
|
315
|
+
def bitcount(key, start = 0, stop = -1)
|
|
316
|
+
node_for(key).bitcount(key, start, stop)
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
# Perform a bitwise operation between strings and store the resulting string in a key.
|
|
320
|
+
def bitop(operation, destkey, *keys)
|
|
321
|
+
ensure_same_node(:bitop, [destkey] + keys) do |node|
|
|
322
|
+
node.bitop(operation, destkey, *keys)
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
# Return the position of the first bit set to 1 or 0 in a string.
|
|
327
|
+
def bitpos(key, bit, start=nil, stop=nil)
|
|
328
|
+
node_for(key).bitpos(key, bit, start, stop)
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
# Set the string value of a key and return its old value.
|
|
332
|
+
def getset(key, value)
|
|
333
|
+
node_for(key).getset(key, value)
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
# Get the length of the value stored in a key.
|
|
337
|
+
def strlen(key)
|
|
338
|
+
node_for(key).strlen(key)
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
def [](key)
|
|
342
|
+
get(key)
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
def []=(key,value)
|
|
346
|
+
set(key, value)
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
# Get the length of a list.
|
|
350
|
+
def llen(key)
|
|
351
|
+
node_for(key).llen(key)
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
# Prepend one or more values to a list.
|
|
355
|
+
def lpush(key, value)
|
|
356
|
+
node_for(key).lpush(key, value)
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
# Prepend a value to a list, only if the list exists.
|
|
360
|
+
def lpushx(key, value)
|
|
361
|
+
node_for(key).lpushx(key, value)
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
# Append one or more values to a list.
|
|
365
|
+
def rpush(key, value)
|
|
366
|
+
node_for(key).rpush(key, value)
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
# Append a value to a list, only if the list exists.
|
|
370
|
+
def rpushx(key, value)
|
|
371
|
+
node_for(key).rpushx(key, value)
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
# Remove and get the first element in a list.
|
|
375
|
+
def lpop(key)
|
|
376
|
+
node_for(key).lpop(key)
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
# Remove and get the last element in a list.
|
|
380
|
+
def rpop(key)
|
|
381
|
+
node_for(key).rpop(key)
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
# Remove the last element in a list, append it to another list and return
|
|
385
|
+
# it.
|
|
386
|
+
def rpoplpush(source, destination)
|
|
387
|
+
ensure_same_node(:rpoplpush, [source, destination]) do |node|
|
|
388
|
+
node.rpoplpush(source, destination)
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def _bpop(cmd, args)
|
|
393
|
+
options = {}
|
|
394
|
+
|
|
395
|
+
case args.last
|
|
396
|
+
when Hash
|
|
397
|
+
options = args.pop
|
|
398
|
+
when Integer
|
|
399
|
+
# Issue deprecation notice in obnoxious mode...
|
|
400
|
+
options[:timeout] = args.pop
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
if args.size > 1
|
|
404
|
+
# Issue deprecation notice in obnoxious mode...
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
keys = args.flatten
|
|
408
|
+
|
|
409
|
+
ensure_same_node(cmd, keys) do |node|
|
|
410
|
+
node.__send__(cmd, keys, options)
|
|
411
|
+
end
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
# Remove and get the first element in a list, or block until one is
|
|
415
|
+
# available.
|
|
416
|
+
def blpop(*args)
|
|
417
|
+
_bpop(:blpop, args)
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
# Remove and get the last element in a list, or block until one is
|
|
421
|
+
# available.
|
|
422
|
+
def brpop(*args)
|
|
423
|
+
_bpop(:brpop, args)
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
# Pop a value from a list, push it to another list and return it; or block
|
|
427
|
+
# until one is available.
|
|
428
|
+
def brpoplpush(source, destination, options = {})
|
|
429
|
+
case options
|
|
430
|
+
when Integer
|
|
431
|
+
# Issue deprecation notice in obnoxious mode...
|
|
432
|
+
options = { :timeout => options }
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
ensure_same_node(:brpoplpush, [source, destination]) do |node|
|
|
436
|
+
node.brpoplpush(source, destination, options)
|
|
437
|
+
end
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
# Get an element from a list by its index.
|
|
441
|
+
def lindex(key, index)
|
|
442
|
+
node_for(key).lindex(key, index)
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
# Insert an element before or after another element in a list.
|
|
446
|
+
def linsert(key, where, pivot, value)
|
|
447
|
+
node_for(key).linsert(key, where, pivot, value)
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
# Get a range of elements from a list.
|
|
451
|
+
def lrange(key, start, stop)
|
|
452
|
+
node_for(key).lrange(key, start, stop)
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
# Remove elements from a list.
|
|
456
|
+
def lrem(key, count, value)
|
|
457
|
+
node_for(key).lrem(key, count, value)
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
# Set the value of an element in a list by its index.
|
|
461
|
+
def lset(key, index, value)
|
|
462
|
+
node_for(key).lset(key, index, value)
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
# Trim a list to the specified range.
|
|
466
|
+
def ltrim(key, start, stop)
|
|
467
|
+
node_for(key).ltrim(key, start, stop)
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
# Get the number of members in a set.
|
|
471
|
+
def scard(key)
|
|
472
|
+
node_for(key).scard(key)
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
# Add one or more members to a set.
|
|
476
|
+
def sadd(key, member)
|
|
477
|
+
node_for(key).sadd(key, member)
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
# Remove one or more members from a set.
|
|
481
|
+
def srem(key, member)
|
|
482
|
+
node_for(key).srem(key, member)
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
# Remove and return a random member from a set.
|
|
486
|
+
def spop(key, count = nil)
|
|
487
|
+
node_for(key).spop(key, count)
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
# Get a random member from a set.
|
|
491
|
+
def srandmember(key, count = nil)
|
|
492
|
+
node_for(key).srandmember(key, count)
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
# Move a member from one set to another.
|
|
496
|
+
def smove(source, destination, member)
|
|
497
|
+
ensure_same_node(:smove, [source, destination]) do |node|
|
|
498
|
+
node.smove(source, destination, member)
|
|
499
|
+
end
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
# Determine if a given value is a member of a set.
|
|
503
|
+
def sismember(key, member)
|
|
504
|
+
node_for(key).sismember(key, member)
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
# Get all the members in a set.
|
|
508
|
+
def smembers(key)
|
|
509
|
+
node_for(key).smembers(key)
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
# Subtract multiple sets.
|
|
513
|
+
def sdiff(*keys)
|
|
514
|
+
ensure_same_node(:sdiff, keys) do |node|
|
|
515
|
+
node.sdiff(*keys)
|
|
516
|
+
end
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
# Subtract multiple sets and store the resulting set in a key.
|
|
520
|
+
def sdiffstore(destination, *keys)
|
|
521
|
+
ensure_same_node(:sdiffstore, [destination] + keys) do |node|
|
|
522
|
+
node.sdiffstore(destination, *keys)
|
|
523
|
+
end
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
# Intersect multiple sets.
|
|
527
|
+
def sinter(*keys)
|
|
528
|
+
ensure_same_node(:sinter, keys) do |node|
|
|
529
|
+
node.sinter(*keys)
|
|
530
|
+
end
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
# Intersect multiple sets and store the resulting set in a key.
|
|
534
|
+
def sinterstore(destination, *keys)
|
|
535
|
+
ensure_same_node(:sinterstore, [destination] + keys) do |node|
|
|
536
|
+
node.sinterstore(destination, *keys)
|
|
537
|
+
end
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
# Add multiple sets.
|
|
541
|
+
def sunion(*keys)
|
|
542
|
+
ensure_same_node(:sunion, keys) do |node|
|
|
543
|
+
node.sunion(*keys)
|
|
544
|
+
end
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
# Add multiple sets and store the resulting set in a key.
|
|
548
|
+
def sunionstore(destination, *keys)
|
|
549
|
+
ensure_same_node(:sunionstore, [destination] + keys) do |node|
|
|
550
|
+
node.sunionstore(destination, *keys)
|
|
551
|
+
end
|
|
552
|
+
end
|
|
553
|
+
|
|
554
|
+
# Get the number of members in a sorted set.
|
|
555
|
+
def zcard(key)
|
|
556
|
+
node_for(key).zcard(key)
|
|
557
|
+
end
|
|
558
|
+
|
|
559
|
+
# Add one or more members to a sorted set, or update the score for members
|
|
560
|
+
# that already exist.
|
|
561
|
+
def zadd(key, *args)
|
|
562
|
+
node_for(key).zadd(key, *args)
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
# Increment the score of a member in a sorted set.
|
|
566
|
+
def zincrby(key, increment, member)
|
|
567
|
+
node_for(key).zincrby(key, increment, member)
|
|
568
|
+
end
|
|
569
|
+
|
|
570
|
+
# Remove one or more members from a sorted set.
|
|
571
|
+
def zrem(key, member)
|
|
572
|
+
node_for(key).zrem(key, member)
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
# Get the score associated with the given member in a sorted set.
|
|
576
|
+
def zscore(key, member)
|
|
577
|
+
node_for(key).zscore(key, member)
|
|
578
|
+
end
|
|
579
|
+
|
|
580
|
+
# Return a range of members in a sorted set, by index.
|
|
581
|
+
def zrange(key, start, stop, options = {})
|
|
582
|
+
node_for(key).zrange(key, start, stop, options)
|
|
583
|
+
end
|
|
584
|
+
|
|
585
|
+
# Return a range of members in a sorted set, by index, with scores ordered
|
|
586
|
+
# from high to low.
|
|
587
|
+
def zrevrange(key, start, stop, options = {})
|
|
588
|
+
node_for(key).zrevrange(key, start, stop, options)
|
|
589
|
+
end
|
|
590
|
+
|
|
591
|
+
# Determine the index of a member in a sorted set.
|
|
592
|
+
def zrank(key, member)
|
|
593
|
+
node_for(key).zrank(key, member)
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
# Determine the index of a member in a sorted set, with scores ordered from
|
|
597
|
+
# high to low.
|
|
598
|
+
def zrevrank(key, member)
|
|
599
|
+
node_for(key).zrevrank(key, member)
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
# Remove all members in a sorted set within the given indexes.
|
|
603
|
+
def zremrangebyrank(key, start, stop)
|
|
604
|
+
node_for(key).zremrangebyrank(key, start, stop)
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
# Return a range of members in a sorted set, by score.
|
|
608
|
+
def zrangebyscore(key, min, max, options = {})
|
|
609
|
+
node_for(key).zrangebyscore(key, min, max, options)
|
|
610
|
+
end
|
|
611
|
+
|
|
612
|
+
# Return a range of members in a sorted set, by score, with scores ordered
|
|
613
|
+
# from high to low.
|
|
614
|
+
def zrevrangebyscore(key, max, min, options = {})
|
|
615
|
+
node_for(key).zrevrangebyscore(key, max, min, options)
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
# Remove all members in a sorted set within the given scores.
|
|
619
|
+
def zremrangebyscore(key, min, max)
|
|
620
|
+
node_for(key).zremrangebyscore(key, min, max)
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
# Get the number of members in a particular score range.
|
|
624
|
+
def zcount(key, min, max)
|
|
625
|
+
node_for(key).zcount(key, min, max)
|
|
626
|
+
end
|
|
627
|
+
|
|
628
|
+
# Intersect multiple sorted sets and store the resulting sorted set in a new
|
|
629
|
+
# key.
|
|
630
|
+
def zinterstore(destination, keys, options = {})
|
|
631
|
+
ensure_same_node(:zinterstore, [destination] + keys) do |node|
|
|
632
|
+
node.zinterstore(destination, keys, options)
|
|
633
|
+
end
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
# Add multiple sorted sets and store the resulting sorted set in a new key.
|
|
637
|
+
def zunionstore(destination, keys, options = {})
|
|
638
|
+
ensure_same_node(:zunionstore, [destination] + keys) do |node|
|
|
639
|
+
node.zunionstore(destination, keys, options)
|
|
640
|
+
end
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
# Get the number of fields in a hash.
|
|
644
|
+
def hlen(key)
|
|
645
|
+
node_for(key).hlen(key)
|
|
646
|
+
end
|
|
647
|
+
|
|
648
|
+
# Set the string value of a hash field.
|
|
649
|
+
def hset(key, field, value)
|
|
650
|
+
node_for(key).hset(key, field, value)
|
|
651
|
+
end
|
|
652
|
+
|
|
653
|
+
# Set the value of a hash field, only if the field does not exist.
|
|
654
|
+
def hsetnx(key, field, value)
|
|
655
|
+
node_for(key).hsetnx(key, field, value)
|
|
656
|
+
end
|
|
657
|
+
|
|
658
|
+
# Set multiple hash fields to multiple values.
|
|
659
|
+
def hmset(key, *attrs)
|
|
660
|
+
node_for(key).hmset(key, *attrs)
|
|
661
|
+
end
|
|
662
|
+
|
|
663
|
+
def mapped_hmset(key, hash)
|
|
664
|
+
node_for(key).hmset(key, *hash.to_a.flatten)
|
|
665
|
+
end
|
|
666
|
+
|
|
667
|
+
# Get the value of a hash field.
|
|
668
|
+
def hget(key, field)
|
|
669
|
+
node_for(key).hget(key, field)
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
# Get the values of all the given hash fields.
|
|
673
|
+
def hmget(key, *fields)
|
|
674
|
+
node_for(key).hmget(key, *fields)
|
|
675
|
+
end
|
|
676
|
+
|
|
677
|
+
def mapped_hmget(key, *fields)
|
|
678
|
+
Hash[*fields.zip(hmget(key, *fields)).flatten]
|
|
679
|
+
end
|
|
680
|
+
|
|
681
|
+
# Delete one or more hash fields.
|
|
682
|
+
def hdel(key, field)
|
|
683
|
+
node_for(key).hdel(key, field)
|
|
684
|
+
end
|
|
685
|
+
|
|
686
|
+
# Determine if a hash field exists.
|
|
687
|
+
def hexists(key, field)
|
|
688
|
+
node_for(key).hexists(key, field)
|
|
689
|
+
end
|
|
690
|
+
|
|
691
|
+
# Increment the integer value of a hash field by the given integer number.
|
|
692
|
+
def hincrby(key, field, increment)
|
|
693
|
+
node_for(key).hincrby(key, field, increment)
|
|
694
|
+
end
|
|
695
|
+
|
|
696
|
+
# Increment the numeric value of a hash field by the given float number.
|
|
697
|
+
def hincrbyfloat(key, field, increment)
|
|
698
|
+
node_for(key).hincrbyfloat(key, field, increment)
|
|
699
|
+
end
|
|
700
|
+
|
|
701
|
+
# Get all the fields in a hash.
|
|
702
|
+
def hkeys(key)
|
|
703
|
+
node_for(key).hkeys(key)
|
|
704
|
+
end
|
|
705
|
+
|
|
706
|
+
# Get all the values in a hash.
|
|
707
|
+
def hvals(key)
|
|
708
|
+
node_for(key).hvals(key)
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
# Get all the fields and values in a hash.
|
|
712
|
+
def hgetall(key)
|
|
713
|
+
node_for(key).hgetall(key)
|
|
714
|
+
end
|
|
715
|
+
|
|
716
|
+
# Post a message to a channel.
|
|
717
|
+
def publish(channel, message)
|
|
718
|
+
node_for(channel).publish(channel, message)
|
|
719
|
+
end
|
|
720
|
+
|
|
721
|
+
def subscribed?
|
|
722
|
+
!! @subscribed_node
|
|
723
|
+
end
|
|
724
|
+
|
|
725
|
+
# Listen for messages published to the given channels.
|
|
726
|
+
def subscribe(channel, *channels, &block)
|
|
727
|
+
if channels.empty?
|
|
728
|
+
@subscribed_node = node_for(channel)
|
|
729
|
+
@subscribed_node.subscribe(channel, &block)
|
|
730
|
+
else
|
|
731
|
+
ensure_same_node(:subscribe, [channel] + channels) do |node|
|
|
732
|
+
@subscribed_node = node
|
|
733
|
+
node.subscribe(channel, *channels, &block)
|
|
734
|
+
end
|
|
735
|
+
end
|
|
736
|
+
end
|
|
737
|
+
|
|
738
|
+
# Stop listening for messages posted to the given channels.
|
|
739
|
+
def unsubscribe(*channels)
|
|
740
|
+
raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
|
|
741
|
+
@subscribed_node.unsubscribe(*channels)
|
|
742
|
+
end
|
|
743
|
+
|
|
744
|
+
# Listen for messages published to channels matching the given patterns.
|
|
745
|
+
def psubscribe(*channels, &block)
|
|
746
|
+
raise NotImplementedError
|
|
747
|
+
end
|
|
748
|
+
|
|
749
|
+
# Stop listening for messages posted to channels matching the given
|
|
750
|
+
# patterns.
|
|
751
|
+
def punsubscribe(*channels)
|
|
752
|
+
raise NotImplementedError
|
|
753
|
+
end
|
|
754
|
+
|
|
755
|
+
# Watch the given keys to determine execution of the MULTI/EXEC block.
|
|
756
|
+
def watch(*keys)
|
|
757
|
+
raise CannotDistribute, :watch
|
|
758
|
+
end
|
|
759
|
+
|
|
760
|
+
# Forget about all watched keys.
|
|
761
|
+
def unwatch
|
|
762
|
+
raise CannotDistribute, :unwatch
|
|
763
|
+
end
|
|
764
|
+
|
|
765
|
+
def pipelined
|
|
766
|
+
raise CannotDistribute, :pipelined
|
|
767
|
+
end
|
|
768
|
+
|
|
769
|
+
# Mark the start of a transaction block.
|
|
770
|
+
def multi
|
|
771
|
+
raise CannotDistribute, :multi
|
|
772
|
+
end
|
|
773
|
+
|
|
774
|
+
# Execute all commands issued after MULTI.
|
|
775
|
+
def exec
|
|
776
|
+
raise CannotDistribute, :exec
|
|
777
|
+
end
|
|
778
|
+
|
|
779
|
+
# Discard all commands issued after MULTI.
|
|
780
|
+
def discard
|
|
781
|
+
raise CannotDistribute, :discard
|
|
782
|
+
end
|
|
783
|
+
|
|
784
|
+
# Control remote script registry.
|
|
785
|
+
def script(subcommand, *args)
|
|
786
|
+
on_each_node(:script, subcommand, *args)
|
|
787
|
+
end
|
|
788
|
+
|
|
789
|
+
# Add one or more members to a HyperLogLog structure.
|
|
790
|
+
def pfadd(key, member)
|
|
791
|
+
node_for(key).pfadd(key, member)
|
|
792
|
+
end
|
|
793
|
+
|
|
794
|
+
# Get the approximate cardinality of members added to HyperLogLog structure.
|
|
795
|
+
def pfcount(*keys)
|
|
796
|
+
ensure_same_node(:pfcount, keys.flatten(1)) do |node|
|
|
797
|
+
node.pfcount(keys)
|
|
798
|
+
end
|
|
799
|
+
end
|
|
800
|
+
|
|
801
|
+
# Merge multiple HyperLogLog values into an unique value that will approximate the cardinality of the union of
|
|
802
|
+
# the observed Sets of the source HyperLogLog structures.
|
|
803
|
+
def pfmerge(dest_key, *source_key)
|
|
804
|
+
ensure_same_node(:pfmerge, [dest_key, *source_key]) do |node|
|
|
805
|
+
node.pfmerge(dest_key, *source_key)
|
|
806
|
+
end
|
|
807
|
+
end
|
|
808
|
+
|
|
809
|
+
def _eval(cmd, args)
|
|
810
|
+
script = args.shift
|
|
811
|
+
options = args.pop if args.last.is_a?(Hash)
|
|
812
|
+
options ||= {}
|
|
813
|
+
|
|
814
|
+
keys = args.shift || options[:keys] || []
|
|
815
|
+
argv = args.shift || options[:argv] || []
|
|
816
|
+
|
|
817
|
+
ensure_same_node(cmd, keys) do |node|
|
|
818
|
+
node.send(cmd, script, keys, argv)
|
|
819
|
+
end
|
|
820
|
+
end
|
|
821
|
+
|
|
822
|
+
# Evaluate Lua script.
|
|
823
|
+
def eval(*args)
|
|
824
|
+
_eval(:eval, args)
|
|
825
|
+
end
|
|
826
|
+
|
|
827
|
+
# Evaluate Lua script by its SHA.
|
|
828
|
+
def evalsha(*args)
|
|
829
|
+
_eval(:evalsha, args)
|
|
830
|
+
end
|
|
831
|
+
|
|
832
|
+
def inspect
|
|
833
|
+
"#<Redis client v#{Redis::VERSION} for #{nodes.map(&:id).join(', ')}>"
|
|
834
|
+
end
|
|
835
|
+
|
|
836
|
+
def dup
|
|
837
|
+
self.class.new(@node_configs, @default_options)
|
|
838
|
+
end
|
|
839
|
+
|
|
840
|
+
protected
|
|
841
|
+
|
|
842
|
+
def on_each_node(command, *args)
|
|
843
|
+
nodes.map do |node|
|
|
844
|
+
node.send(command, *args)
|
|
845
|
+
end
|
|
846
|
+
end
|
|
847
|
+
|
|
848
|
+
def node_index_for(key)
|
|
849
|
+
nodes.index(node_for(key))
|
|
850
|
+
end
|
|
851
|
+
|
|
852
|
+
def key_tag(key)
|
|
853
|
+
key.to_s[@tag, 1] if @tag
|
|
854
|
+
end
|
|
855
|
+
|
|
856
|
+
def ensure_same_node(command, keys)
|
|
857
|
+
all = true
|
|
858
|
+
|
|
859
|
+
tags = keys.map do |key|
|
|
860
|
+
tag = key_tag(key)
|
|
861
|
+
all = false unless tag
|
|
862
|
+
tag
|
|
863
|
+
end
|
|
864
|
+
|
|
865
|
+
if (all && tags.uniq.size != 1) || (!all && keys.uniq.size != 1)
|
|
866
|
+
# Not 1 unique tag or not 1 unique key
|
|
867
|
+
raise CannotDistribute, command
|
|
868
|
+
end
|
|
869
|
+
|
|
870
|
+
yield(node_for(keys.first))
|
|
871
|
+
end
|
|
872
|
+
end
|
|
873
|
+
end
|