redis 2.1.1 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +8 -0
- data/CHANGELOG.md +34 -0
- data/README.md +190 -0
- data/Rakefile +194 -79
- data/benchmarking/logging.rb +62 -0
- data/benchmarking/pipeline.rb +51 -0
- data/benchmarking/speed.rb +21 -0
- data/benchmarking/suite.rb +24 -0
- data/benchmarking/thread_safety.rb +38 -0
- data/benchmarking/worker.rb +71 -0
- data/examples/basic.rb +15 -0
- data/examples/dist_redis.rb +43 -0
- data/examples/incr-decr.rb +17 -0
- data/examples/list.rb +26 -0
- data/examples/pubsub.rb +31 -0
- data/examples/sets.rb +36 -0
- data/examples/unicorn/config.ru +3 -0
- data/examples/unicorn/unicorn.rb +20 -0
- data/lib/redis.rb +612 -156
- data/lib/redis/client.rb +98 -57
- data/lib/redis/connection.rb +9 -134
- data/lib/redis/connection/command_helper.rb +45 -0
- data/lib/redis/connection/hiredis.rb +49 -0
- data/lib/redis/connection/registry.rb +12 -0
- data/lib/redis/connection/ruby.rb +131 -0
- data/lib/redis/connection/synchrony.rb +125 -0
- data/lib/redis/distributed.rb +161 -5
- data/lib/redis/pipeline.rb +6 -0
- data/lib/redis/version.rb +3 -0
- data/redis.gemspec +24 -0
- data/test/commands_on_hashes_test.rb +32 -0
- data/test/commands_on_lists_test.rb +60 -0
- data/test/commands_on_sets_test.rb +78 -0
- data/test/commands_on_sorted_sets_test.rb +109 -0
- data/test/commands_on_strings_test.rb +80 -0
- data/test/commands_on_value_types_test.rb +88 -0
- data/test/connection_handling_test.rb +87 -0
- data/test/db/.gitignore +1 -0
- data/test/distributed_blocking_commands_test.rb +53 -0
- data/test/distributed_commands_on_hashes_test.rb +12 -0
- data/test/distributed_commands_on_lists_test.rb +24 -0
- data/test/distributed_commands_on_sets_test.rb +85 -0
- data/test/distributed_commands_on_strings_test.rb +50 -0
- data/test/distributed_commands_on_value_types_test.rb +73 -0
- data/test/distributed_commands_requiring_clustering_test.rb +148 -0
- data/test/distributed_connection_handling_test.rb +25 -0
- data/test/distributed_internals_test.rb +18 -0
- data/test/distributed_key_tags_test.rb +53 -0
- data/test/distributed_persistence_control_commands_test.rb +24 -0
- data/test/distributed_publish_subscribe_test.rb +101 -0
- data/test/distributed_remote_server_control_commands_test.rb +31 -0
- data/test/distributed_sorting_test.rb +21 -0
- data/test/distributed_test.rb +60 -0
- data/test/distributed_transactions_test.rb +34 -0
- data/test/encoding_test.rb +16 -0
- data/test/error_replies_test.rb +53 -0
- data/test/helper.rb +145 -0
- data/test/internals_test.rb +157 -0
- data/test/lint/hashes.rb +114 -0
- data/test/lint/internals.rb +41 -0
- data/test/lint/lists.rb +93 -0
- data/test/lint/sets.rb +66 -0
- data/test/lint/sorted_sets.rb +167 -0
- data/test/lint/strings.rb +137 -0
- data/test/lint/value_types.rb +84 -0
- data/test/persistence_control_commands_test.rb +22 -0
- data/test/pipelining_commands_test.rb +123 -0
- data/test/publish_subscribe_test.rb +158 -0
- data/test/redis_mock.rb +80 -0
- data/test/remote_server_control_commands_test.rb +63 -0
- data/test/sorting_test.rb +44 -0
- data/test/synchrony_driver.rb +57 -0
- data/test/test.conf +8 -0
- data/test/thread_safety_test.rb +30 -0
- data/test/transactions_test.rb +100 -0
- data/test/unknown_commands_test.rb +14 -0
- data/test/url_param_test.rb +60 -0
- metadata +128 -19
- data/README.markdown +0 -129
@@ -0,0 +1,12 @@
|
|
1
|
+
class Redis
|
2
|
+
module Connection
|
3
|
+
|
4
|
+
# Store a list of loaded connection drivers in the Connection module.
|
5
|
+
# Redis::Client uses the last required driver by default, and will be aware
|
6
|
+
# of the loaded connection drivers if the user chooses to override the
|
7
|
+
# default connection driver.
|
8
|
+
def self.drivers
|
9
|
+
@drivers ||= []
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require "redis/connection/registry"
|
2
|
+
require "redis/connection/command_helper"
|
3
|
+
require "socket"
|
4
|
+
|
5
|
+
class Redis
|
6
|
+
module Connection
|
7
|
+
class Ruby
|
8
|
+
include Redis::Connection::CommandHelper
|
9
|
+
|
10
|
+
MINUS = "-".freeze
|
11
|
+
PLUS = "+".freeze
|
12
|
+
COLON = ":".freeze
|
13
|
+
DOLLAR = "$".freeze
|
14
|
+
ASTERISK = "*".freeze
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
@sock = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def connected?
|
21
|
+
!! @sock
|
22
|
+
end
|
23
|
+
|
24
|
+
def connect(host, port, timeout)
|
25
|
+
with_timeout(timeout.to_f / 1_000_000) do
|
26
|
+
@sock = TCPSocket.new(host, port)
|
27
|
+
@sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def connect_unix(path, timeout)
|
32
|
+
with_timeout(timeout.to_f / 1_000_000) do
|
33
|
+
@sock = UNIXSocket.new(path)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def disconnect
|
38
|
+
@sock.close
|
39
|
+
rescue
|
40
|
+
ensure
|
41
|
+
@sock = nil
|
42
|
+
end
|
43
|
+
|
44
|
+
def timeout=(usecs)
|
45
|
+
secs = Integer(usecs / 1_000_000)
|
46
|
+
usecs = Integer(usecs - (secs * 1_000_000)) # 0 - 999_999
|
47
|
+
|
48
|
+
optval = [secs, usecs].pack("l_2")
|
49
|
+
|
50
|
+
begin
|
51
|
+
@sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval
|
52
|
+
@sock.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval
|
53
|
+
rescue Errno::ENOPROTOOPT
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def write(command)
|
58
|
+
@sock.syswrite(build_command(*command).join(COMMAND_DELIMITER))
|
59
|
+
end
|
60
|
+
|
61
|
+
def read
|
62
|
+
# We read the first byte using read() mainly because gets() is
|
63
|
+
# immune to raw socket timeouts.
|
64
|
+
reply_type = @sock.read(1)
|
65
|
+
|
66
|
+
raise Errno::ECONNRESET unless reply_type
|
67
|
+
|
68
|
+
format_reply(reply_type, @sock.gets)
|
69
|
+
end
|
70
|
+
|
71
|
+
def format_reply(reply_type, line)
|
72
|
+
case reply_type
|
73
|
+
when MINUS then format_error_reply(line)
|
74
|
+
when PLUS then format_status_reply(line)
|
75
|
+
when COLON then format_integer_reply(line)
|
76
|
+
when DOLLAR then format_bulk_reply(line)
|
77
|
+
when ASTERISK then format_multi_bulk_reply(line)
|
78
|
+
else raise ProtocolError.new(reply_type)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def format_error_reply(line)
|
83
|
+
RuntimeError.new(line.strip)
|
84
|
+
end
|
85
|
+
|
86
|
+
def format_status_reply(line)
|
87
|
+
line.strip
|
88
|
+
end
|
89
|
+
|
90
|
+
def format_integer_reply(line)
|
91
|
+
line.to_i
|
92
|
+
end
|
93
|
+
|
94
|
+
def format_bulk_reply(line)
|
95
|
+
bulklen = line.to_i
|
96
|
+
return if bulklen == -1
|
97
|
+
reply = encode(@sock.read(bulklen))
|
98
|
+
@sock.read(2) # Discard CRLF.
|
99
|
+
reply
|
100
|
+
end
|
101
|
+
|
102
|
+
def format_multi_bulk_reply(line)
|
103
|
+
n = line.to_i
|
104
|
+
return if n == -1
|
105
|
+
|
106
|
+
Array.new(n) { read }
|
107
|
+
end
|
108
|
+
|
109
|
+
protected
|
110
|
+
|
111
|
+
begin
|
112
|
+
require "system_timer"
|
113
|
+
|
114
|
+
def with_timeout(seconds, &block)
|
115
|
+
SystemTimer.timeout_after(seconds, &block)
|
116
|
+
end
|
117
|
+
|
118
|
+
rescue LoadError
|
119
|
+
warn "WARNING: using the built-in Timeout class which is known to have issues when used for opening connections. Install the SystemTimer gem if you want to make sure the Redis client will not hang." unless RUBY_VERSION >= "1.9" || RUBY_PLATFORM =~ /java/
|
120
|
+
|
121
|
+
require "timeout"
|
122
|
+
|
123
|
+
def with_timeout(seconds, &block)
|
124
|
+
Timeout.timeout(seconds, &block)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
Redis::Connection.drivers << Redis::Connection::Ruby
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require "redis/connection/command_helper"
|
2
|
+
require "redis/connection/registry"
|
3
|
+
require "em-synchrony"
|
4
|
+
require "hiredis/reader"
|
5
|
+
|
6
|
+
class Redis
|
7
|
+
module Connection
|
8
|
+
class RedisClient < EventMachine::Connection
|
9
|
+
include EventMachine::Deferrable
|
10
|
+
|
11
|
+
def post_init
|
12
|
+
@req = nil
|
13
|
+
@reader = ::Hiredis::Reader.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def connection_completed
|
17
|
+
succeed
|
18
|
+
end
|
19
|
+
|
20
|
+
def receive_data(data)
|
21
|
+
@reader.feed(data)
|
22
|
+
|
23
|
+
begin
|
24
|
+
until (reply = @reader.gets) == false
|
25
|
+
@req.succeed [:reply, reply]
|
26
|
+
end
|
27
|
+
rescue RuntimeError => err
|
28
|
+
@req.fail [:error, ::Redis::ProtocolError.new(err.message)]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def read
|
33
|
+
@req = EventMachine::DefaultDeferrable.new
|
34
|
+
EventMachine::Synchrony.sync @req
|
35
|
+
end
|
36
|
+
|
37
|
+
def send(data)
|
38
|
+
callback { send_data data }
|
39
|
+
end
|
40
|
+
|
41
|
+
def unbind
|
42
|
+
if @req
|
43
|
+
@req.fail [:error, Errno::ECONNRESET]
|
44
|
+
@req = nil
|
45
|
+
else
|
46
|
+
fail
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class Synchrony
|
52
|
+
include Redis::Connection::CommandHelper
|
53
|
+
|
54
|
+
def initialize
|
55
|
+
@timeout = 5_000_000
|
56
|
+
@state = :disconnected
|
57
|
+
@connection = nil
|
58
|
+
end
|
59
|
+
|
60
|
+
def connected?
|
61
|
+
@state == :connected
|
62
|
+
end
|
63
|
+
|
64
|
+
def timeout=(usecs)
|
65
|
+
@timeout = usecs
|
66
|
+
end
|
67
|
+
|
68
|
+
def connect(host, port, timeout)
|
69
|
+
conn = EventMachine.connect(host, port, RedisClient) do |c|
|
70
|
+
c.pending_connect_timeout = [Float(timeout / 1_000_000), 0.1].max
|
71
|
+
end
|
72
|
+
|
73
|
+
setup_connect_callbacks(conn, Fiber.current)
|
74
|
+
end
|
75
|
+
|
76
|
+
def connect_unix(path, timeout)
|
77
|
+
conn = EventMachine.connect_unix_domain(path, RedisClient)
|
78
|
+
setup_connect_callbacks(conn, Fiber.current)
|
79
|
+
end
|
80
|
+
|
81
|
+
def disconnect
|
82
|
+
@state = :disconnected
|
83
|
+
@connection.close_connection
|
84
|
+
@connection = nil
|
85
|
+
end
|
86
|
+
|
87
|
+
def write(command)
|
88
|
+
@connection.send(build_command(*command).join(COMMAND_DELIMITER))
|
89
|
+
end
|
90
|
+
|
91
|
+
def read
|
92
|
+
type, payload = @connection.read
|
93
|
+
|
94
|
+
if type == :reply
|
95
|
+
payload
|
96
|
+
elsif type == :error
|
97
|
+
raise payload
|
98
|
+
else
|
99
|
+
raise "Unknown type #{type.inspect}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def setup_connect_callbacks(conn, f)
|
106
|
+
conn.callback do
|
107
|
+
@connection = conn
|
108
|
+
@state = :connected
|
109
|
+
f.resume conn
|
110
|
+
end
|
111
|
+
|
112
|
+
conn.errback do
|
113
|
+
@connection = conn
|
114
|
+
f.resume :refused
|
115
|
+
end
|
116
|
+
|
117
|
+
r = Fiber.yield
|
118
|
+
raise Errno::ECONNREFUSED if r == :refused
|
119
|
+
r
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
Redis::Connection.drivers << Redis::Connection::Synchrony
|
data/lib/redis/distributed.rb
CHANGED
@@ -23,7 +23,7 @@ class Redis
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def node_for(key)
|
26
|
-
@ring.get_node(key_tag(key) || key)
|
26
|
+
@ring.get_node(key_tag(key.to_s) || key.to_s)
|
27
27
|
end
|
28
28
|
|
29
29
|
def nodes
|
@@ -34,94 +34,139 @@ class Redis
|
|
34
34
|
@ring.add_node Redis.connect(@default_options.merge(:url => url))
|
35
35
|
end
|
36
36
|
|
37
|
+
# Close the connection.
|
37
38
|
def quit
|
38
39
|
on_each_node :quit
|
39
40
|
end
|
40
41
|
|
42
|
+
# Change the selected database for the current connection.
|
41
43
|
def select(db)
|
42
44
|
on_each_node :select, db
|
43
45
|
end
|
44
46
|
|
47
|
+
# Ping the server.
|
45
48
|
def ping
|
46
49
|
on_each_node :ping
|
47
50
|
end
|
48
51
|
|
52
|
+
# Remove all keys from all databases.
|
49
53
|
def flushall
|
50
54
|
on_each_node :flushall
|
51
55
|
end
|
52
56
|
|
57
|
+
# Determine if a key exists.
|
53
58
|
def exists(key)
|
54
59
|
node_for(key).exists(key)
|
55
60
|
end
|
56
61
|
|
57
|
-
|
58
|
-
|
62
|
+
# Delete a key.
|
63
|
+
def del(*args)
|
64
|
+
keys_per_node = args.group_by { |key| node_for(key) }
|
65
|
+
keys_per_node.inject(0) do |sum, (node, keys)|
|
66
|
+
sum + node.del(*keys)
|
67
|
+
end
|
59
68
|
end
|
60
69
|
|
70
|
+
# Determine the type stored at key.
|
61
71
|
def type(key)
|
62
72
|
node_for(key).type(key)
|
63
73
|
end
|
64
74
|
|
75
|
+
# Find all keys matching the given pattern.
|
65
76
|
def keys(glob = "*")
|
66
77
|
on_each_node(:keys, glob).flatten
|
67
78
|
end
|
68
79
|
|
80
|
+
# Return a random key from the keyspace.
|
69
81
|
def randomkey
|
70
82
|
raise CannotDistribute, :randomkey
|
71
83
|
end
|
72
84
|
|
85
|
+
# Rename a key.
|
73
86
|
def rename(old_name, new_name)
|
74
87
|
ensure_same_node(:rename, old_name, new_name) do |node|
|
75
88
|
node.rename(old_name, new_name)
|
76
89
|
end
|
77
90
|
end
|
78
91
|
|
92
|
+
# Rename a key, only if the new key does not exist.
|
79
93
|
def renamenx(old_name, new_name)
|
80
94
|
ensure_same_node(:renamenx, old_name, new_name) do |node|
|
81
95
|
node.renamenx(old_name, new_name)
|
82
96
|
end
|
83
97
|
end
|
84
98
|
|
99
|
+
# Return the number of keys in the selected database.
|
85
100
|
def dbsize
|
86
101
|
on_each_node :dbsize
|
87
102
|
end
|
88
103
|
|
104
|
+
# Set a key's time to live in seconds.
|
89
105
|
def expire(key, seconds)
|
90
106
|
node_for(key).expire(key, seconds)
|
91
107
|
end
|
92
108
|
|
109
|
+
# Set the expiration for a key as a UNIX timestamp.
|
93
110
|
def expireat(key, unix_time)
|
94
111
|
node_for(key).expireat(key, unix_time)
|
95
112
|
end
|
96
113
|
|
114
|
+
# Remove the expiration from a key.
|
97
115
|
def persist(key)
|
98
116
|
node_for(key).persist(key)
|
99
117
|
end
|
100
118
|
|
119
|
+
# Get the time to live for a key.
|
101
120
|
def ttl(key)
|
102
121
|
node_for(key).ttl(key)
|
103
122
|
end
|
104
123
|
|
124
|
+
# Move a key to another database.
|
105
125
|
def move(key, db)
|
106
126
|
node_for(key).move(key, db)
|
107
127
|
end
|
108
128
|
|
129
|
+
# Remove all keys from the current database.
|
109
130
|
def flushdb
|
110
131
|
on_each_node :flushdb
|
111
132
|
end
|
112
133
|
|
134
|
+
# Set the string value of a key.
|
113
135
|
def set(key, value)
|
114
136
|
node_for(key).set(key, value)
|
115
137
|
end
|
116
138
|
|
139
|
+
# Sets or clears the bit at offset in the string value stored at key.
|
140
|
+
def setbit(key, offset, value)
|
141
|
+
node_for(key).setbit(key, offset, value)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Overwrite part of a string at key starting at the specified offset.
|
145
|
+
def setrange(key, offset, value)
|
146
|
+
node_for(key).setrange(key, offset, value)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Set the value and expiration of a key.
|
117
150
|
def setex(key, ttl, value)
|
118
151
|
node_for(key).setex(key, ttl, value)
|
119
152
|
end
|
120
153
|
|
154
|
+
# Get the value of a key.
|
121
155
|
def get(key)
|
122
156
|
node_for(key).get(key)
|
123
157
|
end
|
124
158
|
|
159
|
+
# Returns the bit value at offset in the string value stored at key.
|
160
|
+
def getbit(key, offset)
|
161
|
+
node_for(key).getbit(key, offset)
|
162
|
+
end
|
163
|
+
|
164
|
+
# Get a substring of the string stored at a key.
|
165
|
+
def getrange(key, start, stop)
|
166
|
+
node_for(key).getrange(key, start, stop)
|
167
|
+
end
|
168
|
+
|
169
|
+
# Set the string value of a key and return its old value.
|
125
170
|
def getset(key, value)
|
126
171
|
node_for(key).getset(key, value)
|
127
172
|
end
|
@@ -130,6 +175,7 @@ class Redis
|
|
130
175
|
get(key)
|
131
176
|
end
|
132
177
|
|
178
|
+
# Append a value to a key.
|
133
179
|
def append(key, value)
|
134
180
|
node_for(key).append(key, value)
|
135
181
|
end
|
@@ -142,6 +188,7 @@ class Redis
|
|
142
188
|
set(key, value)
|
143
189
|
end
|
144
190
|
|
191
|
+
# Get the values of all the given keys.
|
145
192
|
def mget(*keys)
|
146
193
|
raise CannotDistribute, :mget
|
147
194
|
end
|
@@ -150,10 +197,12 @@ class Redis
|
|
150
197
|
raise CannotDistribute, :mapped_mget
|
151
198
|
end
|
152
199
|
|
200
|
+
# Set the value of a key, only if the key does not exist.
|
153
201
|
def setnx(key, value)
|
154
202
|
node_for(key).setnx(key, value)
|
155
203
|
end
|
156
204
|
|
205
|
+
# Set multiple keys to multiple values.
|
157
206
|
def mset(*args)
|
158
207
|
raise CannotDistribute, :mset
|
159
208
|
end
|
@@ -162,6 +211,7 @@ class Redis
|
|
162
211
|
mset(*hash.to_a.flatten)
|
163
212
|
end
|
164
213
|
|
214
|
+
# Set multiple keys to multiple values, only if none of the keys exist.
|
165
215
|
def msetnx(*args)
|
166
216
|
raise CannotDistribute, :msetnx
|
167
217
|
end
|
@@ -170,246 +220,335 @@ class Redis
|
|
170
220
|
raise CannotDistribute, :mapped_msetnx
|
171
221
|
end
|
172
222
|
|
223
|
+
# Increment the integer value of a key by one.
|
173
224
|
def incr(key)
|
174
225
|
node_for(key).incr(key)
|
175
226
|
end
|
176
227
|
|
228
|
+
# Increment the integer value of a key by the given number.
|
177
229
|
def incrby(key, increment)
|
178
230
|
node_for(key).incrby(key, increment)
|
179
231
|
end
|
180
232
|
|
233
|
+
# Decrement the integer value of a key by one.
|
181
234
|
def decr(key)
|
182
235
|
node_for(key).decr(key)
|
183
236
|
end
|
184
237
|
|
238
|
+
# Decrement the integer value of a key by the given number.
|
185
239
|
def decrby(key, decrement)
|
186
240
|
node_for(key).decrby(key, decrement)
|
187
241
|
end
|
188
242
|
|
243
|
+
# Append a value to a list.
|
189
244
|
def rpush(key, value)
|
190
245
|
node_for(key).rpush(key, value)
|
191
246
|
end
|
192
247
|
|
248
|
+
# Prepend a value to a list.
|
193
249
|
def lpush(key, value)
|
194
250
|
node_for(key).lpush(key, value)
|
195
251
|
end
|
196
252
|
|
253
|
+
# Get the length of a list.
|
197
254
|
def llen(key)
|
198
255
|
node_for(key).llen(key)
|
199
256
|
end
|
200
257
|
|
258
|
+
# Get a range of elements from a list.
|
201
259
|
def lrange(key, start, stop)
|
202
260
|
node_for(key).lrange(key, start, stop)
|
203
261
|
end
|
204
262
|
|
263
|
+
# Trim a list to the specified range.
|
205
264
|
def ltrim(key, start, stop)
|
206
265
|
node_for(key).ltrim(key, start, stop)
|
207
266
|
end
|
208
267
|
|
268
|
+
# Get an element from a list by its index.
|
209
269
|
def lindex(key, index)
|
210
270
|
node_for(key).lindex(key, index)
|
211
271
|
end
|
212
272
|
|
273
|
+
# Set the value of an element in a list by its index.
|
213
274
|
def lset(key, index, value)
|
214
275
|
node_for(key).lset(key, index, value)
|
215
276
|
end
|
216
277
|
|
278
|
+
# Remove elements from a list.
|
217
279
|
def lrem(key, count, value)
|
218
280
|
node_for(key).lrem(key, count, value)
|
219
281
|
end
|
220
282
|
|
283
|
+
# Remove and get the first element in a list.
|
221
284
|
def lpop(key)
|
222
285
|
node_for(key).lpop(key)
|
223
286
|
end
|
224
287
|
|
288
|
+
# Remove and get the last element in a list.
|
225
289
|
def rpop(key)
|
226
290
|
node_for(key).rpop(key)
|
227
291
|
end
|
228
292
|
|
293
|
+
# Remove the last element in a list, append it to another list and return
|
294
|
+
# it.
|
229
295
|
def rpoplpush(source, destination)
|
230
296
|
ensure_same_node(:rpoplpush, source, destination) do |node|
|
231
297
|
node.rpoplpush(source, destination)
|
232
298
|
end
|
233
299
|
end
|
234
300
|
|
301
|
+
# Remove and get the first element in a list, or block until one is
|
302
|
+
# available.
|
235
303
|
def blpop(key, timeout)
|
236
304
|
node_for(key).blpop(key, timeout)
|
237
305
|
end
|
238
306
|
|
307
|
+
# Remove and get the last element in a list, or block until one is
|
308
|
+
# available.
|
239
309
|
def brpop(key, timeout)
|
240
310
|
node_for(key).brpop(key, timeout)
|
241
311
|
end
|
242
312
|
|
313
|
+
# Pop a value from a list, push it to another list and return it; or block
|
314
|
+
# until one is available.
|
315
|
+
def brpoplpush(source, destination, timeout)
|
316
|
+
ensure_same_node(:brpoplpush, source, destination) do |node|
|
317
|
+
node.brpoplpush(source, destination, timeout)
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
# Add a member to a set.
|
243
322
|
def sadd(key, value)
|
244
323
|
node_for(key).sadd(key, value)
|
245
324
|
end
|
246
325
|
|
326
|
+
# Remove a member from a set.
|
247
327
|
def srem(key, value)
|
248
328
|
node_for(key).srem(key, value)
|
249
329
|
end
|
250
330
|
|
331
|
+
# Remove and return a random member from a set.
|
251
332
|
def spop(key)
|
252
333
|
node_for(key).spop(key)
|
253
334
|
end
|
254
335
|
|
336
|
+
# Move a member from one set to another.
|
255
337
|
def smove(source, destination, member)
|
256
338
|
ensure_same_node(:smove, source, destination) do |node|
|
257
339
|
node.smove(source, destination, member)
|
258
340
|
end
|
259
341
|
end
|
260
342
|
|
343
|
+
# Get the number of members in a set.
|
261
344
|
def scard(key)
|
262
345
|
node_for(key).scard(key)
|
263
346
|
end
|
264
347
|
|
348
|
+
# Determine if a given value is a member of a set.
|
265
349
|
def sismember(key, member)
|
266
350
|
node_for(key).sismember(key, member)
|
267
351
|
end
|
268
352
|
|
353
|
+
# Intersect multiple sets.
|
269
354
|
def sinter(*keys)
|
270
355
|
ensure_same_node(:sinter, *keys) do |node|
|
271
356
|
node.sinter(*keys)
|
272
357
|
end
|
273
358
|
end
|
274
359
|
|
360
|
+
# Intersect multiple sets and store the resulting set in a key.
|
275
361
|
def sinterstore(destination, *keys)
|
276
362
|
ensure_same_node(:sinterstore, destination, *keys) do |node|
|
277
363
|
node.sinterstore(destination, *keys)
|
278
364
|
end
|
279
365
|
end
|
280
366
|
|
367
|
+
# Add multiple sets.
|
281
368
|
def sunion(*keys)
|
282
369
|
ensure_same_node(:sunion, *keys) do |node|
|
283
370
|
node.sunion(*keys)
|
284
371
|
end
|
285
372
|
end
|
286
373
|
|
374
|
+
# Add multiple sets and store the resulting set in a key.
|
287
375
|
def sunionstore(destination, *keys)
|
288
376
|
ensure_same_node(:sunionstore, destination, *keys) do |node|
|
289
377
|
node.sunionstore(destination, *keys)
|
290
378
|
end
|
291
379
|
end
|
292
380
|
|
381
|
+
# Subtract multiple sets.
|
293
382
|
def sdiff(*keys)
|
294
383
|
ensure_same_node(:sdiff, *keys) do |node|
|
295
384
|
node.sdiff(*keys)
|
296
385
|
end
|
297
386
|
end
|
298
387
|
|
388
|
+
# Subtract multiple sets and store the resulting set in a key.
|
299
389
|
def sdiffstore(destination, *keys)
|
300
390
|
ensure_same_node(:sdiffstore, destination, *keys) do |node|
|
301
391
|
node.sdiffstore(destination, *keys)
|
302
392
|
end
|
303
393
|
end
|
304
394
|
|
395
|
+
# Get all the members in a set.
|
305
396
|
def smembers(key)
|
306
397
|
node_for(key).smembers(key)
|
307
398
|
end
|
308
399
|
|
400
|
+
# Get a random member from a set.
|
309
401
|
def srandmember(key)
|
310
402
|
node_for(key).srandmember(key)
|
311
403
|
end
|
312
404
|
|
405
|
+
# Add a member to a sorted set, or update its score if it already exists.
|
313
406
|
def zadd(key, score, member)
|
314
407
|
node_for(key).zadd(key, score, member)
|
315
408
|
end
|
316
409
|
|
410
|
+
# Remove a member from a sorted set.
|
317
411
|
def zrem(key, member)
|
318
412
|
node_for(key).zrem(key, member)
|
319
413
|
end
|
320
414
|
|
415
|
+
# Increment the score of a member in a sorted set.
|
321
416
|
def zincrby(key, increment, member)
|
322
417
|
node_for(key).zincrby(key, increment, member)
|
323
418
|
end
|
324
419
|
|
420
|
+
# Return a range of members in a sorted set, by index.
|
325
421
|
def zrange(key, start, stop, options = {})
|
326
422
|
node_for(key).zrange(key, start, stop, options)
|
327
423
|
end
|
328
424
|
|
425
|
+
# Determine the index of a member in a sorted set.
|
329
426
|
def zrank(key, member)
|
330
427
|
node_for(key).zrank(key, member)
|
331
428
|
end
|
332
429
|
|
430
|
+
# Determine the index of a member in a sorted set, with scores ordered from
|
431
|
+
# high to low.
|
333
432
|
def zrevrank(key, member)
|
334
433
|
node_for(key).zrevrank(key, member)
|
335
434
|
end
|
336
435
|
|
436
|
+
# Return a range of members in a sorted set, by index, with scores ordered
|
437
|
+
# from high to low.
|
337
438
|
def zrevrange(key, start, stop, options = {})
|
338
439
|
node_for(key).zrevrange(key, start, stop, options)
|
339
440
|
end
|
340
441
|
|
442
|
+
# Remove all members in a sorted set within the given scores.
|
341
443
|
def zremrangebyscore(key, min, max)
|
342
444
|
node_for(key).zremrangebyscore(key, min, max)
|
343
445
|
end
|
344
446
|
|
447
|
+
# Remove all members in a sorted set within the given indexes.
|
345
448
|
def zremrangebyrank(key, start, stop)
|
346
449
|
node_for(key).zremrangebyrank(key, start, stop)
|
347
450
|
end
|
348
451
|
|
452
|
+
# Return a range of members in a sorted set, by score.
|
349
453
|
def zrangebyscore(key, min, max, options = {})
|
350
454
|
node_for(key).zrangebyscore(key, min, max, options)
|
351
455
|
end
|
352
456
|
|
457
|
+
# Return a range of members in a sorted set, by score, with scores ordered
|
458
|
+
# from high to low.
|
459
|
+
def zrevrangebyscore(key, max, min, options = {})
|
460
|
+
node_for(key).zrevrangebyscore(key, max, min, options)
|
461
|
+
end
|
462
|
+
|
463
|
+
# Get the number of members in a sorted set.
|
353
464
|
def zcard(key)
|
354
465
|
node_for(key).zcard(key)
|
355
466
|
end
|
356
467
|
|
468
|
+
# Get the score associated with the given member in a sorted set.
|
357
469
|
def zscore(key, member)
|
358
470
|
node_for(key).zscore(key, member)
|
359
471
|
end
|
360
472
|
|
473
|
+
# Intersect multiple sorted sets and store the resulting sorted set in a new
|
474
|
+
# key.
|
361
475
|
def zinterstore(destination, keys, options = {})
|
362
476
|
ensure_same_node(:zinterstore, destination, *keys) do |node|
|
363
477
|
node.zinterstore(destination, keys, options)
|
364
478
|
end
|
365
479
|
end
|
366
480
|
|
481
|
+
# Add multiple sorted sets and store the resulting sorted set in a new key.
|
367
482
|
def zunionstore(destination, keys, options = {})
|
368
483
|
ensure_same_node(:zunionstore, destination, *keys) do |node|
|
369
484
|
node.zunionstore(destination, keys, options)
|
370
485
|
end
|
371
486
|
end
|
372
487
|
|
488
|
+
# Set the string value of a hash field.
|
373
489
|
def hset(key, field, value)
|
374
490
|
node_for(key).hset(key, field, value)
|
375
491
|
end
|
376
492
|
|
493
|
+
# Get the value of a hash field.
|
377
494
|
def hget(key, field)
|
378
495
|
node_for(key).hget(key, field)
|
379
496
|
end
|
380
497
|
|
498
|
+
# Delete a hash field.
|
381
499
|
def hdel(key, field)
|
382
500
|
node_for(key).hdel(key, field)
|
383
501
|
end
|
384
502
|
|
503
|
+
# Determine if a hash field exists.
|
385
504
|
def hexists(key, field)
|
386
505
|
node_for(key).hexists(key, field)
|
387
506
|
end
|
388
507
|
|
508
|
+
# Get the number of fields in a hash.
|
389
509
|
def hlen(key)
|
390
510
|
node_for(key).hlen(key)
|
391
511
|
end
|
392
512
|
|
513
|
+
# Get all the fields in a hash.
|
393
514
|
def hkeys(key)
|
394
515
|
node_for(key).hkeys(key)
|
395
516
|
end
|
396
517
|
|
518
|
+
# Get all the values in a hash.
|
397
519
|
def hvals(key)
|
398
520
|
node_for(key).hvals(key)
|
399
521
|
end
|
400
522
|
|
523
|
+
# Get all the fields and values in a hash.
|
401
524
|
def hgetall(key)
|
402
525
|
node_for(key).hgetall(key)
|
403
526
|
end
|
404
527
|
|
528
|
+
# Set multiple hash fields to multiple values.
|
405
529
|
def hmset(key, *attrs)
|
406
530
|
node_for(key).hmset(key, *attrs)
|
407
531
|
end
|
408
532
|
|
533
|
+
def mapped_hmset(key, hash)
|
534
|
+
node_for(key).hmset(key, *hash.to_a.flatten)
|
535
|
+
end
|
536
|
+
|
537
|
+
# Get the values of all the given hash fields.
|
538
|
+
def hmget(key, *fields)
|
539
|
+
node_for(key).hmget(key, *fields)
|
540
|
+
end
|
541
|
+
|
542
|
+
def mapped_hmget(key, *fields)
|
543
|
+
Hash[*fields.zip(hmget(key, *fields)).flatten]
|
544
|
+
end
|
545
|
+
|
546
|
+
# Increment the integer value of a hash field by the given number.
|
409
547
|
def hincrby(key, field, increment)
|
410
548
|
node_for(key).hincrby(key, field, increment)
|
411
549
|
end
|
412
550
|
|
551
|
+
# Sort the elements in a list, set or sorted set.
|
413
552
|
def sort(key, options = {})
|
414
553
|
keys = [key, options[:by], options[:store], *Array(options[:get])].compact
|
415
554
|
|
@@ -418,26 +557,32 @@ class Redis
|
|
418
557
|
end
|
419
558
|
end
|
420
559
|
|
421
|
-
|
560
|
+
# Mark the start of a transaction block.
|
561
|
+
def multi
|
422
562
|
raise CannotDistribute, :multi
|
423
563
|
end
|
424
564
|
|
565
|
+
# Watch the given keys to determine execution of the MULTI/EXEC block.
|
425
566
|
def watch(*keys)
|
426
567
|
raise CannotDistribute, :watch
|
427
568
|
end
|
428
569
|
|
570
|
+
# Forget about all watched keys.
|
429
571
|
def unwatch
|
430
572
|
raise CannotDistribute, :unwatch
|
431
573
|
end
|
432
574
|
|
575
|
+
# Execute all commands issued after MULTI.
|
433
576
|
def exec
|
434
577
|
raise CannotDistribute, :exec
|
435
578
|
end
|
436
579
|
|
580
|
+
# Discard all commands issued after MULTI.
|
437
581
|
def discard
|
438
582
|
raise CannotDistribute, :discard
|
439
583
|
end
|
440
584
|
|
585
|
+
# Post a message to a channel.
|
441
586
|
def publish(channel, message)
|
442
587
|
node_for(channel).publish(channel, message)
|
443
588
|
end
|
@@ -446,11 +591,13 @@ class Redis
|
|
446
591
|
!! @subscribed_node
|
447
592
|
end
|
448
593
|
|
594
|
+
# Stop listening for messages posted to the given channels.
|
449
595
|
def unsubscribe(*channels)
|
450
596
|
raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
|
451
597
|
@subscribed_node.unsubscribe(*channels)
|
452
598
|
end
|
453
599
|
|
600
|
+
# Listen for messages published to the given channels.
|
454
601
|
def subscribe(channel, *channels, &block)
|
455
602
|
if channels.empty?
|
456
603
|
@subscribed_node = node_for(channel)
|
@@ -463,34 +610,43 @@ class Redis
|
|
463
610
|
end
|
464
611
|
end
|
465
612
|
|
613
|
+
# Stop listening for messages posted to channels matching the given
|
614
|
+
# patterns.
|
466
615
|
def punsubscribe(*channels)
|
467
616
|
raise NotImplementedError
|
468
617
|
end
|
469
618
|
|
619
|
+
# Listen for messages published to channels matching the given patterns.
|
470
620
|
def psubscribe(*channels, &block)
|
471
621
|
raise NotImplementedError
|
472
622
|
end
|
473
623
|
|
624
|
+
# Synchronously save the dataset to disk.
|
474
625
|
def save
|
475
626
|
on_each_node :save
|
476
627
|
end
|
477
628
|
|
629
|
+
# Asynchronously save the dataset to disk.
|
478
630
|
def bgsave
|
479
631
|
on_each_node :bgsave
|
480
632
|
end
|
481
633
|
|
634
|
+
# Get the UNIX time stamp of the last successful save to disk.
|
482
635
|
def lastsave
|
483
636
|
on_each_node :lastsave
|
484
637
|
end
|
485
638
|
|
639
|
+
# Get information and statistics about the server.
|
486
640
|
def info
|
487
641
|
on_each_node :info
|
488
642
|
end
|
489
643
|
|
644
|
+
# Listen for all requests received by the server in real time.
|
490
645
|
def monitor
|
491
646
|
raise NotImplementedError
|
492
647
|
end
|
493
648
|
|
649
|
+
# Echo the given string.
|
494
650
|
def echo(value)
|
495
651
|
on_each_node :echo, value
|
496
652
|
end
|
@@ -512,7 +668,7 @@ class Redis
|
|
512
668
|
end
|
513
669
|
|
514
670
|
def key_tag(key)
|
515
|
-
key[@tag, 1] if @tag
|
671
|
+
key.to_s[@tag, 1] if @tag
|
516
672
|
end
|
517
673
|
|
518
674
|
def ensure_same_node(command, *keys)
|