redis 2.0.0 → 2.2.1

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.
Files changed (81) hide show
  1. data/.gitignore +8 -0
  2. data/CHANGELOG.md +49 -0
  3. data/README.md +208 -0
  4. data/Rakefile +183 -73
  5. data/TODO.md +4 -0
  6. data/benchmarking/logging.rb +62 -0
  7. data/benchmarking/pipeline.rb +51 -0
  8. data/benchmarking/speed.rb +21 -0
  9. data/benchmarking/suite.rb +24 -0
  10. data/benchmarking/thread_safety.rb +38 -0
  11. data/benchmarking/worker.rb +71 -0
  12. data/examples/basic.rb +15 -0
  13. data/examples/dist_redis.rb +43 -0
  14. data/examples/incr-decr.rb +17 -0
  15. data/examples/list.rb +26 -0
  16. data/examples/pubsub.rb +31 -0
  17. data/examples/sets.rb +36 -0
  18. data/examples/unicorn/config.ru +3 -0
  19. data/examples/unicorn/unicorn.rb +20 -0
  20. data/lib/redis/client.rb +149 -165
  21. data/lib/redis/connection/command_helper.rb +45 -0
  22. data/lib/redis/connection/hiredis.rb +49 -0
  23. data/lib/redis/connection/registry.rb +12 -0
  24. data/lib/redis/connection/ruby.rb +135 -0
  25. data/lib/redis/connection/synchrony.rb +129 -0
  26. data/lib/redis/connection.rb +9 -0
  27. data/lib/redis/distributed.rb +182 -7
  28. data/lib/redis/pipeline.rb +22 -1
  29. data/lib/redis/subscribe.rb +19 -4
  30. data/lib/redis/version.rb +3 -0
  31. data/lib/redis.rb +715 -155
  32. data/redis.gemspec +24 -0
  33. data/test/commands_on_hashes_test.rb +32 -0
  34. data/test/commands_on_lists_test.rb +60 -0
  35. data/test/commands_on_sets_test.rb +78 -0
  36. data/test/commands_on_sorted_sets_test.rb +109 -0
  37. data/test/commands_on_strings_test.rb +80 -0
  38. data/test/commands_on_value_types_test.rb +88 -0
  39. data/test/connection_handling_test.rb +88 -0
  40. data/test/db/.gitignore +1 -0
  41. data/test/distributed_blocking_commands_test.rb +53 -0
  42. data/test/distributed_commands_on_hashes_test.rb +12 -0
  43. data/test/distributed_commands_on_lists_test.rb +24 -0
  44. data/test/distributed_commands_on_sets_test.rb +85 -0
  45. data/test/distributed_commands_on_strings_test.rb +50 -0
  46. data/test/distributed_commands_on_value_types_test.rb +73 -0
  47. data/test/distributed_commands_requiring_clustering_test.rb +148 -0
  48. data/test/distributed_connection_handling_test.rb +25 -0
  49. data/test/distributed_internals_test.rb +27 -0
  50. data/test/distributed_key_tags_test.rb +53 -0
  51. data/test/distributed_persistence_control_commands_test.rb +24 -0
  52. data/test/distributed_publish_subscribe_test.rb +101 -0
  53. data/test/distributed_remote_server_control_commands_test.rb +43 -0
  54. data/test/distributed_sorting_test.rb +21 -0
  55. data/test/distributed_test.rb +59 -0
  56. data/test/distributed_transactions_test.rb +34 -0
  57. data/test/encoding_test.rb +16 -0
  58. data/test/error_replies_test.rb +53 -0
  59. data/test/helper.rb +145 -0
  60. data/test/internals_test.rb +160 -0
  61. data/test/lint/hashes.rb +114 -0
  62. data/test/lint/internals.rb +37 -0
  63. data/test/lint/lists.rb +93 -0
  64. data/test/lint/sets.rb +66 -0
  65. data/test/lint/sorted_sets.rb +167 -0
  66. data/test/lint/strings.rb +137 -0
  67. data/test/lint/value_types.rb +84 -0
  68. data/test/persistence_control_commands_test.rb +22 -0
  69. data/test/pipelining_commands_test.rb +123 -0
  70. data/test/publish_subscribe_test.rb +158 -0
  71. data/test/redis_mock.rb +80 -0
  72. data/test/remote_server_control_commands_test.rb +82 -0
  73. data/test/sorting_test.rb +44 -0
  74. data/test/synchrony_driver.rb +57 -0
  75. data/test/test.conf +8 -0
  76. data/test/thread_safety_test.rb +30 -0
  77. data/test/transactions_test.rb +100 -0
  78. data/test/unknown_commands_test.rb +14 -0
  79. data/test/url_param_test.rb +60 -0
  80. metadata +130 -22
  81. data/README.markdown +0 -120
data/lib/redis.rb CHANGED
@@ -1,11 +1,21 @@
1
- require 'socket'
1
+ require "monitor"
2
2
 
3
3
  class Redis
4
- VERSION = "2.0.0"
5
-
6
4
  class ProtocolError < RuntimeError
7
5
  def initialize(reply_type)
8
- super("Protocol error, got '#{reply_type}' as initial reply byte")
6
+ super(<<-EOS.gsub(/(?:^|\n)\s*/, " "))
7
+ Got '#{reply_type}' as initial reply byte.
8
+ If you're running in a multi-threaded environment, make sure you
9
+ pass the :thread_safe option when initializing the connection.
10
+ If you're in a forking environment, such as Unicorn, you need to
11
+ connect to Redis after forking.
12
+ EOS
13
+ end
14
+ end
15
+
16
+ module DisableThreadSafety
17
+ def synchronize
18
+ yield
9
19
  end
10
20
  end
11
21
 
@@ -16,364 +26,805 @@ class Redis
16
26
  attr :client
17
27
 
18
28
  def self.connect(options = {})
29
+ options = options.dup
30
+
19
31
  require "uri"
20
32
 
21
33
  url = URI(options.delete(:url) || ENV["REDIS_URL"] || "redis://127.0.0.1:6379/0")
22
34
 
23
- options[:host] = url.host
24
- options[:port] = url.port
25
- options[:password] = url.password
26
- options[:db] = url.path[1..-1].to_i
35
+ options[:host] ||= url.host
36
+ options[:port] ||= url.port
37
+ options[:password] ||= url.password
38
+ options[:db] ||= url.path[1..-1].to_i
27
39
 
28
40
  new(options)
29
41
  end
30
42
 
43
+ def self.current
44
+ Thread.current[:redis] ||= Redis.connect
45
+ end
46
+
47
+ def self.current=(redis)
48
+ Thread.current[:redis] = redis
49
+ end
50
+
51
+ include MonitorMixin
52
+
31
53
  def initialize(options = {})
32
- if options[:thread_safe]
33
- @client = Client::ThreadSafe.new(options)
54
+ @client = Client.new(options)
55
+
56
+ if options[:thread_safe] == false
57
+ # Override #synchronize
58
+ extend DisableThreadSafety
34
59
  else
35
- @client = Client.new(options)
60
+ # Monitor#initialize
61
+ super()
62
+ end
63
+ end
64
+
65
+ # Run code without the client reconnecting
66
+ def without_reconnect(&block)
67
+ synchronize do
68
+ @client.without_reconnect(&block)
36
69
  end
37
70
  end
38
71
 
72
+ # Authenticate to the server.
73
+ def auth(password)
74
+ synchronize do
75
+ @client.call [:auth, password]
76
+ end
77
+ end
78
+
79
+ # Change the selected database for the current connection.
39
80
  def select(db)
40
- @client.db = db
41
- @client.call(:select, db)
81
+ synchronize do
82
+ @client.db = db
83
+ @client.call [:select, db]
84
+ end
42
85
  end
43
86
 
44
- def info
45
- Hash[*@client.call(:info).split(/:|\r\n/)]
87
+ # Get information and statistics about the server.
88
+ def info(cmd = nil)
89
+ synchronize do
90
+ reply = @client.call [:info, cmd].compact
91
+
92
+ if reply.kind_of?(String)
93
+ reply = Hash[*reply.split(/:|\r\n/).grep(/^[^#]/)]
94
+
95
+ if cmd && cmd.to_s == "commandstats"
96
+ # Extract nested hashes for INFO COMMANDSTATS
97
+ reply = Hash[reply.map do |k, v|
98
+ [k[/^cmdstat_(.*)$/, 1], Hash[*v.split(/,|=/)]]
99
+ end]
100
+ end
101
+ end
102
+
103
+ reply
104
+ end
105
+ end
106
+
107
+ def config(action, *args)
108
+ synchronize do
109
+ reply = @client.call [:config, action, *args]
110
+
111
+ if reply.kind_of?(Array) && action == :get
112
+ Hash[*reply]
113
+ else
114
+ reply
115
+ end
116
+ end
46
117
  end
47
118
 
119
+ # Remove all keys from the current database.
48
120
  def flushdb
49
- @client.call(:flushdb)
121
+ synchronize do
122
+ @client.call [:flushdb]
123
+ end
50
124
  end
51
125
 
126
+ # Remove all keys from all databases.
127
+ def flushall
128
+ synchronize do
129
+ @client.call [:flushall]
130
+ end
131
+ end
132
+
133
+ # Synchronously save the dataset to disk.
52
134
  def save
53
- @client.call(:save)
135
+ synchronize do
136
+ @client.call [:save]
137
+ end
54
138
  end
55
139
 
140
+ # Asynchronously save the dataset to disk.
56
141
  def bgsave
57
- @client.call(:bgsave)
142
+ synchronize do
143
+ @client.call [:bgsave]
144
+ end
58
145
  end
59
146
 
147
+ # Asynchronously rewrite the append-only file.
148
+ def bgrewriteaof
149
+ synchronize do
150
+ @client.call [:bgrewriteaof]
151
+ end
152
+ end
153
+
154
+ # Get the value of a key.
60
155
  def get(key)
61
- @client.call(:get, key)
156
+ synchronize do
157
+ @client.call [:get, key]
158
+ end
159
+ end
160
+
161
+ # Returns the bit value at offset in the string value stored at key.
162
+ def getbit(key, offset)
163
+ synchronize do
164
+ @client.call [:getbit, key, offset]
165
+ end
166
+ end
167
+
168
+ # Get a substring of the string stored at a key.
169
+ def getrange(key, start, stop)
170
+ synchronize do
171
+ @client.call [:getrange, key, start, stop]
172
+ end
62
173
  end
63
174
 
175
+ # Set the string value of a key and return its old value.
64
176
  def getset(key, value)
65
- @client.call(:getset, key, value)
177
+ synchronize do
178
+ @client.call [:getset, key, value]
179
+ end
66
180
  end
67
181
 
182
+ # Get the values of all the given keys.
68
183
  def mget(*keys)
69
- @client.call(:mget, *keys)
184
+ synchronize do
185
+ @client.call [:mget, *keys]
186
+ end
70
187
  end
71
188
 
189
+ # Append a value to a key.
72
190
  def append(key, value)
73
- @client.call(:append, key, value)
191
+ synchronize do
192
+ @client.call [:append, key, value]
193
+ end
74
194
  end
75
195
 
76
196
  def substr(key, start, stop)
77
- @client.call(:substr, key, start, stop)
197
+ synchronize do
198
+ @client.call [:substr, key, start, stop]
199
+ end
78
200
  end
79
201
 
202
+ # Get the length of the value stored in a key.
203
+ def strlen(key)
204
+ synchronize do
205
+ @client.call [:strlen, key]
206
+ end
207
+ end
208
+
209
+ # Get all the fields and values in a hash.
80
210
  def hgetall(key)
81
- Hash[*@client.call(:hgetall, key)]
211
+ synchronize do
212
+ reply = @client.call [:hgetall, key]
213
+
214
+ if reply.kind_of?(Array)
215
+ Hash[*reply]
216
+ else
217
+ reply
218
+ end
219
+ end
82
220
  end
83
221
 
222
+ # Get the value of a hash field.
84
223
  def hget(key, field)
85
- @client.call(:hget, key, field)
224
+ synchronize do
225
+ @client.call [:hget, key, field]
226
+ end
86
227
  end
87
228
 
229
+ # Delete a hash field.
88
230
  def hdel(key, field)
89
- @client.call(:hdel, key, field)
231
+ synchronize do
232
+ @client.call [:hdel, key, field]
233
+ end
90
234
  end
91
235
 
236
+ # Get all the fields in a hash.
92
237
  def hkeys(key)
93
- @client.call(:hkeys, key)
238
+ synchronize do
239
+ @client.call [:hkeys, key]
240
+ end
94
241
  end
95
242
 
243
+ # Find all keys matching the given pattern.
96
244
  def keys(pattern = "*")
97
- @client.call(:keys, pattern)
245
+ synchronize do
246
+ reply = @client.call [:keys, pattern]
247
+
248
+ if reply.kind_of?(String)
249
+ reply.split(" ")
250
+ else
251
+ reply
252
+ end
253
+ end
98
254
  end
99
255
 
256
+ # Return a random key from the keyspace.
100
257
  def randomkey
101
- @client.call(:randomkey)
258
+ synchronize do
259
+ @client.call [:randomkey]
260
+ end
102
261
  end
103
262
 
263
+ # Echo the given string.
104
264
  def echo(value)
105
- @client.call(:echo, value)
265
+ synchronize do
266
+ @client.call [:echo, value]
267
+ end
106
268
  end
107
269
 
270
+ # Ping the server.
108
271
  def ping
109
- @client.call(:ping)
272
+ synchronize do
273
+ @client.call [:ping]
274
+ end
110
275
  end
111
276
 
277
+ # Get the UNIX time stamp of the last successful save to disk.
112
278
  def lastsave
113
- @client.call(:lastsave)
279
+ synchronize do
280
+ @client.call [:lastsave]
281
+ end
114
282
  end
115
283
 
284
+ # Return the number of keys in the selected database.
116
285
  def dbsize
117
- @client.call(:dbsize)
286
+ synchronize do
287
+ @client.call [:dbsize]
288
+ end
118
289
  end
119
290
 
291
+ # Determine if a key exists.
120
292
  def exists(key)
121
- _bool @client.call(:exists, key)
293
+ synchronize do
294
+ _bool @client.call [:exists, key]
295
+ end
122
296
  end
123
297
 
298
+ # Get the length of a list.
124
299
  def llen(key)
125
- @client.call(:llen, key)
300
+ synchronize do
301
+ @client.call [:llen, key]
302
+ end
126
303
  end
127
304
 
305
+ # Get a range of elements from a list.
128
306
  def lrange(key, start, stop)
129
- @client.call(:lrange, key, start, stop)
307
+ synchronize do
308
+ @client.call [:lrange, key, start, stop]
309
+ end
130
310
  end
131
311
 
312
+ # Trim a list to the specified range.
132
313
  def ltrim(key, start, stop)
133
- @client.call(:ltrim, key, start, stop)
314
+ synchronize do
315
+ @client.call [:ltrim, key, start, stop]
316
+ end
134
317
  end
135
318
 
319
+ # Get an element from a list by its index.
136
320
  def lindex(key, index)
137
- @client.call(:lindex, key, index)
321
+ synchronize do
322
+ @client.call [:lindex, key, index]
323
+ end
324
+ end
325
+
326
+ # Insert an element before or after another element in a list.
327
+ def linsert(key, where, pivot, value)
328
+ synchronize do
329
+ @client.call [:linsert, key, where, pivot, value]
330
+ end
138
331
  end
139
332
 
333
+ # Set the value of an element in a list by its index.
140
334
  def lset(key, index, value)
141
- @client.call(:lset, key, index, value)
335
+ synchronize do
336
+ @client.call [:lset, key, index, value]
337
+ end
142
338
  end
143
339
 
340
+ # Remove elements from a list.
144
341
  def lrem(key, count, value)
145
- @client.call(:lrem, key, count, value)
342
+ synchronize do
343
+ @client.call [:lrem, key, count, value]
344
+ end
146
345
  end
147
346
 
347
+ # Append a value to a list.
148
348
  def rpush(key, value)
149
- @client.call(:rpush, key, value)
349
+ synchronize do
350
+ @client.call [:rpush, key, value]
351
+ end
150
352
  end
151
353
 
354
+ # Append a value to a list, only if the list exists.
355
+ def rpushx(key, value)
356
+ synchronize do
357
+ @client.call [:rpushx, key, value]
358
+ end
359
+ end
360
+
361
+ # Prepend a value to a list.
152
362
  def lpush(key, value)
153
- @client.call(:lpush, key, value)
363
+ synchronize do
364
+ @client.call [:lpush, key, value]
365
+ end
154
366
  end
155
367
 
368
+ # Prepend a value to a list, only if the list exists.
369
+ def lpushx(key, value)
370
+ synchronize do
371
+ @client.call [:lpushx, key, value]
372
+ end
373
+ end
374
+
375
+ # Remove and get the last element in a list.
156
376
  def rpop(key)
157
- @client.call(:rpop, key)
377
+ synchronize do
378
+ @client.call [:rpop, key]
379
+ end
158
380
  end
159
381
 
160
- def blpop(key, timeout)
161
- @client.call_without_timeout(:blpop, key, timeout)
382
+ # Remove and get the first element in a list, or block until one is available.
383
+ def blpop(*args)
384
+ synchronize do
385
+ @client.call_without_timeout(:blpop, *args)
386
+ end
162
387
  end
163
388
 
164
- def brpop(key, timeout)
165
- @client.call_without_timeout(:brpop, key, timeout)
389
+ # Remove and get the last element in a list, or block until one is available.
390
+ def brpop(*args)
391
+ synchronize do
392
+ @client.call_without_timeout(:brpop, *args)
393
+ end
166
394
  end
167
395
 
396
+ # Pop a value from a list, push it to another list and return it; or block
397
+ # until one is available.
398
+ def brpoplpush(source, destination, timeout)
399
+ synchronize do
400
+ @client.call_without_timeout(:brpoplpush, source, destination, timeout)
401
+ end
402
+ end
403
+
404
+ # Remove the last element in a list, append it to another list and return it.
168
405
  def rpoplpush(source, destination)
169
- @client.call(:rpoplpush, source, destination)
406
+ synchronize do
407
+ @client.call [:rpoplpush, source, destination]
408
+ end
170
409
  end
171
410
 
411
+ # Remove and get the first element in a list.
172
412
  def lpop(key)
173
- @client.call(:lpop, key)
413
+ synchronize do
414
+ @client.call [:lpop, key]
415
+ end
174
416
  end
175
417
 
418
+ # Get all the members in a set.
176
419
  def smembers(key)
177
- @client.call(:smembers, key)
420
+ synchronize do
421
+ @client.call [:smembers, key]
422
+ end
178
423
  end
179
424
 
425
+ # Determine if a given value is a member of a set.
180
426
  def sismember(key, member)
181
- _bool @client.call(:sismember, key, member)
427
+ synchronize do
428
+ _bool @client.call [:sismember, key, member]
429
+ end
182
430
  end
183
431
 
432
+ # Add a member to a set.
184
433
  def sadd(key, value)
185
- _bool @client.call(:sadd, key, value)
434
+ synchronize do
435
+ _bool @client.call [:sadd, key, value]
436
+ end
186
437
  end
187
438
 
439
+ # Remove a member from a set.
188
440
  def srem(key, value)
189
- _bool @client.call(:srem, key, value)
441
+ synchronize do
442
+ _bool @client.call [:srem, key, value]
443
+ end
190
444
  end
191
445
 
446
+ # Move a member from one set to another.
192
447
  def smove(source, destination, member)
193
- _bool @client.call(:smove, source, destination, member)
448
+ synchronize do
449
+ _bool @client.call [:smove, source, destination, member]
450
+ end
194
451
  end
195
452
 
453
+ # Remove and return a random member from a set.
196
454
  def spop(key)
197
- @client.call(:spop, key)
455
+ synchronize do
456
+ @client.call [:spop, key]
457
+ end
198
458
  end
199
459
 
460
+ # Get the number of members in a set.
200
461
  def scard(key)
201
- @client.call(:scard, key)
462
+ synchronize do
463
+ @client.call [:scard, key]
464
+ end
202
465
  end
203
466
 
467
+ # Intersect multiple sets.
204
468
  def sinter(*keys)
205
- @client.call(:sinter, *keys)
469
+ synchronize do
470
+ @client.call [:sinter, *keys]
471
+ end
206
472
  end
207
473
 
474
+ # Intersect multiple sets and store the resulting set in a key.
208
475
  def sinterstore(destination, *keys)
209
- @client.call(:sinterstore, destination, *keys)
476
+ synchronize do
477
+ @client.call [:sinterstore, destination, *keys]
478
+ end
210
479
  end
211
480
 
481
+ # Add multiple sets.
212
482
  def sunion(*keys)
213
- @client.call(:sunion, *keys)
483
+ synchronize do
484
+ @client.call [:sunion, *keys]
485
+ end
214
486
  end
215
487
 
488
+ # Add multiple sets and store the resulting set in a key.
216
489
  def sunionstore(destination, *keys)
217
- @client.call(:sunionstore, destination, *keys)
490
+ synchronize do
491
+ @client.call [:sunionstore, destination, *keys]
492
+ end
218
493
  end
219
494
 
495
+ # Subtract multiple sets.
220
496
  def sdiff(*keys)
221
- @client.call(:sdiff, *keys)
497
+ synchronize do
498
+ @client.call [:sdiff, *keys]
499
+ end
222
500
  end
223
501
 
502
+ # Subtract multiple sets and store the resulting set in a key.
224
503
  def sdiffstore(destination, *keys)
225
- @client.call(:sdiffstore, destination, *keys)
504
+ synchronize do
505
+ @client.call [:sdiffstore, destination, *keys]
506
+ end
226
507
  end
227
508
 
509
+ # Get a random member from a set.
228
510
  def srandmember(key)
229
- @client.call(:srandmember, key)
511
+ synchronize do
512
+ @client.call [:srandmember, key]
513
+ end
230
514
  end
231
515
 
516
+ # Add a member to a sorted set, or update its score if it already exists.
232
517
  def zadd(key, score, member)
233
- _bool @client.call(:zadd, key, score, member)
518
+ synchronize do
519
+ _bool @client.call [:zadd, key, score, member]
520
+ end
234
521
  end
235
522
 
523
+ # Determine the index of a member in a sorted set.
236
524
  def zrank(key, member)
237
- @client.call(:zrank, key, member)
525
+ synchronize do
526
+ @client.call [:zrank, key, member]
527
+ end
238
528
  end
239
529
 
530
+ # Determine the index of a member in a sorted set, with scores ordered from
531
+ # high to low.
240
532
  def zrevrank(key, member)
241
- @client.call(:zrevrank, key, member)
533
+ synchronize do
534
+ @client.call [:zrevrank, key, member]
535
+ end
242
536
  end
243
537
 
538
+ # Increment the score of a member in a sorted set.
244
539
  def zincrby(key, increment, member)
245
- @client.call(:zincrby, key, increment, member)
540
+ synchronize do
541
+ @client.call [:zincrby, key, increment, member]
542
+ end
246
543
  end
247
544
 
545
+ # Get the number of members in a sorted set.
248
546
  def zcard(key)
249
- @client.call(:zcard, key)
547
+ synchronize do
548
+ @client.call [:zcard, key]
549
+ end
250
550
  end
251
551
 
552
+ # Return a range of members in a sorted set, by index.
252
553
  def zrange(key, start, stop, options = {})
253
554
  command = CommandOptions.new(options) do |c|
555
+ c.bool :withscores
254
556
  c.bool :with_scores
255
557
  end
256
558
 
257
- @client.call(:zrange, key, start, stop, *command.to_a)
559
+ synchronize do
560
+ @client.call [:zrange, key, start, stop, *command.to_a]
561
+ end
258
562
  end
259
563
 
564
+ # Return a range of members in a sorted set, by score.
260
565
  def zrangebyscore(key, min, max, options = {})
261
566
  command = CommandOptions.new(options) do |c|
262
567
  c.splat :limit
568
+ c.bool :withscores
263
569
  c.bool :with_scores
264
570
  end
265
571
 
266
- @client.call(:zrangebyscore, key, min, max, *command.to_a)
572
+ synchronize do
573
+ @client.call [:zrangebyscore, key, min, max, *command.to_a]
574
+ end
267
575
  end
268
576
 
577
+ # Count the members in a sorted set with scores within the given values.
578
+ def zcount(key, start, stop)
579
+ synchronize do
580
+ @client.call [:zcount, key, start, stop]
581
+ end
582
+ end
583
+
584
+ # Return a range of members in a sorted set, by index, with scores ordered
585
+ # from high to low.
269
586
  def zrevrange(key, start, stop, options = {})
270
587
  command = CommandOptions.new(options) do |c|
588
+ c.bool :withscores
271
589
  c.bool :with_scores
272
590
  end
273
591
 
274
- @client.call(:zrevrange, key, start, stop, *command.to_a)
592
+ synchronize do
593
+ @client.call [:zrevrange, key, start, stop, *command.to_a]
594
+ end
595
+ end
596
+
597
+ # Return a range of members in a sorted set, by score, with scores ordered
598
+ # from high to low.
599
+ def zrevrangebyscore(key, max, min, options = {})
600
+ command = CommandOptions.new(options) do |c|
601
+ c.splat :limit
602
+ c.bool :withscores
603
+ c.bool :with_scores
604
+ end
605
+
606
+ synchronize do
607
+ @client.call [:zrevrangebyscore, key, max, min, *command.to_a]
608
+ end
275
609
  end
276
610
 
611
+ # Remove all members in a sorted set within the given scores.
277
612
  def zremrangebyscore(key, min, max)
278
- @client.call(:zremrangebyscore, key, min, max)
613
+ synchronize do
614
+ @client.call [:zremrangebyscore, key, min, max]
615
+ end
279
616
  end
280
617
 
618
+ # Remove all members in a sorted set within the given indexes.
281
619
  def zremrangebyrank(key, start, stop)
282
- @client.call(:zremrangebyrank, key, start, stop)
620
+ synchronize do
621
+ @client.call [:zremrangebyrank, key, start, stop]
622
+ end
283
623
  end
284
624
 
625
+ # Get the score associated with the given member in a sorted set.
285
626
  def zscore(key, member)
286
- @client.call(:zscore, key, member)
627
+ synchronize do
628
+ @client.call [:zscore, key, member]
629
+ end
287
630
  end
288
631
 
632
+ # Remove a member from a sorted set.
289
633
  def zrem(key, member)
290
- _bool @client.call(:zrem, key, member)
634
+ synchronize do
635
+ _bool @client.call [:zrem, key, member]
636
+ end
291
637
  end
292
638
 
639
+ # Intersect multiple sorted sets and store the resulting sorted set in a new
640
+ # key.
293
641
  def zinterstore(destination, keys, options = {})
294
642
  command = CommandOptions.new(options) do |c|
295
643
  c.splat :weights
296
644
  c.value :aggregate
297
645
  end
298
646
 
299
- @client.call(:zinterstore, destination, keys.size, *(keys + command.to_a))
647
+ synchronize do
648
+ @client.call [:zinterstore, destination, keys.size, *(keys + command.to_a)]
649
+ end
300
650
  end
301
651
 
652
+ # Add multiple sorted sets and store the resulting sorted set in a new key.
302
653
  def zunionstore(destination, keys, options = {})
303
654
  command = CommandOptions.new(options) do |c|
304
655
  c.splat :weights
305
656
  c.value :aggregate
306
657
  end
307
658
 
308
- @client.call(:zunionstore, destination, keys.size, *(keys + command.to_a))
659
+ synchronize do
660
+ @client.call [:zunionstore, destination, keys.size, *(keys + command.to_a)]
661
+ end
309
662
  end
310
663
 
664
+ # Move a key to another database.
311
665
  def move(key, db)
312
- _bool @client.call(:move, key, db)
666
+ synchronize do
667
+ _bool @client.call [:move, key, db]
668
+ end
313
669
  end
314
670
 
671
+ # Set the value of a key, only if the key does not exist.
315
672
  def setnx(key, value)
316
- _bool @client.call(:setnx, key, value)
673
+ synchronize do
674
+ _bool @client.call [:setnx, key, value]
675
+ end
317
676
  end
318
677
 
678
+ # Delete a key.
319
679
  def del(*keys)
320
- _bool @client.call(:del, *keys)
680
+ synchronize do
681
+ @client.call [:del, *keys]
682
+ end
321
683
  end
322
684
 
685
+ # Rename a key.
323
686
  def rename(old_name, new_name)
324
- @client.call(:rename, old_name, new_name)
687
+ synchronize do
688
+ @client.call [:rename, old_name, new_name]
689
+ end
325
690
  end
326
691
 
692
+ # Rename a key, only if the new key does not exist.
327
693
  def renamenx(old_name, new_name)
328
- _bool @client.call(:renamenx, old_name, new_name)
694
+ synchronize do
695
+ _bool @client.call [:renamenx, old_name, new_name]
696
+ end
329
697
  end
330
698
 
699
+ # Set a key's time to live in seconds.
331
700
  def expire(key, seconds)
332
- _bool @client.call(:expire, key, seconds)
701
+ synchronize do
702
+ _bool @client.call [:expire, key, seconds]
703
+ end
704
+ end
705
+
706
+ # Remove the expiration from a key.
707
+ def persist(key)
708
+ synchronize do
709
+ _bool @client.call [:persist, key]
710
+ end
333
711
  end
334
712
 
713
+ # Get the time to live for a key.
335
714
  def ttl(key)
336
- @client.call(:ttl, key)
715
+ synchronize do
716
+ @client.call [:ttl, key]
717
+ end
337
718
  end
338
719
 
720
+ # Set the expiration for a key as a UNIX timestamp.
339
721
  def expireat(key, unix_time)
340
- _bool @client.call(:expireat, key, unix_time)
722
+ synchronize do
723
+ _bool @client.call [:expireat, key, unix_time]
724
+ end
341
725
  end
342
726
 
727
+ # Set the string value of a hash field.
343
728
  def hset(key, field, value)
344
- _bool @client.call(:hset, key, field, value)
729
+ synchronize do
730
+ _bool @client.call [:hset, key, field, value]
731
+ end
345
732
  end
346
733
 
734
+ # Set the value of a hash field, only if the field does not exist.
735
+ def hsetnx(key, field, value)
736
+ synchronize do
737
+ _bool @client.call [:hsetnx, key, field, value]
738
+ end
739
+ end
740
+
741
+ # Set multiple hash fields to multiple values.
347
742
  def hmset(key, *attrs)
348
- @client.call(:hmset, key, *attrs)
743
+ synchronize do
744
+ @client.call [:hmset, key, *attrs]
745
+ end
349
746
  end
350
747
 
351
748
  def mapped_hmset(key, hash)
352
749
  hmset(key, *hash.to_a.flatten)
353
750
  end
354
751
 
752
+ # Get the values of all the given hash fields.
753
+ def hmget(key, *fields)
754
+ synchronize do
755
+ @client.call [:hmget, key, *fields]
756
+ end
757
+ end
758
+
759
+ def mapped_hmget(key, *fields)
760
+ reply = hmget(key, *fields)
761
+
762
+ if reply.kind_of?(Array)
763
+ Hash[*fields.zip(reply).flatten]
764
+ else
765
+ reply
766
+ end
767
+ end
768
+
769
+ # Get the number of fields in a hash.
355
770
  def hlen(key)
356
- @client.call(:hlen, key)
771
+ synchronize do
772
+ @client.call [:hlen, key]
773
+ end
357
774
  end
358
775
 
776
+ # Get all the values in a hash.
359
777
  def hvals(key)
360
- @client.call(:hvals, key)
778
+ synchronize do
779
+ @client.call [:hvals, key]
780
+ end
361
781
  end
362
782
 
783
+ # Increment the integer value of a hash field by the given number.
363
784
  def hincrby(key, field, increment)
364
- @client.call(:hincrby, key, field, increment)
785
+ synchronize do
786
+ @client.call [:hincrby, key, field, increment]
787
+ end
365
788
  end
366
789
 
790
+ # Discard all commands issued after MULTI.
367
791
  def discard
368
- @client.call(:discard)
792
+ synchronize do
793
+ @client.call [:discard]
794
+ end
369
795
  end
370
796
 
797
+ # Determine if a hash field exists.
371
798
  def hexists(key, field)
372
- _bool @client.call(:hexists, key, field)
799
+ synchronize do
800
+ _bool @client.call [:hexists, key, field]
801
+ end
373
802
  end
374
803
 
804
+ # Listen for all requests received by the server in real time.
375
805
  def monitor(&block)
376
- @client.call_loop(:monitor, &block)
806
+ synchronize do
807
+ @client.call_loop([:monitor], &block)
808
+ end
809
+ end
810
+
811
+ def debug(*args)
812
+ synchronize do
813
+ @client.call [:debug, *args]
814
+ end
815
+ end
816
+
817
+ def object(*args)
818
+ synchronize do
819
+ @client.call [:object, *args]
820
+ end
821
+ end
822
+
823
+ # Internal command used for replication.
824
+ def sync
825
+ synchronize do
826
+ @client.call [:sync]
827
+ end
377
828
  end
378
829
 
379
830
  def [](key)
@@ -384,24 +835,50 @@ class Redis
384
835
  set(key, value)
385
836
  end
386
837
 
838
+ # Set the string value of a key.
387
839
  def set(key, value)
388
- @client.call(:set, key, value)
840
+ synchronize do
841
+ @client.call [:set, key, value]
842
+ end
389
843
  end
390
844
 
845
+ # Sets or clears the bit at offset in the string value stored at key.
846
+ def setbit(key, offset, value)
847
+ synchronize do
848
+ @client.call [:setbit, key, offset, value]
849
+ end
850
+ end
851
+
852
+ # Set the value and expiration of a key.
391
853
  def setex(key, ttl, value)
392
- @client.call(:setex, key, ttl, value)
854
+ synchronize do
855
+ @client.call [:setex, key, ttl, value]
856
+ end
857
+ end
858
+
859
+ # Overwrite part of a string at key starting at the specified offset.
860
+ def setrange(key, offset, value)
861
+ synchronize do
862
+ @client.call [:setrange, key, offset, value]
863
+ end
393
864
  end
394
865
 
866
+ # Set multiple keys to multiple values.
395
867
  def mset(*args)
396
- @client.call(:mset, *args)
868
+ synchronize do
869
+ @client.call [:mset, *args]
870
+ end
397
871
  end
398
872
 
399
873
  def mapped_mset(hash)
400
874
  mset(*hash.to_a.flatten)
401
875
  end
402
876
 
877
+ # Set multiple keys to multiple values, only if none of the keys exist.
403
878
  def msetnx(*args)
404
- @client.call(:msetnx, *args)
879
+ synchronize do
880
+ @client.call [:msetnx, *args]
881
+ end
405
882
  end
406
883
 
407
884
  def mapped_msetnx(hash)
@@ -409,14 +886,16 @@ class Redis
409
886
  end
410
887
 
411
888
  def mapped_mget(*keys)
412
- result = {}
413
- mget(*keys).each do |value|
414
- key = keys.shift
415
- result.merge!(key => value) unless value.nil?
889
+ reply = mget(*keys)
890
+
891
+ if reply.kind_of?(Array)
892
+ Hash[*keys.zip(reply).flatten]
893
+ else
894
+ reply
416
895
  end
417
- result
418
896
  end
419
897
 
898
+ # Sort the elements in a list, set or sorted set.
420
899
  def sort(key, options = {})
421
900
  command = CommandOptions.new(options) do |c|
422
901
  c.value :by
@@ -426,93 +905,181 @@ class Redis
426
905
  c.value :store
427
906
  end
428
907
 
429
- @client.call(:sort, key, *command.to_a)
908
+ synchronize do
909
+ @client.call [:sort, key, *command.to_a]
910
+ end
430
911
  end
431
912
 
913
+ # Increment the integer value of a key by one.
432
914
  def incr(key)
433
- @client.call(:incr, key)
915
+ synchronize do
916
+ @client.call [:incr, key]
917
+ end
434
918
  end
435
919
 
920
+ # Increment the integer value of a key by the given number.
436
921
  def incrby(key, increment)
437
- @client.call(:incrby, key, increment)
922
+ synchronize do
923
+ @client.call [:incrby, key, increment]
924
+ end
438
925
  end
439
926
 
927
+ # Decrement the integer value of a key by one.
440
928
  def decr(key)
441
- @client.call(:decr, key)
929
+ synchronize do
930
+ @client.call [:decr, key]
931
+ end
442
932
  end
443
933
 
934
+ # Decrement the integer value of a key by the given number.
444
935
  def decrby(key, decrement)
445
- @client.call(:decrby, key, decrement)
936
+ synchronize do
937
+ @client.call [:decrby, key, decrement]
938
+ end
446
939
  end
447
940
 
941
+ # Determine the type stored at key.
448
942
  def type(key)
449
- @client.call(:type, key)
943
+ synchronize do
944
+ @client.call [:type, key]
945
+ end
450
946
  end
451
947
 
948
+ # Close the connection.
452
949
  def quit
453
- @client.call(:quit)
454
- rescue Errno::ECONNRESET
950
+ synchronize do
951
+ begin
952
+ @client.call [:quit]
953
+ rescue Errno::ECONNRESET
954
+ ensure
955
+ @client.disconnect
956
+ end
957
+ end
455
958
  end
456
959
 
457
- def pipelined
458
- original, @client = @client, Pipeline.new
459
- yield
460
- original.call_pipelined(@client.commands) unless @client.commands.empty?
461
- ensure
462
- @client = original
960
+ # Synchronously save the dataset to disk and then shut down the server.
961
+ def shutdown
962
+ synchronize do
963
+ @client.call_without_reply [:shutdown]
964
+ end
463
965
  end
464
966
 
465
- def exec
466
- @client.call(:exec)
967
+ # Make the server a slave of another instance, or promote it as master.
968
+ def slaveof(host, port)
969
+ synchronize do
970
+ @client.call [:slaveof, host, port]
971
+ end
467
972
  end
468
973
 
469
- def multi(&block)
470
- result = @client.call :multi
974
+ def pipelined(options = {})
975
+ synchronize do
976
+ begin
977
+ original, @client = @client, Pipeline.new
978
+ yield
979
+ original.call_pipelined(@client.commands, options) unless @client.commands.empty?
980
+ ensure
981
+ @client = original
982
+ end
983
+ end
984
+ end
471
985
 
472
- return result unless block_given?
986
+ # Watch the given keys to determine execution of the MULTI/EXEC block.
987
+ def watch(*keys)
988
+ synchronize do
989
+ @client.call [:watch, *keys]
990
+ end
991
+ end
473
992
 
474
- begin
475
- yield(self)
476
- rescue Exception => e
477
- discard
478
- raise e
993
+ # Forget about all watched keys.
994
+ def unwatch
995
+ synchronize do
996
+ @client.call [:unwatch]
479
997
  end
998
+ end
480
999
 
481
- exec
1000
+ # Execute all commands issued after MULTI.
1001
+ def exec
1002
+ synchronize do
1003
+ @client.call [:exec]
1004
+ end
1005
+ end
1006
+
1007
+ # Mark the start of a transaction block.
1008
+ def multi
1009
+ synchronize do
1010
+ if !block_given?
1011
+ @client.call :multi
1012
+ else
1013
+ result = pipelined(:raise => false) do
1014
+ multi
1015
+ yield(self)
1016
+ exec
1017
+ end
1018
+
1019
+ result.last
1020
+ end
1021
+ end
482
1022
  end
483
1023
 
1024
+ # Post a message to a channel.
484
1025
  def publish(channel, message)
485
- @client.call(:publish, channel, message)
1026
+ synchronize do
1027
+ @client.call [:publish, channel, message]
1028
+ end
486
1029
  end
487
1030
 
488
1031
  def subscribed?
489
- @client.kind_of? SubscribedClient
1032
+ synchronize do
1033
+ @client.kind_of? SubscribedClient
1034
+ end
490
1035
  end
491
1036
 
1037
+ # Stop listening for messages posted to the given channels.
492
1038
  def unsubscribe(*channels)
493
- raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
494
- @client.unsubscribe(*channels)
1039
+ synchronize do
1040
+ raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
1041
+ @client.unsubscribe(*channels)
1042
+ end
495
1043
  end
496
1044
 
1045
+ # Stop listening for messages posted to channels matching the given patterns.
497
1046
  def punsubscribe(*channels)
498
- raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
499
- @client.punsubscribe(*channels)
1047
+ synchronize do
1048
+ raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
1049
+ @client.punsubscribe(*channels)
1050
+ end
500
1051
  end
501
1052
 
1053
+ # Listen for messages published to the given channels.
502
1054
  def subscribe(*channels, &block)
503
- subscription(:subscribe, channels, block)
1055
+ synchronize do
1056
+ subscription(:subscribe, channels, block)
1057
+ end
504
1058
  end
505
1059
 
1060
+ # Listen for messages published to channels matching the given patterns.
506
1061
  def psubscribe(*channels, &block)
507
- subscription(:psubscribe, channels, block)
1062
+ synchronize do
1063
+ subscription(:psubscribe, channels, block)
1064
+ end
508
1065
  end
509
1066
 
510
1067
  def id
511
- @client.id
1068
+ synchronize do
1069
+ @client.id
1070
+ end
1071
+ end
1072
+
1073
+ def inspect
1074
+ synchronize do
1075
+ "#<Redis client v#{Redis::VERSION} connected to #{id} (Redis v#{info["redis_version"]})>"
1076
+ end
512
1077
  end
513
1078
 
514
1079
  def method_missing(command, *args)
515
- @client.call(command, *args)
1080
+ synchronize do
1081
+ @client.call [command, *args]
1082
+ end
516
1083
  end
517
1084
 
518
1085
  class CommandOptions
@@ -553,12 +1120,15 @@ class Redis
553
1120
 
554
1121
  private
555
1122
 
1123
+ # Commands returning 1 for true and 0 for false may be executed in a pipeline
1124
+ # where the method call will return nil. Propagate the nil instead of falsely
1125
+ # returning false.
556
1126
  def _bool(value)
557
- value == 1
1127
+ value == 1 if value
558
1128
  end
559
1129
 
560
1130
  def subscription(method, channels, block)
561
- return @client.call(method, *channels) if subscribed?
1131
+ return @client.call [method, *channels] if subscribed?
562
1132
 
563
1133
  begin
564
1134
  original, @client = @client, SubscribedClient.new(@client)
@@ -570,19 +1140,9 @@ private
570
1140
 
571
1141
  end
572
1142
 
573
- begin
574
- if RUBY_VERSION >= '1.9'
575
- require 'timeout'
576
- Redis::Timer = Timeout
577
- else
578
- require 'system_timer'
579
- Redis::Timer = SystemTimer
580
- end
581
- rescue LoadError
582
- Redis::Timer = nil
583
- end
584
-
585
- require 'redis/client'
586
- require 'redis/pipeline'
587
- require 'redis/subscribe'
588
- require 'redis/compat'
1143
+ require "redis/version"
1144
+ require "redis/connection"
1145
+ require "redis/client"
1146
+ require "redis/pipeline"
1147
+ require "redis/subscribe"
1148
+ require "redis/compat"