redis 3.3.5 → 5.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +290 -2
  3. data/README.md +146 -146
  4. data/lib/redis/client.rb +79 -541
  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 +339 -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 +884 -0
  18. data/lib/redis/commands/streams.rb +402 -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 +237 -0
  22. data/lib/redis/distributed.rb +328 -108
  23. data/lib/redis/errors.rb +23 -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 +115 -2695
  29. metadata +38 -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/connection_test.rb +0 -57
  73. data/test/db/.gitkeep +0 -0
  74. data/test/distributed_blocking_commands_test.rb +0 -46
  75. data/test/distributed_commands_on_hashes_test.rb +0 -10
  76. data/test/distributed_commands_on_hyper_log_log_test.rb +0 -33
  77. data/test/distributed_commands_on_lists_test.rb +0 -22
  78. data/test/distributed_commands_on_sets_test.rb +0 -83
  79. data/test/distributed_commands_on_sorted_sets_test.rb +0 -18
  80. data/test/distributed_commands_on_strings_test.rb +0 -59
  81. data/test/distributed_commands_on_value_types_test.rb +0 -95
  82. data/test/distributed_commands_requiring_clustering_test.rb +0 -164
  83. data/test/distributed_connection_handling_test.rb +0 -23
  84. data/test/distributed_internals_test.rb +0 -79
  85. data/test/distributed_key_tags_test.rb +0 -52
  86. data/test/distributed_persistence_control_commands_test.rb +0 -26
  87. data/test/distributed_publish_subscribe_test.rb +0 -92
  88. data/test/distributed_remote_server_control_commands_test.rb +0 -66
  89. data/test/distributed_scripting_test.rb +0 -102
  90. data/test/distributed_sorting_test.rb +0 -20
  91. data/test/distributed_test.rb +0 -58
  92. data/test/distributed_transactions_test.rb +0 -32
  93. data/test/encoding_test.rb +0 -18
  94. data/test/error_replies_test.rb +0 -59
  95. data/test/fork_safety_test.rb +0 -65
  96. data/test/helper.rb +0 -232
  97. data/test/helper_test.rb +0 -24
  98. data/test/internals_test.rb +0 -417
  99. data/test/lint/blocking_commands.rb +0 -150
  100. data/test/lint/hashes.rb +0 -162
  101. data/test/lint/hyper_log_log.rb +0 -60
  102. data/test/lint/lists.rb +0 -143
  103. data/test/lint/sets.rb +0 -140
  104. data/test/lint/sorted_sets.rb +0 -316
  105. data/test/lint/strings.rb +0 -260
  106. data/test/lint/value_types.rb +0 -122
  107. data/test/persistence_control_commands_test.rb +0 -26
  108. data/test/pipelining_commands_test.rb +0 -242
  109. data/test/publish_subscribe_test.rb +0 -282
  110. data/test/remote_server_control_commands_test.rb +0 -118
  111. data/test/scanning_test.rb +0 -413
  112. data/test/scripting_test.rb +0 -78
  113. data/test/sentinel_command_test.rb +0 -80
  114. data/test/sentinel_test.rb +0 -255
  115. data/test/sorting_test.rb +0 -59
  116. data/test/ssl_test.rb +0 -73
  117. data/test/support/connection/hiredis.rb +0 -1
  118. data/test/support/connection/ruby.rb +0 -1
  119. data/test/support/connection/synchrony.rb +0 -17
  120. data/test/support/redis_mock.rb +0 -130
  121. data/test/support/ssl/gen_certs.sh +0 -31
  122. data/test/support/ssl/trusted-ca.crt +0 -25
  123. data/test/support/ssl/trusted-ca.key +0 -27
  124. data/test/support/ssl/trusted-cert.crt +0 -81
  125. data/test/support/ssl/trusted-cert.key +0 -28
  126. data/test/support/ssl/untrusted-ca.crt +0 -26
  127. data/test/support/ssl/untrusted-ca.key +0 -27
  128. data/test/support/ssl/untrusted-cert.crt +0 -82
  129. data/test/support/ssl/untrusted-cert.key +0 -28
  130. data/test/support/wire/synchrony.rb +0 -24
  131. data/test/support/wire/thread.rb +0 -5
  132. data/test/synchrony_driver.rb +0 -88
  133. data/test/test.conf.erb +0 -9
  134. data/test/thread_safety_test.rb +0 -62
  135. data/test/transactions_test.rb +0 -264
  136. data/test/unknown_commands_test.rb +0 -14
  137. 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