redis 3.3.5 → 5.0.7

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 (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,402 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Redis
4
+ module Commands
5
+ module Streams
6
+ # Returns the stream information each subcommand.
7
+ #
8
+ # @example stream
9
+ # redis.xinfo(:stream, 'mystream')
10
+ # @example groups
11
+ # redis.xinfo(:groups, 'mystream')
12
+ # @example consumers
13
+ # redis.xinfo(:consumers, 'mystream', 'mygroup')
14
+ #
15
+ # @param subcommand [String] e.g. `stream` `groups` `consumers`
16
+ # @param key [String] the stream key
17
+ # @param group [String] the consumer group name, required if subcommand is `consumers`
18
+ #
19
+ # @return [Hash] information of the stream if subcommand is `stream`
20
+ # @return [Array<Hash>] information of the consumer groups if subcommand is `groups`
21
+ # @return [Array<Hash>] information of the consumers if subcommand is `consumers`
22
+ def xinfo(subcommand, key, group = nil)
23
+ args = [:xinfo, subcommand, key, group].compact
24
+ block = case subcommand.to_s.downcase
25
+ when 'stream' then Hashify
26
+ when 'groups', 'consumers' then proc { |r| r.map(&Hashify) }
27
+ end
28
+
29
+ send_command(args, &block)
30
+ end
31
+
32
+ # Add new entry to the stream.
33
+ #
34
+ # @example Without options
35
+ # redis.xadd('mystream', f1: 'v1', f2: 'v2')
36
+ # @example With options
37
+ # redis.xadd('mystream', { f1: 'v1', f2: 'v2' }, id: '0-0', maxlen: 1000, approximate: true, nomkstream: true)
38
+ #
39
+ # @param key [String] the stream key
40
+ # @param entry [Hash] one or multiple field-value pairs
41
+ # @param opts [Hash] several options for `XADD` command
42
+ #
43
+ # @option opts [String] :id the entry id, default value is `*`, it means auto generation
44
+ # @option opts [Integer] :maxlen max length of entries
45
+ # @option opts [Boolean] :approximate whether to add `~` modifier of maxlen or not
46
+ # @option opts [Boolean] :nomkstream whether to add NOMKSTREAM, default is not to add
47
+ #
48
+ # @return [String] the entry id
49
+ def xadd(key, entry, approximate: nil, maxlen: nil, nomkstream: nil, id: '*')
50
+ args = [:xadd, key]
51
+ args << 'NOMKSTREAM' if nomkstream
52
+ if maxlen
53
+ args << "MAXLEN"
54
+ args << "~" if approximate
55
+ args << maxlen
56
+ end
57
+ args << id
58
+ args.concat(entry.flatten)
59
+ send_command(args)
60
+ end
61
+
62
+ # Trims older entries of the stream if needed.
63
+ #
64
+ # @example Without options
65
+ # redis.xtrim('mystream', 1000)
66
+ # @example With options
67
+ # redis.xtrim('mystream', 1000, approximate: true)
68
+ # @example With strategy
69
+ # redis.xtrim('mystream', '1-0', strategy: 'MINID')
70
+ #
71
+ # @overload xtrim(key, maxlen, strategy: 'MAXLEN', approximate: true)
72
+ # @param key [String] the stream key
73
+ # @param maxlen [Integer] max length of entries
74
+ # @param strategy [String] the limit strategy, must be MAXLEN
75
+ # @param approximate [Boolean] whether to add `~` modifier of maxlen or not
76
+ # @param limit [Integer] maximum count of entries to be evicted
77
+ # @overload xtrim(key, minid, strategy: 'MINID', approximate: true)
78
+ # @param key [String] the stream key
79
+ # @param minid [String] minimum id of entries
80
+ # @param strategy [String] the limit strategy, must be MINID
81
+ # @param approximate [Boolean] whether to add `~` modifier of minid or not
82
+ # @param limit [Integer] maximum count of entries to be evicted
83
+ #
84
+ # @return [Integer] the number of entries actually deleted
85
+ def xtrim(key, len_or_id, strategy: 'MAXLEN', approximate: false, limit: nil)
86
+ strategy = strategy.to_s.upcase
87
+
88
+ args = [:xtrim, key, strategy]
89
+ args << '~' if approximate
90
+ args << len_or_id
91
+ args.concat(['LIMIT', limit]) if limit
92
+ send_command(args)
93
+ end
94
+
95
+ # Delete entries by entry ids.
96
+ #
97
+ # @example With splatted entry ids
98
+ # redis.xdel('mystream', '0-1', '0-2')
99
+ # @example With arrayed entry ids
100
+ # redis.xdel('mystream', ['0-1', '0-2'])
101
+ #
102
+ # @param key [String] the stream key
103
+ # @param ids [Array<String>] one or multiple entry ids
104
+ #
105
+ # @return [Integer] the number of entries actually deleted
106
+ def xdel(key, *ids)
107
+ args = [:xdel, key].concat(ids.flatten)
108
+ send_command(args)
109
+ end
110
+
111
+ # Fetches entries of the stream in ascending order.
112
+ #
113
+ # @example Without options
114
+ # redis.xrange('mystream')
115
+ # @example With a specific start
116
+ # redis.xrange('mystream', '0-1')
117
+ # @example With a specific start and end
118
+ # redis.xrange('mystream', '0-1', '0-3')
119
+ # @example With count options
120
+ # redis.xrange('mystream', count: 10)
121
+ #
122
+ # @param key [String] the stream key
123
+ # @param start [String] first entry id of range, default value is `-`
124
+ # @param end [String] last entry id of range, default value is `+`
125
+ # @param count [Integer] the number of entries as limit
126
+ #
127
+ # @return [Array<Array<String, Hash>>] the ids and entries pairs
128
+ def xrange(key, start = '-', range_end = '+', count: nil)
129
+ args = [:xrange, key, start, range_end]
130
+ args.concat(['COUNT', count]) if count
131
+ send_command(args, &HashifyStreamEntries)
132
+ end
133
+
134
+ # Fetches entries of the stream in descending order.
135
+ #
136
+ # @example Without options
137
+ # redis.xrevrange('mystream')
138
+ # @example With a specific end
139
+ # redis.xrevrange('mystream', '0-3')
140
+ # @example With a specific end and start
141
+ # redis.xrevrange('mystream', '0-3', '0-1')
142
+ # @example With count options
143
+ # redis.xrevrange('mystream', count: 10)
144
+ #
145
+ # @param key [String] the stream key
146
+ # @param end [String] first entry id of range, default value is `+`
147
+ # @param start [String] last entry id of range, default value is `-`
148
+ # @params count [Integer] the number of entries as limit
149
+ #
150
+ # @return [Array<Array<String, Hash>>] the ids and entries pairs
151
+ def xrevrange(key, range_end = '+', start = '-', count: nil)
152
+ args = [:xrevrange, key, range_end, start]
153
+ args.concat(['COUNT', count]) if count
154
+ send_command(args, &HashifyStreamEntries)
155
+ end
156
+
157
+ # Returns the number of entries inside a stream.
158
+ #
159
+ # @example With key
160
+ # redis.xlen('mystream')
161
+ #
162
+ # @param key [String] the stream key
163
+ #
164
+ # @return [Integer] the number of entries
165
+ def xlen(key)
166
+ send_command([:xlen, key])
167
+ end
168
+
169
+ # Fetches entries from one or multiple streams. Optionally blocking.
170
+ #
171
+ # @example With a key
172
+ # redis.xread('mystream', '0-0')
173
+ # @example With multiple keys
174
+ # redis.xread(%w[mystream1 mystream2], %w[0-0 0-0])
175
+ # @example With count option
176
+ # redis.xread('mystream', '0-0', count: 2)
177
+ # @example With block option
178
+ # redis.xread('mystream', '$', block: 1000)
179
+ #
180
+ # @param keys [Array<String>] one or multiple stream keys
181
+ # @param ids [Array<String>] one or multiple entry ids
182
+ # @param count [Integer] the number of entries as limit per stream
183
+ # @param block [Integer] the number of milliseconds as blocking timeout
184
+ #
185
+ # @return [Hash{String => Hash{String => Hash}}] the entries
186
+ def xread(keys, ids, count: nil, block: nil)
187
+ args = [:xread]
188
+ args << 'COUNT' << count if count
189
+ args << 'BLOCK' << block.to_i if block
190
+ _xread(args, keys, ids, block)
191
+ end
192
+
193
+ # Manages the consumer group of the stream.
194
+ #
195
+ # @example With `create` subcommand
196
+ # redis.xgroup(:create, 'mystream', 'mygroup', '$')
197
+ # @example With `setid` subcommand
198
+ # redis.xgroup(:setid, 'mystream', 'mygroup', '$')
199
+ # @example With `destroy` subcommand
200
+ # redis.xgroup(:destroy, 'mystream', 'mygroup')
201
+ # @example With `delconsumer` subcommand
202
+ # redis.xgroup(:delconsumer, 'mystream', 'mygroup', 'consumer1')
203
+ #
204
+ # @param subcommand [String] `create` `setid` `destroy` `delconsumer`
205
+ # @param key [String] the stream key
206
+ # @param group [String] the consumer group name
207
+ # @param id_or_consumer [String]
208
+ # * the entry id or `$`, required if subcommand is `create` or `setid`
209
+ # * the consumer name, required if subcommand is `delconsumer`
210
+ # @param mkstream [Boolean] whether to create an empty stream automatically or not
211
+ #
212
+ # @return [String] `OK` if subcommand is `create` or `setid`
213
+ # @return [Integer] effected count if subcommand is `destroy` or `delconsumer`
214
+ def xgroup(subcommand, key, group, id_or_consumer = nil, mkstream: false)
215
+ args = [:xgroup, subcommand, key, group, id_or_consumer, (mkstream ? 'MKSTREAM' : nil)].compact
216
+ send_command(args)
217
+ end
218
+
219
+ # Fetches a subset of the entries from one or multiple streams related with the consumer group.
220
+ # Optionally blocking.
221
+ #
222
+ # @example With a key
223
+ # redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>')
224
+ # @example With multiple keys
225
+ # redis.xreadgroup('mygroup', 'consumer1', %w[mystream1 mystream2], %w[> >])
226
+ # @example With count option
227
+ # redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', count: 2)
228
+ # @example With block option
229
+ # redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', block: 1000)
230
+ # @example With noack option
231
+ # redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', noack: true)
232
+ #
233
+ # @param group [String] the consumer group name
234
+ # @param consumer [String] the consumer name
235
+ # @param keys [Array<String>] one or multiple stream keys
236
+ # @param ids [Array<String>] one or multiple entry ids
237
+ # @param opts [Hash] several options for `XREADGROUP` command
238
+ #
239
+ # @option opts [Integer] :count the number of entries as limit
240
+ # @option opts [Integer] :block the number of milliseconds as blocking timeout
241
+ # @option opts [Boolean] :noack whether message loss is acceptable or not
242
+ #
243
+ # @return [Hash{String => Hash{String => Hash}}] the entries
244
+ def xreadgroup(group, consumer, keys, ids, count: nil, block: nil, noack: nil)
245
+ args = [:xreadgroup, 'GROUP', group, consumer]
246
+ args << 'COUNT' << count if count
247
+ args << 'BLOCK' << block.to_i if block
248
+ args << 'NOACK' if noack
249
+ _xread(args, keys, ids, block)
250
+ end
251
+
252
+ # Removes one or multiple entries from the pending entries list of a stream consumer group.
253
+ #
254
+ # @example With a entry id
255
+ # redis.xack('mystream', 'mygroup', '1526569495631-0')
256
+ # @example With splatted entry ids
257
+ # redis.xack('mystream', 'mygroup', '0-1', '0-2')
258
+ # @example With arrayed entry ids
259
+ # redis.xack('mystream', 'mygroup', %w[0-1 0-2])
260
+ #
261
+ # @param key [String] the stream key
262
+ # @param group [String] the consumer group name
263
+ # @param ids [Array<String>] one or multiple entry ids
264
+ #
265
+ # @return [Integer] the number of entries successfully acknowledged
266
+ def xack(key, group, *ids)
267
+ args = [:xack, key, group].concat(ids.flatten)
268
+ send_command(args)
269
+ end
270
+
271
+ # Changes the ownership of a pending entry
272
+ #
273
+ # @example With splatted entry ids
274
+ # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-1', '0-2')
275
+ # @example With arrayed entry ids
276
+ # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2])
277
+ # @example With idle option
278
+ # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], idle: 1000)
279
+ # @example With time option
280
+ # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], time: 1542866959000)
281
+ # @example With retrycount option
282
+ # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], retrycount: 10)
283
+ # @example With force option
284
+ # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], force: true)
285
+ # @example With justid option
286
+ # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], justid: true)
287
+ #
288
+ # @param key [String] the stream key
289
+ # @param group [String] the consumer group name
290
+ # @param consumer [String] the consumer name
291
+ # @param min_idle_time [Integer] the number of milliseconds
292
+ # @param ids [Array<String>] one or multiple entry ids
293
+ # @param opts [Hash] several options for `XCLAIM` command
294
+ #
295
+ # @option opts [Integer] :idle the number of milliseconds as last time it was delivered of the entry
296
+ # @option opts [Integer] :time the number of milliseconds as a specific Unix Epoch time
297
+ # @option opts [Integer] :retrycount the number of retry counter
298
+ # @option opts [Boolean] :force whether to create the pending entry to the pending entries list or not
299
+ # @option opts [Boolean] :justid whether to fetch just an array of entry ids or not
300
+ #
301
+ # @return [Hash{String => Hash}] the entries successfully claimed
302
+ # @return [Array<String>] the entry ids successfully claimed if justid option is `true`
303
+ def xclaim(key, group, consumer, min_idle_time, *ids, **opts)
304
+ args = [:xclaim, key, group, consumer, min_idle_time].concat(ids.flatten)
305
+ args.concat(['IDLE', opts[:idle].to_i]) if opts[:idle]
306
+ args.concat(['TIME', opts[:time].to_i]) if opts[:time]
307
+ args.concat(['RETRYCOUNT', opts[:retrycount]]) if opts[:retrycount]
308
+ args << 'FORCE' if opts[:force]
309
+ args << 'JUSTID' if opts[:justid]
310
+ blk = opts[:justid] ? Noop : HashifyStreamEntries
311
+ send_command(args, &blk)
312
+ end
313
+
314
+ # Transfers ownership of pending stream entries that match the specified criteria.
315
+ #
316
+ # @example Claim next pending message stuck > 5 minutes and mark as retry
317
+ # redis.xautoclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0')
318
+ # @example Claim 50 next pending messages stuck > 5 minutes and mark as retry
319
+ # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0', count: 50)
320
+ # @example Claim next pending message stuck > 5 minutes and don't mark as retry
321
+ # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0', justid: true)
322
+ # @example Claim next pending message after this id stuck > 5 minutes and mark as retry
323
+ # redis.xautoclaim('mystream', 'mygroup', 'consumer1', 3600000, '1641321233-0')
324
+ #
325
+ # @param key [String] the stream key
326
+ # @param group [String] the consumer group name
327
+ # @param consumer [String] the consumer name
328
+ # @param min_idle_time [Integer] the number of milliseconds
329
+ # @param start [String] entry id to start scanning from or 0-0 for everything
330
+ # @param count [Integer] number of messages to claim (default 1)
331
+ # @param justid [Boolean] whether to fetch just an array of entry ids or not.
332
+ # Does not increment retry count when true
333
+ #
334
+ # @return [Hash{String => Hash}] the entries successfully claimed
335
+ # @return [Array<String>] the entry ids successfully claimed if justid option is `true`
336
+ def xautoclaim(key, group, consumer, min_idle_time, start, count: nil, justid: false)
337
+ args = [:xautoclaim, key, group, consumer, min_idle_time, start]
338
+ if count
339
+ args << 'COUNT' << count.to_s
340
+ end
341
+ args << 'JUSTID' if justid
342
+ blk = justid ? HashifyStreamAutoclaimJustId : HashifyStreamAutoclaim
343
+ send_command(args, &blk)
344
+ end
345
+
346
+ # Fetches not acknowledging pending entries
347
+ #
348
+ # @example With key and group
349
+ # redis.xpending('mystream', 'mygroup')
350
+ # @example With range options
351
+ # redis.xpending('mystream', 'mygroup', '-', '+', 10)
352
+ # @example With range and idle time options
353
+ # redis.xpending('mystream', 'mygroup', '-', '+', 10, idle: 9000)
354
+ # @example With range and consumer options
355
+ # redis.xpending('mystream', 'mygroup', '-', '+', 10, 'consumer1')
356
+ #
357
+ # @param key [String] the stream key
358
+ # @param group [String] the consumer group name
359
+ # @param start [String] start first entry id of range
360
+ # @param end [String] end last entry id of range
361
+ # @param count [Integer] count the number of entries as limit
362
+ # @param consumer [String] the consumer name
363
+ #
364
+ # @option opts [Integer] :idle pending message minimum idle time in milliseconds
365
+ #
366
+ # @return [Hash] the summary of pending entries
367
+ # @return [Array<Hash>] the pending entries details if options were specified
368
+ def xpending(key, group, *args, idle: nil)
369
+ command_args = [:xpending, key, group]
370
+ command_args << 'IDLE' << Integer(idle) if idle
371
+ case args.size
372
+ when 0, 3, 4
373
+ command_args.concat(args)
374
+ else
375
+ raise ArgumentError, "wrong number of arguments (given #{args.size + 2}, expected 2, 5 or 6)"
376
+ end
377
+
378
+ summary_needed = args.empty?
379
+ blk = summary_needed ? HashifyStreamPendings : HashifyStreamPendingDetails
380
+ send_command(command_args, &blk)
381
+ end
382
+
383
+ private
384
+
385
+ def _xread(args, keys, ids, blocking_timeout_msec)
386
+ keys = keys.is_a?(Array) ? keys : [keys]
387
+ ids = ids.is_a?(Array) ? ids : [ids]
388
+ args << 'STREAMS'
389
+ args.concat(keys)
390
+ args.concat(ids)
391
+
392
+ if blocking_timeout_msec.nil?
393
+ send_command(args, &HashifyStreams)
394
+ elsif blocking_timeout_msec.to_f.zero?
395
+ send_blocking_command(args, 0, &HashifyStreams)
396
+ else
397
+ send_blocking_command(args, blocking_timeout_msec.to_f / 1_000, &HashifyStreams)
398
+ end
399
+ end
400
+ end
401
+ end
402
+ end