redis 3.3.5 → 4.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +236 -2
  3. data/README.md +169 -89
  4. data/lib/redis/client.rb +176 -108
  5. data/lib/redis/cluster/command.rb +79 -0
  6. data/lib/redis/cluster/command_loader.rb +33 -0
  7. data/lib/redis/cluster/key_slot_converter.rb +72 -0
  8. data/lib/redis/cluster/node.rb +120 -0
  9. data/lib/redis/cluster/node_key.rb +31 -0
  10. data/lib/redis/cluster/node_loader.rb +34 -0
  11. data/lib/redis/cluster/option.rb +100 -0
  12. data/lib/redis/cluster/slot.rb +86 -0
  13. data/lib/redis/cluster/slot_loader.rb +46 -0
  14. data/lib/redis/cluster.rb +315 -0
  15. data/lib/redis/commands/bitmaps.rb +63 -0
  16. data/lib/redis/commands/cluster.rb +45 -0
  17. data/lib/redis/commands/connection.rb +58 -0
  18. data/lib/redis/commands/geo.rb +84 -0
  19. data/lib/redis/commands/hashes.rb +251 -0
  20. data/lib/redis/commands/hyper_log_log.rb +37 -0
  21. data/lib/redis/commands/keys.rb +455 -0
  22. data/lib/redis/commands/lists.rb +290 -0
  23. data/lib/redis/commands/pubsub.rb +72 -0
  24. data/lib/redis/commands/scripting.rb +114 -0
  25. data/lib/redis/commands/server.rb +188 -0
  26. data/lib/redis/commands/sets.rb +223 -0
  27. data/lib/redis/commands/sorted_sets.rb +812 -0
  28. data/lib/redis/commands/streams.rb +382 -0
  29. data/lib/redis/commands/strings.rb +313 -0
  30. data/lib/redis/commands/transactions.rb +139 -0
  31. data/lib/redis/commands.rb +240 -0
  32. data/lib/redis/connection/command_helper.rb +7 -10
  33. data/lib/redis/connection/hiredis.rb +5 -3
  34. data/lib/redis/connection/registry.rb +2 -1
  35. data/lib/redis/connection/ruby.rb +136 -128
  36. data/lib/redis/connection/synchrony.rb +24 -9
  37. data/lib/redis/connection.rb +3 -1
  38. data/lib/redis/distributed.rb +255 -85
  39. data/lib/redis/errors.rb +57 -0
  40. data/lib/redis/hash_ring.rb +30 -73
  41. data/lib/redis/pipeline.rb +178 -13
  42. data/lib/redis/subscribe.rb +11 -12
  43. data/lib/redis/version.rb +3 -1
  44. data/lib/redis.rb +174 -2661
  45. metadata +66 -202
  46. data/.gitignore +0 -16
  47. data/.travis/Gemfile +0 -11
  48. data/.travis.yml +0 -89
  49. data/.yardopts +0 -3
  50. data/Gemfile +0 -4
  51. data/Rakefile +0 -87
  52. data/benchmarking/logging.rb +0 -71
  53. data/benchmarking/pipeline.rb +0 -51
  54. data/benchmarking/speed.rb +0 -21
  55. data/benchmarking/suite.rb +0 -24
  56. data/benchmarking/worker.rb +0 -71
  57. data/examples/basic.rb +0 -15
  58. data/examples/consistency.rb +0 -114
  59. data/examples/dist_redis.rb +0 -43
  60. data/examples/incr-decr.rb +0 -17
  61. data/examples/list.rb +0 -26
  62. data/examples/pubsub.rb +0 -37
  63. data/examples/sentinel/sentinel.conf +0 -9
  64. data/examples/sentinel/start +0 -49
  65. data/examples/sentinel.rb +0 -41
  66. data/examples/sets.rb +0 -36
  67. data/examples/unicorn/config.ru +0 -3
  68. data/examples/unicorn/unicorn.rb +0 -20
  69. data/redis.gemspec +0 -44
  70. data/test/bitpos_test.rb +0 -69
  71. data/test/blocking_commands_test.rb +0 -42
  72. data/test/client_test.rb +0 -59
  73. data/test/command_map_test.rb +0 -30
  74. data/test/commands_on_hashes_test.rb +0 -21
  75. data/test/commands_on_hyper_log_log_test.rb +0 -21
  76. data/test/commands_on_lists_test.rb +0 -20
  77. data/test/commands_on_sets_test.rb +0 -77
  78. data/test/commands_on_sorted_sets_test.rb +0 -137
  79. data/test/commands_on_strings_test.rb +0 -101
  80. data/test/commands_on_value_types_test.rb +0 -133
  81. data/test/connection_handling_test.rb +0 -277
  82. data/test/connection_test.rb +0 -57
  83. data/test/db/.gitkeep +0 -0
  84. data/test/distributed_blocking_commands_test.rb +0 -46
  85. data/test/distributed_commands_on_hashes_test.rb +0 -10
  86. data/test/distributed_commands_on_hyper_log_log_test.rb +0 -33
  87. data/test/distributed_commands_on_lists_test.rb +0 -22
  88. data/test/distributed_commands_on_sets_test.rb +0 -83
  89. data/test/distributed_commands_on_sorted_sets_test.rb +0 -18
  90. data/test/distributed_commands_on_strings_test.rb +0 -59
  91. data/test/distributed_commands_on_value_types_test.rb +0 -95
  92. data/test/distributed_commands_requiring_clustering_test.rb +0 -164
  93. data/test/distributed_connection_handling_test.rb +0 -23
  94. data/test/distributed_internals_test.rb +0 -79
  95. data/test/distributed_key_tags_test.rb +0 -52
  96. data/test/distributed_persistence_control_commands_test.rb +0 -26
  97. data/test/distributed_publish_subscribe_test.rb +0 -92
  98. data/test/distributed_remote_server_control_commands_test.rb +0 -66
  99. data/test/distributed_scripting_test.rb +0 -102
  100. data/test/distributed_sorting_test.rb +0 -20
  101. data/test/distributed_test.rb +0 -58
  102. data/test/distributed_transactions_test.rb +0 -32
  103. data/test/encoding_test.rb +0 -18
  104. data/test/error_replies_test.rb +0 -59
  105. data/test/fork_safety_test.rb +0 -65
  106. data/test/helper.rb +0 -232
  107. data/test/helper_test.rb +0 -24
  108. data/test/internals_test.rb +0 -417
  109. data/test/lint/blocking_commands.rb +0 -150
  110. data/test/lint/hashes.rb +0 -162
  111. data/test/lint/hyper_log_log.rb +0 -60
  112. data/test/lint/lists.rb +0 -143
  113. data/test/lint/sets.rb +0 -140
  114. data/test/lint/sorted_sets.rb +0 -316
  115. data/test/lint/strings.rb +0 -260
  116. data/test/lint/value_types.rb +0 -122
  117. data/test/persistence_control_commands_test.rb +0 -26
  118. data/test/pipelining_commands_test.rb +0 -242
  119. data/test/publish_subscribe_test.rb +0 -282
  120. data/test/remote_server_control_commands_test.rb +0 -118
  121. data/test/scanning_test.rb +0 -413
  122. data/test/scripting_test.rb +0 -78
  123. data/test/sentinel_command_test.rb +0 -80
  124. data/test/sentinel_test.rb +0 -255
  125. data/test/sorting_test.rb +0 -59
  126. data/test/ssl_test.rb +0 -73
  127. data/test/support/connection/hiredis.rb +0 -1
  128. data/test/support/connection/ruby.rb +0 -1
  129. data/test/support/connection/synchrony.rb +0 -17
  130. data/test/support/redis_mock.rb +0 -130
  131. data/test/support/ssl/gen_certs.sh +0 -31
  132. data/test/support/ssl/trusted-ca.crt +0 -25
  133. data/test/support/ssl/trusted-ca.key +0 -27
  134. data/test/support/ssl/trusted-cert.crt +0 -81
  135. data/test/support/ssl/trusted-cert.key +0 -28
  136. data/test/support/ssl/untrusted-ca.crt +0 -26
  137. data/test/support/ssl/untrusted-ca.key +0 -27
  138. data/test/support/ssl/untrusted-cert.crt +0 -82
  139. data/test/support/ssl/untrusted-cert.key +0 -28
  140. data/test/support/wire/synchrony.rb +0 -24
  141. data/test/support/wire/thread.rb +0 -5
  142. data/test/synchrony_driver.rb +0 -88
  143. data/test/test.conf.erb +0 -9
  144. data/test/thread_safety_test.rb +0 -62
  145. data/test/transactions_test.rb +0 -264
  146. data/test/unknown_commands_test.rb +0 -14
  147. data/test/url_param_test.rb +0 -138
@@ -0,0 +1,290 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Redis
4
+ module Commands
5
+ module Lists
6
+ # Get the length of a list.
7
+ #
8
+ # @param [String] key
9
+ # @return [Integer]
10
+ def llen(key)
11
+ send_command([:llen, key])
12
+ end
13
+
14
+ # Remove the first/last element in a list, append/prepend it to another list and return it.
15
+ #
16
+ # @param [String] source source key
17
+ # @param [String] destination destination key
18
+ # @param [String, Symbol] where_source from where to remove the element from the source list
19
+ # e.g. 'LEFT' - from head, 'RIGHT' - from tail
20
+ # @param [String, Symbol] where_destination where to push the element to the source list
21
+ # e.g. 'LEFT' - to head, 'RIGHT' - to tail
22
+ #
23
+ # @return [nil, String] the element, or nil when the source key does not exist
24
+ #
25
+ # @note This command comes in place of the now deprecated RPOPLPUSH.
26
+ # Doing LMOVE RIGHT LEFT is equivalent.
27
+ def lmove(source, destination, where_source, where_destination)
28
+ where_source, where_destination = _normalize_move_wheres(where_source, where_destination)
29
+
30
+ send_command([:lmove, source, destination, where_source, where_destination])
31
+ end
32
+
33
+ # Remove the first/last element in a list and append/prepend it
34
+ # to another list and return it, or block until one is available.
35
+ #
36
+ # @example With timeout
37
+ # element = redis.blmove("foo", "bar", "LEFT", "RIGHT", timeout: 5)
38
+ # # => nil on timeout
39
+ # # => "element" on success
40
+ # @example Without timeout
41
+ # element = redis.blmove("foo", "bar", "LEFT", "RIGHT")
42
+ # # => "element"
43
+ #
44
+ # @param [String] source source key
45
+ # @param [String] destination destination key
46
+ # @param [String, Symbol] where_source from where to remove the element from the source list
47
+ # e.g. 'LEFT' - from head, 'RIGHT' - from tail
48
+ # @param [String, Symbol] where_destination where to push the element to the source list
49
+ # e.g. 'LEFT' - to head, 'RIGHT' - to tail
50
+ # @param [Hash] options
51
+ # - `:timeout => Numeric`: timeout in seconds, defaults to no timeout
52
+ #
53
+ # @return [nil, String] the element, or nil when the source key does not exist or the timeout expired
54
+ #
55
+ def blmove(source, destination, where_source, where_destination, timeout: 0)
56
+ where_source, where_destination = _normalize_move_wheres(where_source, where_destination)
57
+
58
+ command = [:blmove, source, destination, where_source, where_destination, timeout]
59
+ send_blocking_command(command, timeout)
60
+ end
61
+
62
+ # Prepend one or more values to a list, creating the list if it doesn't exist
63
+ #
64
+ # @param [String] key
65
+ # @param [String, Array<String>] value string value, or array of string values to push
66
+ # @return [Integer] the length of the list after the push operation
67
+ def lpush(key, value)
68
+ send_command([:lpush, key, value])
69
+ end
70
+
71
+ # Prepend a value to a list, only if the list exists.
72
+ #
73
+ # @param [String] key
74
+ # @param [String] value
75
+ # @return [Integer] the length of the list after the push operation
76
+ def lpushx(key, value)
77
+ send_command([:lpushx, key, value])
78
+ end
79
+
80
+ # Append one or more values to a list, creating the list if it doesn't exist
81
+ #
82
+ # @param [String] key
83
+ # @param [String, Array<String>] value string value, or array of string values to push
84
+ # @return [Integer] the length of the list after the push operation
85
+ def rpush(key, value)
86
+ send_command([:rpush, key, value])
87
+ end
88
+
89
+ # Append a value to a list, only if the list exists.
90
+ #
91
+ # @param [String] key
92
+ # @param [String] value
93
+ # @return [Integer] the length of the list after the push operation
94
+ def rpushx(key, value)
95
+ send_command([:rpushx, key, value])
96
+ end
97
+
98
+ # Remove and get the first elements in a list.
99
+ #
100
+ # @param [String] key
101
+ # @param [Integer] count number of elements to remove
102
+ # @return [String, Array<String>] the values of the first elements
103
+ def lpop(key, count = nil)
104
+ command = [:lpop, key]
105
+ command << count if count
106
+ send_command(command)
107
+ end
108
+
109
+ # Remove and get the last elements in a list.
110
+ #
111
+ # @param [String] key
112
+ # @param [Integer] count number of elements to remove
113
+ # @return [String, Array<String>] the values of the last elements
114
+ def rpop(key, count = nil)
115
+ command = [:rpop, key]
116
+ command << count if count
117
+ send_command(command)
118
+ end
119
+
120
+ # Remove the last element in a list, append it to another list and return it.
121
+ #
122
+ # @param [String] source source key
123
+ # @param [String] destination destination key
124
+ # @return [nil, String] the element, or nil when the source key does not exist
125
+ def rpoplpush(source, destination)
126
+ send_command([:rpoplpush, source, destination])
127
+ end
128
+
129
+ # Remove and get the first element in a list, or block until one is available.
130
+ #
131
+ # @example With timeout
132
+ # list, element = redis.blpop("list", :timeout => 5)
133
+ # # => nil on timeout
134
+ # # => ["list", "element"] on success
135
+ # @example Without timeout
136
+ # list, element = redis.blpop("list")
137
+ # # => ["list", "element"]
138
+ # @example Blocking pop on multiple lists
139
+ # list, element = redis.blpop(["list", "another_list"])
140
+ # # => ["list", "element"]
141
+ #
142
+ # @param [String, Array<String>] keys one or more keys to perform the
143
+ # blocking pop on
144
+ # @param [Hash] options
145
+ # - `:timeout => Integer`: timeout in seconds, defaults to no timeout
146
+ #
147
+ # @return [nil, [String, String]]
148
+ # - `nil` when the operation timed out
149
+ # - tuple of the list that was popped from and element was popped otherwise
150
+ def blpop(*args)
151
+ _bpop(:blpop, args)
152
+ end
153
+
154
+ # Remove and get the last element in a list, or block until one is available.
155
+ #
156
+ # @param [String, Array<String>] keys one or more keys to perform the
157
+ # blocking pop on
158
+ # @param [Hash] options
159
+ # - `:timeout => Integer`: timeout in seconds, defaults to no timeout
160
+ #
161
+ # @return [nil, [String, String]]
162
+ # - `nil` when the operation timed out
163
+ # - tuple of the list that was popped from and element was popped otherwise
164
+ #
165
+ # @see #blpop
166
+ def brpop(*args)
167
+ _bpop(:brpop, args)
168
+ end
169
+
170
+ # Pop a value from a list, push it to another list and return it; or block
171
+ # until one is available.
172
+ #
173
+ # @param [String] source source key
174
+ # @param [String] destination destination key
175
+ # @param [Hash] options
176
+ # - `:timeout => Integer`: timeout in seconds, defaults to no timeout
177
+ #
178
+ # @return [nil, String]
179
+ # - `nil` when the operation timed out
180
+ # - the element was popped and pushed otherwise
181
+ def brpoplpush(source, destination, deprecated_timeout = 0, timeout: deprecated_timeout)
182
+ command = [:brpoplpush, source, destination, timeout]
183
+ send_blocking_command(command, timeout)
184
+ end
185
+
186
+ # Get an element from a list by its index.
187
+ #
188
+ # @param [String] key
189
+ # @param [Integer] index
190
+ # @return [String]
191
+ def lindex(key, index)
192
+ send_command([:lindex, key, index])
193
+ end
194
+
195
+ # Insert an element before or after another element in a list.
196
+ #
197
+ # @param [String] key
198
+ # @param [String, Symbol] where `BEFORE` or `AFTER`
199
+ # @param [String] pivot reference element
200
+ # @param [String] value
201
+ # @return [Integer] length of the list after the insert operation, or `-1`
202
+ # when the element `pivot` was not found
203
+ def linsert(key, where, pivot, value)
204
+ send_command([:linsert, key, where, pivot, value])
205
+ end
206
+
207
+ # Get a range of elements from a list.
208
+ #
209
+ # @param [String] key
210
+ # @param [Integer] start start index
211
+ # @param [Integer] stop stop index
212
+ # @return [Array<String>]
213
+ def lrange(key, start, stop)
214
+ send_command([:lrange, key, start, stop])
215
+ end
216
+
217
+ # Remove elements from a list.
218
+ #
219
+ # @param [String] key
220
+ # @param [Integer] count number of elements to remove. Use a positive
221
+ # value to remove the first `count` occurrences of `value`. A negative
222
+ # value to remove the last `count` occurrences of `value`. Or zero, to
223
+ # remove all occurrences of `value` from the list.
224
+ # @param [String] value
225
+ # @return [Integer] the number of removed elements
226
+ def lrem(key, count, value)
227
+ send_command([:lrem, key, count, value])
228
+ end
229
+
230
+ # Set the value of an element in a list by its index.
231
+ #
232
+ # @param [String] key
233
+ # @param [Integer] index
234
+ # @param [String] value
235
+ # @return [String] `OK`
236
+ def lset(key, index, value)
237
+ send_command([:lset, key, index, value])
238
+ end
239
+
240
+ # Trim a list to the specified range.
241
+ #
242
+ # @param [String] key
243
+ # @param [Integer] start start index
244
+ # @param [Integer] stop stop index
245
+ # @return [String] `OK`
246
+ def ltrim(key, start, stop)
247
+ send_command([:ltrim, key, start, stop])
248
+ end
249
+
250
+ private
251
+
252
+ def _bpop(cmd, args, &blk)
253
+ timeout = if args.last.is_a?(Hash)
254
+ options = args.pop
255
+ options[:timeout]
256
+ elsif args.last.respond_to?(:to_int)
257
+ last_arg = args.pop
258
+ ::Redis.deprecate!(
259
+ "Passing the timeout as a positional argument is deprecated, it should be passed as a keyword argument:\n" \
260
+ " redis.#{cmd}(#{args.map(&:inspect).join(', ')}, timeout: #{last_arg.to_int})" \
261
+ "(called from: #{caller(2, 1).first})"
262
+ )
263
+ last_arg.to_int
264
+ end
265
+
266
+ timeout ||= 0
267
+
268
+ keys = args.flatten
269
+
270
+ command = [cmd, keys, timeout]
271
+ send_blocking_command(command, timeout, &blk)
272
+ end
273
+
274
+ def _normalize_move_wheres(where_source, where_destination)
275
+ where_source = where_source.to_s.upcase
276
+ where_destination = where_destination.to_s.upcase
277
+
278
+ if where_source != "LEFT" && where_source != "RIGHT"
279
+ raise ArgumentError, "where_source must be 'LEFT' or 'RIGHT'"
280
+ end
281
+
282
+ if where_destination != "LEFT" && where_destination != "RIGHT"
283
+ raise ArgumentError, "where_destination must be 'LEFT' or 'RIGHT'"
284
+ end
285
+
286
+ [where_source, where_destination]
287
+ end
288
+ end
289
+ end
290
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Redis
4
+ module Commands
5
+ module Pubsub
6
+ # Post a message to a channel.
7
+ def publish(channel, message)
8
+ send_command([:publish, channel, message])
9
+ end
10
+
11
+ def subscribed?
12
+ synchronize do |client|
13
+ client.is_a? SubscribedClient
14
+ end
15
+ end
16
+
17
+ # Listen for messages published to the given channels.
18
+ def subscribe(*channels, &block)
19
+ synchronize do |_client|
20
+ _subscription(:subscribe, 0, channels, block)
21
+ end
22
+ end
23
+
24
+ # Listen for messages published to the given channels. Throw a timeout error
25
+ # if there is no messages for a timeout period.
26
+ def subscribe_with_timeout(timeout, *channels, &block)
27
+ synchronize do |_client|
28
+ _subscription(:subscribe_with_timeout, timeout, channels, block)
29
+ end
30
+ end
31
+
32
+ # Stop listening for messages posted to the given channels.
33
+ def unsubscribe(*channels)
34
+ synchronize do |client|
35
+ raise "Can't unsubscribe if not subscribed." unless subscribed?
36
+
37
+ client.unsubscribe(*channels)
38
+ end
39
+ end
40
+
41
+ # Listen for messages published to channels matching the given patterns.
42
+ def psubscribe(*channels, &block)
43
+ synchronize do |_client|
44
+ _subscription(:psubscribe, 0, channels, block)
45
+ end
46
+ end
47
+
48
+ # Listen for messages published to channels matching the given patterns.
49
+ # Throw a timeout error if there is no messages for a timeout period.
50
+ def psubscribe_with_timeout(timeout, *channels, &block)
51
+ synchronize do |_client|
52
+ _subscription(:psubscribe_with_timeout, timeout, channels, block)
53
+ end
54
+ end
55
+
56
+ # Stop listening for messages posted to channels matching the given patterns.
57
+ def punsubscribe(*channels)
58
+ synchronize do |client|
59
+ raise "Can't unsubscribe if not subscribed." unless subscribed?
60
+
61
+ client.punsubscribe(*channels)
62
+ end
63
+ end
64
+
65
+ # Inspect the state of the Pub/Sub subsystem.
66
+ # Possible subcommands: channels, numsub, numpat.
67
+ def pubsub(subcommand, *args)
68
+ send_command([:pubsub, subcommand] + args)
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Redis
4
+ module Commands
5
+ module Scripting
6
+ # Control remote script registry.
7
+ #
8
+ # @example Load a script
9
+ # sha = redis.script(:load, "return 1")
10
+ # # => <sha of this script>
11
+ # @example Check if a script exists
12
+ # redis.script(:exists, sha)
13
+ # # => true
14
+ # @example Check if multiple scripts exist
15
+ # redis.script(:exists, [sha, other_sha])
16
+ # # => [true, false]
17
+ # @example Flush the script registry
18
+ # redis.script(:flush)
19
+ # # => "OK"
20
+ # @example Kill a running script
21
+ # redis.script(:kill)
22
+ # # => "OK"
23
+ #
24
+ # @param [String] subcommand e.g. `exists`, `flush`, `load`, `kill`
25
+ # @param [Array<String>] args depends on subcommand
26
+ # @return [String, Boolean, Array<Boolean>, ...] depends on subcommand
27
+ #
28
+ # @see #eval
29
+ # @see #evalsha
30
+ def script(subcommand, *args)
31
+ subcommand = subcommand.to_s.downcase
32
+
33
+ if subcommand == "exists"
34
+ arg = args.first
35
+
36
+ send_command([:script, :exists, arg]) do |reply|
37
+ reply = reply.map { |r| Boolify.call(r) }
38
+
39
+ if arg.is_a?(Array)
40
+ reply
41
+ else
42
+ reply.first
43
+ end
44
+ end
45
+ else
46
+ send_command([:script, subcommand] + args)
47
+ end
48
+ end
49
+
50
+ # Evaluate Lua script.
51
+ #
52
+ # @example EVAL without KEYS nor ARGV
53
+ # redis.eval("return 1")
54
+ # # => 1
55
+ # @example EVAL with KEYS and ARGV as array arguments
56
+ # redis.eval("return { KEYS, ARGV }", ["k1", "k2"], ["a1", "a2"])
57
+ # # => [["k1", "k2"], ["a1", "a2"]]
58
+ # @example EVAL with KEYS and ARGV in a hash argument
59
+ # redis.eval("return { KEYS, ARGV }", :keys => ["k1", "k2"], :argv => ["a1", "a2"])
60
+ # # => [["k1", "k2"], ["a1", "a2"]]
61
+ #
62
+ # @param [Array<String>] keys optional array with keys to pass to the script
63
+ # @param [Array<String>] argv optional array with arguments to pass to the script
64
+ # @param [Hash] options
65
+ # - `:keys => Array<String>`: optional array with keys to pass to the script
66
+ # - `:argv => Array<String>`: optional array with arguments to pass to the script
67
+ # @return depends on the script
68
+ #
69
+ # @see #script
70
+ # @see #evalsha
71
+ def eval(*args)
72
+ _eval(:eval, args)
73
+ end
74
+
75
+ # Evaluate Lua script by its SHA.
76
+ #
77
+ # @example EVALSHA without KEYS nor ARGV
78
+ # redis.evalsha(sha)
79
+ # # => <depends on script>
80
+ # @example EVALSHA with KEYS and ARGV as array arguments
81
+ # redis.evalsha(sha, ["k1", "k2"], ["a1", "a2"])
82
+ # # => <depends on script>
83
+ # @example EVALSHA with KEYS and ARGV in a hash argument
84
+ # redis.evalsha(sha, :keys => ["k1", "k2"], :argv => ["a1", "a2"])
85
+ # # => <depends on script>
86
+ #
87
+ # @param [Array<String>] keys optional array with keys to pass to the script
88
+ # @param [Array<String>] argv optional array with arguments to pass to the script
89
+ # @param [Hash] options
90
+ # - `:keys => Array<String>`: optional array with keys to pass to the script
91
+ # - `:argv => Array<String>`: optional array with arguments to pass to the script
92
+ # @return depends on the script
93
+ #
94
+ # @see #script
95
+ # @see #eval
96
+ def evalsha(*args)
97
+ _eval(:evalsha, args)
98
+ end
99
+
100
+ private
101
+
102
+ def _eval(cmd, args)
103
+ script = args.shift
104
+ options = args.pop if args.last.is_a?(Hash)
105
+ options ||= {}
106
+
107
+ keys = args.shift || options[:keys] || []
108
+ argv = args.shift || options[:argv] || []
109
+
110
+ send_command([cmd, script, keys.length] + keys + argv)
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,188 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Redis
4
+ module Commands
5
+ module Server
6
+ # Asynchronously rewrite the append-only file.
7
+ #
8
+ # @return [String] `OK`
9
+ def bgrewriteaof
10
+ send_command([:bgrewriteaof])
11
+ end
12
+
13
+ # Asynchronously save the dataset to disk.
14
+ #
15
+ # @return [String] `OK`
16
+ def bgsave
17
+ send_command([:bgsave])
18
+ end
19
+
20
+ # Get or set server configuration parameters.
21
+ #
22
+ # @param [Symbol] action e.g. `:get`, `:set`, `:resetstat`
23
+ # @return [String, Hash] string reply, or hash when retrieving more than one
24
+ # property with `CONFIG GET`
25
+ def config(action, *args)
26
+ send_command([:config, action] + args) do |reply|
27
+ if reply.is_a?(Array) && action == :get
28
+ Hashify.call(reply)
29
+ else
30
+ reply
31
+ end
32
+ end
33
+ end
34
+
35
+ # Manage client connections.
36
+ #
37
+ # @param [String, Symbol] subcommand e.g. `kill`, `list`, `getname`, `setname`
38
+ # @return [String, Hash] depends on subcommand
39
+ def client(subcommand = nil, *args)
40
+ send_command([:client, subcommand] + args) do |reply|
41
+ if subcommand.to_s == "list"
42
+ reply.lines.map do |line|
43
+ entries = line.chomp.split(/[ =]/)
44
+ Hash[entries.each_slice(2).to_a]
45
+ end
46
+ else
47
+ reply
48
+ end
49
+ end
50
+ end
51
+
52
+ # Return the number of keys in the selected database.
53
+ #
54
+ # @return [Integer]
55
+ def dbsize
56
+ send_command([:dbsize])
57
+ end
58
+
59
+ # Remove all keys from all databases.
60
+ #
61
+ # @param [Hash] options
62
+ # - `:async => Boolean`: async flush (default: false)
63
+ # @return [String] `OK`
64
+ def flushall(options = nil)
65
+ if options && options[:async]
66
+ send_command(%i[flushall async])
67
+ else
68
+ send_command([:flushall])
69
+ end
70
+ end
71
+
72
+ # Remove all keys from the current database.
73
+ #
74
+ # @param [Hash] options
75
+ # - `:async => Boolean`: async flush (default: false)
76
+ # @return [String] `OK`
77
+ def flushdb(options = nil)
78
+ if options && options[:async]
79
+ send_command(%i[flushdb async])
80
+ else
81
+ send_command([:flushdb])
82
+ end
83
+ end
84
+
85
+ # Get information and statistics about the server.
86
+ #
87
+ # @param [String, Symbol] cmd e.g. "commandstats"
88
+ # @return [Hash<String, String>]
89
+ def info(cmd = nil)
90
+ send_command([:info, cmd].compact) do |reply|
91
+ if reply.is_a?(String)
92
+ reply = HashifyInfo.call(reply)
93
+
94
+ if cmd && cmd.to_s == "commandstats"
95
+ # Extract nested hashes for INFO COMMANDSTATS
96
+ reply = Hash[reply.map do |k, v|
97
+ v = v.split(",").map { |e| e.split("=") }
98
+ [k[/^cmdstat_(.*)$/, 1], Hash[v]]
99
+ end]
100
+ end
101
+ end
102
+
103
+ reply
104
+ end
105
+ end
106
+
107
+ # Get the UNIX time stamp of the last successful save to disk.
108
+ #
109
+ # @return [Integer]
110
+ def lastsave
111
+ send_command([:lastsave])
112
+ end
113
+
114
+ # Listen for all requests received by the server in real time.
115
+ #
116
+ # There is no way to interrupt this command.
117
+ #
118
+ # @yield a block to be called for every line of output
119
+ # @yieldparam [String] line timestamp and command that was executed
120
+ def monitor(&block)
121
+ synchronize do |client|
122
+ client.call_loop([:monitor], &block)
123
+ end
124
+ end
125
+
126
+ # Synchronously save the dataset to disk.
127
+ #
128
+ # @return [String]
129
+ def save
130
+ send_command([:save])
131
+ end
132
+
133
+ # Synchronously save the dataset to disk and then shut down the server.
134
+ def shutdown
135
+ synchronize do |client|
136
+ client.with_reconnect(false) do
137
+ begin
138
+ client.call([:shutdown])
139
+ rescue ConnectionError
140
+ # This means Redis has probably exited.
141
+ nil
142
+ end
143
+ end
144
+ end
145
+ end
146
+
147
+ # Make the server a slave of another instance, or promote it as master.
148
+ def slaveof(host, port)
149
+ send_command([:slaveof, host, port])
150
+ end
151
+
152
+ # Interact with the slowlog (get, len, reset)
153
+ #
154
+ # @param [String] subcommand e.g. `get`, `len`, `reset`
155
+ # @param [Integer] length maximum number of entries to return
156
+ # @return [Array<String>, Integer, String] depends on subcommand
157
+ def slowlog(subcommand, length = nil)
158
+ synchronize do |client|
159
+ args = [:slowlog, subcommand]
160
+ args << length if length
161
+ client.call args
162
+ end
163
+ end
164
+
165
+ # Internal command used for replication.
166
+ def sync
167
+ send_command([:sync])
168
+ end
169
+
170
+ # Return the server time.
171
+ #
172
+ # @example
173
+ # r.time # => [ 1333093196, 606806 ]
174
+ #
175
+ # @return [Array<Integer>] tuple of seconds since UNIX epoch and
176
+ # microseconds in the current second
177
+ def time
178
+ send_command([:time]) do |reply|
179
+ reply&.map(&:to_i)
180
+ end
181
+ end
182
+
183
+ def debug(*args)
184
+ send_command([:debug] + args)
185
+ end
186
+ end
187
+ end
188
+ end