finsync_redis 3.3.5
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 +7 -0
- data/.gitignore +16 -0
- data/.travis/Gemfile +11 -0
- data/.travis.yml +89 -0
- data/.yardopts +3 -0
- data/CHANGELOG.md +373 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +410 -0
- data/Rakefile +87 -0
- data/benchmarking/logging.rb +71 -0
- data/benchmarking/pipeline.rb +51 -0
- data/benchmarking/speed.rb +21 -0
- data/benchmarking/suite.rb +24 -0
- data/benchmarking/worker.rb +71 -0
- data/examples/basic.rb +15 -0
- data/examples/consistency.rb +114 -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 +37 -0
- data/examples/sentinel/sentinel.conf +9 -0
- data/examples/sentinel/start +49 -0
- data/examples/sentinel.rb +41 -0
- data/examples/sets.rb +36 -0
- data/examples/unicorn/config.ru +3 -0
- data/examples/unicorn/unicorn.rb +20 -0
- data/lib/redis/client.rb +590 -0
- data/lib/redis/connection/command_helper.rb +44 -0
- data/lib/redis/connection/hiredis.rb +66 -0
- data/lib/redis/connection/registry.rb +12 -0
- data/lib/redis/connection/ruby.rb +429 -0
- data/lib/redis/connection/synchrony.rb +133 -0
- data/lib/redis/connection.rb +9 -0
- data/lib/redis/distributed.rb +873 -0
- data/lib/redis/errors.rb +40 -0
- data/lib/redis/hash_ring.rb +132 -0
- data/lib/redis/pipeline.rb +141 -0
- data/lib/redis/subscribe.rb +91 -0
- data/lib/redis/version.rb +3 -0
- data/lib/redis.rb +2788 -0
- data/redis.gemspec +44 -0
- data/test/bitpos_test.rb +69 -0
- data/test/blocking_commands_test.rb +42 -0
- data/test/client_test.rb +59 -0
- data/test/command_map_test.rb +30 -0
- data/test/commands_on_hashes_test.rb +21 -0
- data/test/commands_on_hyper_log_log_test.rb +21 -0
- data/test/commands_on_lists_test.rb +20 -0
- data/test/commands_on_sets_test.rb +77 -0
- data/test/commands_on_sorted_sets_test.rb +137 -0
- data/test/commands_on_strings_test.rb +101 -0
- data/test/commands_on_value_types_test.rb +133 -0
- data/test/connection_handling_test.rb +277 -0
- data/test/connection_test.rb +57 -0
- data/test/db/.gitkeep +0 -0
- data/test/distributed_blocking_commands_test.rb +46 -0
- data/test/distributed_commands_on_hashes_test.rb +10 -0
- data/test/distributed_commands_on_hyper_log_log_test.rb +33 -0
- data/test/distributed_commands_on_lists_test.rb +22 -0
- data/test/distributed_commands_on_sets_test.rb +83 -0
- data/test/distributed_commands_on_sorted_sets_test.rb +18 -0
- data/test/distributed_commands_on_strings_test.rb +59 -0
- data/test/distributed_commands_on_value_types_test.rb +95 -0
- data/test/distributed_commands_requiring_clustering_test.rb +164 -0
- data/test/distributed_connection_handling_test.rb +23 -0
- data/test/distributed_internals_test.rb +79 -0
- data/test/distributed_key_tags_test.rb +52 -0
- data/test/distributed_persistence_control_commands_test.rb +26 -0
- data/test/distributed_publish_subscribe_test.rb +92 -0
- data/test/distributed_remote_server_control_commands_test.rb +66 -0
- data/test/distributed_scripting_test.rb +102 -0
- data/test/distributed_sorting_test.rb +20 -0
- data/test/distributed_test.rb +58 -0
- data/test/distributed_transactions_test.rb +32 -0
- data/test/encoding_test.rb +18 -0
- data/test/error_replies_test.rb +59 -0
- data/test/fork_safety_test.rb +65 -0
- data/test/helper.rb +232 -0
- data/test/helper_test.rb +24 -0
- data/test/internals_test.rb +417 -0
- 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 +260 -0
- data/test/lint/value_types.rb +122 -0
- data/test/persistence_control_commands_test.rb +26 -0
- data/test/pipelining_commands_test.rb +242 -0
- data/test/publish_subscribe_test.rb +282 -0
- data/test/remote_server_control_commands_test.rb +118 -0
- data/test/scanning_test.rb +413 -0
- data/test/scripting_test.rb +78 -0
- data/test/sentinel_command_test.rb +80 -0
- data/test/sentinel_test.rb +255 -0
- data/test/sorting_test.rb +59 -0
- data/test/ssl_test.rb +73 -0
- data/test/support/connection/hiredis.rb +1 -0
- data/test/support/connection/ruby.rb +1 -0
- data/test/support/connection/synchrony.rb +17 -0
- data/test/support/redis_mock.rb +130 -0
- data/test/support/ssl/gen_certs.sh +31 -0
- data/test/support/ssl/trusted-ca.crt +25 -0
- data/test/support/ssl/trusted-ca.key +27 -0
- data/test/support/ssl/trusted-cert.crt +81 -0
- data/test/support/ssl/trusted-cert.key +28 -0
- data/test/support/ssl/untrusted-ca.crt +26 -0
- data/test/support/ssl/untrusted-ca.key +27 -0
- data/test/support/ssl/untrusted-cert.crt +82 -0
- data/test/support/ssl/untrusted-cert.key +28 -0
- data/test/support/wire/synchrony.rb +24 -0
- data/test/support/wire/thread.rb +5 -0
- data/test/synchrony_driver.rb +88 -0
- data/test/test.conf.erb +9 -0
- data/test/thread_safety_test.rb +62 -0
- data/test/transactions_test.rb +264 -0
- data/test/unknown_commands_test.rb +14 -0
- data/test/url_param_test.rb +138 -0
- metadata +202 -0
@@ -0,0 +1,873 @@
|
|
1
|
+
require "redis/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)
|
148
|
+
node_for(key).restore(key, ttl, serialized_value)
|
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
|