redis 3.3.3 → 5.0.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.
Files changed (136) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +280 -12
  3. data/README.md +141 -147
  4. data/lib/redis/client.rb +77 -539
  5. data/lib/redis/commands/bitmaps.rb +66 -0
  6. data/lib/redis/commands/cluster.rb +28 -0
  7. data/lib/redis/commands/connection.rb +53 -0
  8. data/lib/redis/commands/geo.rb +84 -0
  9. data/lib/redis/commands/hashes.rb +254 -0
  10. data/lib/redis/commands/hyper_log_log.rb +37 -0
  11. data/lib/redis/commands/keys.rb +437 -0
  12. data/lib/redis/commands/lists.rb +285 -0
  13. data/lib/redis/commands/pubsub.rb +54 -0
  14. data/lib/redis/commands/scripting.rb +114 -0
  15. data/lib/redis/commands/server.rb +188 -0
  16. data/lib/redis/commands/sets.rb +214 -0
  17. data/lib/redis/commands/sorted_sets.rb +818 -0
  18. data/lib/redis/commands/streams.rb +384 -0
  19. data/lib/redis/commands/strings.rb +314 -0
  20. data/lib/redis/commands/transactions.rb +115 -0
  21. data/lib/redis/commands.rb +235 -0
  22. data/lib/redis/distributed.rb +300 -108
  23. data/lib/redis/errors.rb +22 -1
  24. data/lib/redis/hash_ring.rb +36 -79
  25. data/lib/redis/pipeline.rb +69 -83
  26. data/lib/redis/subscribe.rb +26 -19
  27. data/lib/redis/version.rb +3 -1
  28. data/lib/redis.rb +113 -2685
  29. metadata +40 -218
  30. data/.gitignore +0 -16
  31. data/.travis/Gemfile +0 -11
  32. data/.travis.yml +0 -89
  33. data/.yardopts +0 -3
  34. data/Gemfile +0 -4
  35. data/Rakefile +0 -87
  36. data/benchmarking/logging.rb +0 -71
  37. data/benchmarking/pipeline.rb +0 -51
  38. data/benchmarking/speed.rb +0 -21
  39. data/benchmarking/suite.rb +0 -24
  40. data/benchmarking/worker.rb +0 -71
  41. data/examples/basic.rb +0 -15
  42. data/examples/consistency.rb +0 -114
  43. data/examples/dist_redis.rb +0 -43
  44. data/examples/incr-decr.rb +0 -17
  45. data/examples/list.rb +0 -26
  46. data/examples/pubsub.rb +0 -37
  47. data/examples/sentinel/sentinel.conf +0 -9
  48. data/examples/sentinel/start +0 -49
  49. data/examples/sentinel.rb +0 -41
  50. data/examples/sets.rb +0 -36
  51. data/examples/unicorn/config.ru +0 -3
  52. data/examples/unicorn/unicorn.rb +0 -20
  53. data/lib/redis/connection/command_helper.rb +0 -44
  54. data/lib/redis/connection/hiredis.rb +0 -66
  55. data/lib/redis/connection/registry.rb +0 -12
  56. data/lib/redis/connection/ruby.rb +0 -429
  57. data/lib/redis/connection/synchrony.rb +0 -133
  58. data/lib/redis/connection.rb +0 -9
  59. data/redis.gemspec +0 -44
  60. data/test/bitpos_test.rb +0 -69
  61. data/test/blocking_commands_test.rb +0 -42
  62. data/test/client_test.rb +0 -59
  63. data/test/command_map_test.rb +0 -30
  64. data/test/commands_on_hashes_test.rb +0 -21
  65. data/test/commands_on_hyper_log_log_test.rb +0 -21
  66. data/test/commands_on_lists_test.rb +0 -20
  67. data/test/commands_on_sets_test.rb +0 -77
  68. data/test/commands_on_sorted_sets_test.rb +0 -137
  69. data/test/commands_on_strings_test.rb +0 -101
  70. data/test/commands_on_value_types_test.rb +0 -133
  71. data/test/connection_handling_test.rb +0 -277
  72. data/test/db/.gitkeep +0 -0
  73. data/test/distributed_blocking_commands_test.rb +0 -46
  74. data/test/distributed_commands_on_hashes_test.rb +0 -10
  75. data/test/distributed_commands_on_hyper_log_log_test.rb +0 -33
  76. data/test/distributed_commands_on_lists_test.rb +0 -22
  77. data/test/distributed_commands_on_sets_test.rb +0 -83
  78. data/test/distributed_commands_on_sorted_sets_test.rb +0 -18
  79. data/test/distributed_commands_on_strings_test.rb +0 -59
  80. data/test/distributed_commands_on_value_types_test.rb +0 -95
  81. data/test/distributed_commands_requiring_clustering_test.rb +0 -164
  82. data/test/distributed_connection_handling_test.rb +0 -23
  83. data/test/distributed_internals_test.rb +0 -79
  84. data/test/distributed_key_tags_test.rb +0 -52
  85. data/test/distributed_persistence_control_commands_test.rb +0 -26
  86. data/test/distributed_publish_subscribe_test.rb +0 -92
  87. data/test/distributed_remote_server_control_commands_test.rb +0 -66
  88. data/test/distributed_scripting_test.rb +0 -102
  89. data/test/distributed_sorting_test.rb +0 -20
  90. data/test/distributed_test.rb +0 -58
  91. data/test/distributed_transactions_test.rb +0 -32
  92. data/test/encoding_test.rb +0 -18
  93. data/test/error_replies_test.rb +0 -59
  94. data/test/fork_safety_test.rb +0 -65
  95. data/test/helper.rb +0 -232
  96. data/test/helper_test.rb +0 -24
  97. data/test/internals_test.rb +0 -457
  98. data/test/lint/blocking_commands.rb +0 -150
  99. data/test/lint/hashes.rb +0 -162
  100. data/test/lint/hyper_log_log.rb +0 -60
  101. data/test/lint/lists.rb +0 -143
  102. data/test/lint/sets.rb +0 -140
  103. data/test/lint/sorted_sets.rb +0 -316
  104. data/test/lint/strings.rb +0 -260
  105. data/test/lint/value_types.rb +0 -122
  106. data/test/persistence_control_commands_test.rb +0 -26
  107. data/test/pipelining_commands_test.rb +0 -242
  108. data/test/publish_subscribe_test.rb +0 -282
  109. data/test/remote_server_control_commands_test.rb +0 -118
  110. data/test/scanning_test.rb +0 -413
  111. data/test/scripting_test.rb +0 -78
  112. data/test/sentinel_command_test.rb +0 -80
  113. data/test/sentinel_test.rb +0 -255
  114. data/test/sorting_test.rb +0 -59
  115. data/test/ssl_test.rb +0 -73
  116. data/test/support/connection/hiredis.rb +0 -1
  117. data/test/support/connection/ruby.rb +0 -1
  118. data/test/support/connection/synchrony.rb +0 -17
  119. data/test/support/redis_mock.rb +0 -130
  120. data/test/support/ssl/gen_certs.sh +0 -31
  121. data/test/support/ssl/trusted-ca.crt +0 -25
  122. data/test/support/ssl/trusted-ca.key +0 -27
  123. data/test/support/ssl/trusted-cert.crt +0 -81
  124. data/test/support/ssl/trusted-cert.key +0 -28
  125. data/test/support/ssl/untrusted-ca.crt +0 -26
  126. data/test/support/ssl/untrusted-ca.key +0 -27
  127. data/test/support/ssl/untrusted-cert.crt +0 -82
  128. data/test/support/ssl/untrusted-cert.key +0 -28
  129. data/test/support/wire/synchrony.rb +0 -24
  130. data/test/support/wire/thread.rb +0 -5
  131. data/test/synchrony_driver.rb +0 -88
  132. data/test/test.conf.erb +0 -9
  133. data/test/thread_safety_test.rb +0 -62
  134. data/test/transactions_test.rb +0 -264
  135. data/test/unknown_commands_test.rb +0 -14
  136. data/test/url_param_test.rb +0 -138
@@ -0,0 +1,437 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Redis
4
+ module Commands
5
+ module Keys
6
+ # Scan the keyspace
7
+ #
8
+ # @example Retrieve the first batch of keys
9
+ # redis.scan(0)
10
+ # # => ["4", ["key:21", "key:47", "key:42"]]
11
+ # @example Retrieve a batch of keys matching a pattern
12
+ # redis.scan(4, :match => "key:1?")
13
+ # # => ["92", ["key:13", "key:18"]]
14
+ # @example Retrieve a batch of keys of a certain type
15
+ # redis.scan(92, :type => "zset")
16
+ # # => ["173", ["sortedset:14", "sortedset:78"]]
17
+ #
18
+ # @param [String, Integer] cursor the cursor of the iteration
19
+ # @param [Hash] options
20
+ # - `:match => String`: only return keys matching the pattern
21
+ # - `:count => Integer`: return count keys at most per iteration
22
+ # - `:type => String`: return keys only of the given type
23
+ #
24
+ # @return [String, Array<String>] the next cursor and all found keys
25
+ def scan(cursor, **options)
26
+ _scan(:scan, cursor, [], **options)
27
+ end
28
+
29
+ # Scan the keyspace
30
+ #
31
+ # @example Retrieve all of the keys (with possible duplicates)
32
+ # redis.scan_each.to_a
33
+ # # => ["key:21", "key:47", "key:42"]
34
+ # @example Execute block for each key matching a pattern
35
+ # redis.scan_each(:match => "key:1?") {|key| puts key}
36
+ # # => key:13
37
+ # # => key:18
38
+ # @example Execute block for each key of a type
39
+ # redis.scan_each(:type => "hash") {|key| puts redis.type(key)}
40
+ # # => "hash"
41
+ # # => "hash"
42
+ #
43
+ # @param [Hash] options
44
+ # - `:match => String`: only return keys matching the pattern
45
+ # - `:count => Integer`: return count keys at most per iteration
46
+ # - `:type => String`: return keys only of the given type
47
+ #
48
+ # @return [Enumerator] an enumerator for all found keys
49
+ def scan_each(**options, &block)
50
+ return to_enum(:scan_each, **options) unless block_given?
51
+
52
+ cursor = 0
53
+ loop do
54
+ cursor, keys = scan(cursor, **options)
55
+ keys.each(&block)
56
+ break if cursor == "0"
57
+ end
58
+ end
59
+
60
+ # Remove the expiration from a key.
61
+ #
62
+ # @param [String] key
63
+ # @return [Boolean] whether the timeout was removed or not
64
+ def persist(key)
65
+ send_command([:persist, key], &Boolify)
66
+ end
67
+
68
+ # Set a key's time to live in seconds.
69
+ #
70
+ # @param [String] key
71
+ # @param [Integer] seconds time to live
72
+ # @param [Hash] options
73
+ # - `:nx => true`: Set expiry only when the key has no expiry.
74
+ # - `:xx => true`: Set expiry only when the key has an existing expiry.
75
+ # - `:gt => true`: Set expiry only when the new expiry is greater than current one.
76
+ # - `:lt => true`: Set expiry only when the new expiry is less than current one.
77
+ # @return [Boolean] whether the timeout was set or not
78
+ def expire(key, seconds, nx: nil, xx: nil, gt: nil, lt: nil)
79
+ args = [:expire, key, Integer(seconds)]
80
+ args << "NX" if nx
81
+ args << "XX" if xx
82
+ args << "GT" if gt
83
+ args << "LT" if lt
84
+
85
+ send_command(args, &Boolify)
86
+ end
87
+
88
+ # Set the expiration for a key as a UNIX timestamp.
89
+ #
90
+ # @param [String] key
91
+ # @param [Integer] unix_time expiry time specified as a UNIX timestamp
92
+ # @param [Hash] options
93
+ # - `:nx => true`: Set expiry only when the key has no expiry.
94
+ # - `:xx => true`: Set expiry only when the key has an existing expiry.
95
+ # - `:gt => true`: Set expiry only when the new expiry is greater than current one.
96
+ # - `:lt => true`: Set expiry only when the new expiry is less than current one.
97
+ # @return [Boolean] whether the timeout was set or not
98
+ def expireat(key, unix_time, nx: nil, xx: nil, gt: nil, lt: nil)
99
+ args = [:expireat, key, Integer(unix_time)]
100
+ args << "NX" if nx
101
+ args << "XX" if xx
102
+ args << "GT" if gt
103
+ args << "LT" if lt
104
+
105
+ send_command(args, &Boolify)
106
+ end
107
+
108
+ # Get the time to live (in seconds) for a key.
109
+ #
110
+ # @param [String] key
111
+ # @return [Integer] remaining time to live in seconds.
112
+ #
113
+ # In Redis 2.6 or older the command returns -1 if the key does not exist or if
114
+ # the key exist but has no associated expire.
115
+ #
116
+ # Starting with Redis 2.8 the return value in case of error changed:
117
+ #
118
+ # - The command returns -2 if the key does not exist.
119
+ # - The command returns -1 if the key exists but has no associated expire.
120
+ def ttl(key)
121
+ send_command([:ttl, key])
122
+ end
123
+
124
+ # Set a key's time to live in milliseconds.
125
+ #
126
+ # @param [String] key
127
+ # @param [Integer] milliseconds time to live
128
+ # @param [Hash] options
129
+ # - `:nx => true`: Set expiry only when the key has no expiry.
130
+ # - `:xx => true`: Set expiry only when the key has an existing expiry.
131
+ # - `:gt => true`: Set expiry only when the new expiry is greater than current one.
132
+ # - `:lt => true`: Set expiry only when the new expiry is less than current one.
133
+ # @return [Boolean] whether the timeout was set or not
134
+ def pexpire(key, milliseconds, nx: nil, xx: nil, gt: nil, lt: nil)
135
+ args = [:pexpire, key, Integer(milliseconds)]
136
+ args << "NX" if nx
137
+ args << "XX" if xx
138
+ args << "GT" if gt
139
+ args << "LT" if lt
140
+
141
+ send_command(args, &Boolify)
142
+ end
143
+
144
+ # Set the expiration for a key as number of milliseconds from UNIX Epoch.
145
+ #
146
+ # @param [String] key
147
+ # @param [Integer] ms_unix_time expiry time specified as number of milliseconds from UNIX Epoch.
148
+ # @param [Hash] options
149
+ # - `:nx => true`: Set expiry only when the key has no expiry.
150
+ # - `:xx => true`: Set expiry only when the key has an existing expiry.
151
+ # - `:gt => true`: Set expiry only when the new expiry is greater than current one.
152
+ # - `:lt => true`: Set expiry only when the new expiry is less than current one.
153
+ # @return [Boolean] whether the timeout was set or not
154
+ def pexpireat(key, ms_unix_time, nx: nil, xx: nil, gt: nil, lt: nil)
155
+ args = [:pexpireat, key, Integer(ms_unix_time)]
156
+ args << "NX" if nx
157
+ args << "XX" if xx
158
+ args << "GT" if gt
159
+ args << "LT" if lt
160
+
161
+ send_command(args, &Boolify)
162
+ end
163
+
164
+ # Get the time to live (in milliseconds) for a key.
165
+ #
166
+ # @param [String] key
167
+ # @return [Integer] remaining time to live in milliseconds
168
+ # In Redis 2.6 or older the command returns -1 if the key does not exist or if
169
+ # the key exist but has no associated expire.
170
+ #
171
+ # Starting with Redis 2.8 the return value in case of error changed:
172
+ #
173
+ # - The command returns -2 if the key does not exist.
174
+ # - The command returns -1 if the key exists but has no associated expire.
175
+ def pttl(key)
176
+ send_command([:pttl, key])
177
+ end
178
+
179
+ # Return a serialized version of the value stored at a key.
180
+ #
181
+ # @param [String] key
182
+ # @return [String] serialized_value
183
+ def dump(key)
184
+ send_command([:dump, key])
185
+ end
186
+
187
+ # Create a key using the serialized value, previously obtained using DUMP.
188
+ #
189
+ # @param [String] key
190
+ # @param [String] ttl
191
+ # @param [String] serialized_value
192
+ # @param [Hash] options
193
+ # - `:replace => Boolean`: if false, raises an error if key already exists
194
+ # @raise [Redis::CommandError]
195
+ # @return [String] `"OK"`
196
+ def restore(key, ttl, serialized_value, replace: nil)
197
+ args = [:restore, key, ttl, serialized_value]
198
+ args << 'REPLACE' if replace
199
+
200
+ send_command(args)
201
+ end
202
+
203
+ # Transfer a key from the connected instance to another instance.
204
+ #
205
+ # @param [String, Array<String>] key
206
+ # @param [Hash] options
207
+ # - `:host => String`: host of instance to migrate to
208
+ # - `:port => Integer`: port of instance to migrate to
209
+ # - `:db => Integer`: database to migrate to (default: same as source)
210
+ # - `:timeout => Integer`: timeout (default: same as connection timeout)
211
+ # - `:copy => Boolean`: Do not remove the key from the local instance.
212
+ # - `:replace => Boolean`: Replace existing key on the remote instance.
213
+ # @return [String] `"OK"`
214
+ def migrate(key, options)
215
+ args = [:migrate]
216
+ args << (options[:host] || raise(':host not specified'))
217
+ args << (options[:port] || raise(':port not specified'))
218
+ args << (key.is_a?(String) ? key : '')
219
+ args << (options[:db] || @client.db).to_i
220
+ args << (options[:timeout] || @client.timeout).to_i
221
+ args << 'COPY' if options[:copy]
222
+ args << 'REPLACE' if options[:replace]
223
+ args += ['KEYS', *key] if key.is_a?(Array)
224
+
225
+ send_command(args)
226
+ end
227
+
228
+ # Delete one or more keys.
229
+ #
230
+ # @param [String, Array<String>] keys
231
+ # @return [Integer] number of keys that were deleted
232
+ def del(*keys)
233
+ keys.flatten!(1)
234
+ return 0 if keys.empty?
235
+
236
+ send_command([:del] + keys)
237
+ end
238
+
239
+ # Unlink one or more keys.
240
+ #
241
+ # @param [String, Array<String>] keys
242
+ # @return [Integer] number of keys that were unlinked
243
+ def unlink(*keys)
244
+ send_command([:unlink] + keys)
245
+ end
246
+
247
+ # Determine how many of the keys exists.
248
+ #
249
+ # @param [String, Array<String>] keys
250
+ # @return [Integer]
251
+ def exists(*keys)
252
+ send_command([:exists, *keys])
253
+ end
254
+
255
+ # Determine if any of the keys exists.
256
+ #
257
+ # @param [String, Array<String>] keys
258
+ # @return [Boolean]
259
+ def exists?(*keys)
260
+ send_command([:exists, *keys]) do |value|
261
+ value > 0
262
+ end
263
+ end
264
+
265
+ # Find all keys matching the given pattern.
266
+ #
267
+ # @param [String] pattern
268
+ # @return [Array<String>]
269
+ def keys(pattern = "*")
270
+ send_command([:keys, pattern]) do |reply|
271
+ if reply.is_a?(String)
272
+ reply.split(" ")
273
+ else
274
+ reply
275
+ end
276
+ end
277
+ end
278
+
279
+ # Move a key to another database.
280
+ #
281
+ # @example Move a key to another database
282
+ # redis.set "foo", "bar"
283
+ # # => "OK"
284
+ # redis.move "foo", 2
285
+ # # => true
286
+ # redis.exists "foo"
287
+ # # => false
288
+ # redis.select 2
289
+ # # => "OK"
290
+ # redis.exists "foo"
291
+ # # => true
292
+ # redis.get "foo"
293
+ # # => "bar"
294
+ #
295
+ # @param [String] key
296
+ # @param [Integer] db
297
+ # @return [Boolean] whether the key was moved or not
298
+ def move(key, db)
299
+ send_command([:move, key, db], &Boolify)
300
+ end
301
+
302
+ # Copy a value from one key to another.
303
+ #
304
+ # @example Copy a value to another key
305
+ # redis.set "foo", "value"
306
+ # # => "OK"
307
+ # redis.copy "foo", "bar"
308
+ # # => true
309
+ # redis.get "bar"
310
+ # # => "value"
311
+ #
312
+ # @example Copy a value to a key in another database
313
+ # redis.set "foo", "value"
314
+ # # => "OK"
315
+ # redis.copy "foo", "bar", db: 2
316
+ # # => true
317
+ # redis.select 2
318
+ # # => "OK"
319
+ # redis.get "bar"
320
+ # # => "value"
321
+ #
322
+ # @param [String] source
323
+ # @param [String] destination
324
+ # @param [Integer] db
325
+ # @param [Boolean] replace removes the `destination` key before copying value to it
326
+ # @return [Boolean] whether the key was copied or not
327
+ def copy(source, destination, db: nil, replace: false)
328
+ command = [:copy, source, destination]
329
+ command << "DB" << db if db
330
+ command << "REPLACE" if replace
331
+
332
+ send_command(command, &Boolify)
333
+ end
334
+
335
+ def object(*args)
336
+ send_command([:object] + args)
337
+ end
338
+
339
+ # Return a random key from the keyspace.
340
+ #
341
+ # @return [String]
342
+ def randomkey
343
+ send_command([:randomkey])
344
+ end
345
+
346
+ # Rename a key. If the new key already exists it is overwritten.
347
+ #
348
+ # @param [String] old_name
349
+ # @param [String] new_name
350
+ # @return [String] `OK`
351
+ def rename(old_name, new_name)
352
+ send_command([:rename, old_name, new_name])
353
+ end
354
+
355
+ # Rename a key, only if the new key does not exist.
356
+ #
357
+ # @param [String] old_name
358
+ # @param [String] new_name
359
+ # @return [Boolean] whether the key was renamed or not
360
+ def renamenx(old_name, new_name)
361
+ send_command([:renamenx, old_name, new_name], &Boolify)
362
+ end
363
+
364
+ # Sort the elements in a list, set or sorted set.
365
+ #
366
+ # @example Retrieve the first 2 elements from an alphabetically sorted "list"
367
+ # redis.sort("list", :order => "alpha", :limit => [0, 2])
368
+ # # => ["a", "b"]
369
+ # @example Store an alphabetically descending list in "target"
370
+ # redis.sort("list", :order => "desc alpha", :store => "target")
371
+ # # => 26
372
+ #
373
+ # @param [String] key
374
+ # @param [Hash] options
375
+ # - `:by => String`: use external key to sort elements by
376
+ # - `:limit => [offset, count]`: skip `offset` elements, return a maximum
377
+ # of `count` elements
378
+ # - `:get => [String, Array<String>]`: single key or array of keys to
379
+ # retrieve per element in the result
380
+ # - `:order => String`: combination of `ASC`, `DESC` and optionally `ALPHA`
381
+ # - `:store => String`: key to store the result at
382
+ #
383
+ # @return [Array<String>, Array<Array<String>>, Integer]
384
+ # - when `:get` is not specified, or holds a single element, an array of elements
385
+ # - when `:get` is specified, and holds more than one element, an array of
386
+ # elements where every element is an array with the result for every
387
+ # element specified in `:get`
388
+ # - when `:store` is specified, the number of elements in the stored result
389
+ def sort(key, by: nil, limit: nil, get: nil, order: nil, store: nil)
390
+ args = [:sort, key]
391
+ args << "BY" << by if by
392
+
393
+ if limit
394
+ args << "LIMIT"
395
+ args.concat(limit)
396
+ end
397
+
398
+ get = Array(get)
399
+ get.each do |item|
400
+ args << "GET" << item
401
+ end
402
+
403
+ args.concat(order.split(" ")) if order
404
+ args << "STORE" << store if store
405
+
406
+ send_command(args) do |reply|
407
+ if get.size > 1 && !store
408
+ reply.each_slice(get.size).to_a if reply
409
+ else
410
+ reply
411
+ end
412
+ end
413
+ end
414
+
415
+ # Determine the type stored at key.
416
+ #
417
+ # @param [String] key
418
+ # @return [String] `string`, `list`, `set`, `zset`, `hash` or `none`
419
+ def type(key)
420
+ send_command([:type, key])
421
+ end
422
+
423
+ private
424
+
425
+ def _scan(command, cursor, args, match: nil, count: nil, type: nil, &block)
426
+ # SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
427
+
428
+ args << cursor
429
+ args << "MATCH" << match if match
430
+ args << "COUNT" << Integer(count) if count
431
+ args << "TYPE" << type if type
432
+
433
+ send_command([command] + args, &block)
434
+ end
435
+ end
436
+ end
437
+ end