valkey-rb 1.0.0
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.
- checksums.yaml +7 -0
- data/.rubocop.yml +58 -0
- data/.rubocop_todo.yml +22 -0
- data/README.md +95 -0
- data/Rakefile +23 -0
- data/lib/valkey/bindings.rb +224 -0
- data/lib/valkey/commands/bitmap_commands.rb +86 -0
- data/lib/valkey/commands/cluster_commands.rb +259 -0
- data/lib/valkey/commands/connection_commands.rb +318 -0
- data/lib/valkey/commands/function_commands.rb +255 -0
- data/lib/valkey/commands/generic_commands.rb +525 -0
- data/lib/valkey/commands/geo_commands.rb +87 -0
- data/lib/valkey/commands/hash_commands.rb +587 -0
- data/lib/valkey/commands/hyper_log_log_commands.rb +51 -0
- data/lib/valkey/commands/json_commands.rb +389 -0
- data/lib/valkey/commands/list_commands.rb +348 -0
- data/lib/valkey/commands/module_commands.rb +125 -0
- data/lib/valkey/commands/pubsub_commands.rb +237 -0
- data/lib/valkey/commands/scripting_commands.rb +286 -0
- data/lib/valkey/commands/server_commands.rb +961 -0
- data/lib/valkey/commands/set_commands.rb +220 -0
- data/lib/valkey/commands/sorted_set_commands.rb +971 -0
- data/lib/valkey/commands/stream_commands.rb +636 -0
- data/lib/valkey/commands/string_commands.rb +359 -0
- data/lib/valkey/commands/transaction_commands.rb +175 -0
- data/lib/valkey/commands/vector_search_commands.rb +271 -0
- data/lib/valkey/commands.rb +68 -0
- data/lib/valkey/errors.rb +41 -0
- data/lib/valkey/libglide_ffi.so +0 -0
- data/lib/valkey/opentelemetry.rb +207 -0
- data/lib/valkey/pipeline.rb +20 -0
- data/lib/valkey/protobuf/command_request_pb.rb +51 -0
- data/lib/valkey/protobuf/connection_request_pb.rb +51 -0
- data/lib/valkey/protobuf/response_pb.rb +39 -0
- data/lib/valkey/pubsub_callback.rb +10 -0
- data/lib/valkey/request_error_type.rb +10 -0
- data/lib/valkey/request_type.rb +436 -0
- data/lib/valkey/response_type.rb +20 -0
- data/lib/valkey/utils.rb +253 -0
- data/lib/valkey/version.rb +5 -0
- data/lib/valkey.rb +551 -0
- metadata +119 -0
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Valkey
|
|
4
|
+
module Commands
|
|
5
|
+
# This module contains commands related to Redis Streams.
|
|
6
|
+
#
|
|
7
|
+
# @see https://valkey.io/commands/#stream
|
|
8
|
+
#
|
|
9
|
+
module StreamCommands
|
|
10
|
+
# Append a new entry to a stream.
|
|
11
|
+
#
|
|
12
|
+
# @param [String] key stream key
|
|
13
|
+
# @param [Hash, Array] entry field-value pairs
|
|
14
|
+
# @param [Hash] options optional parameters
|
|
15
|
+
# - `:id => String`: entry ID (default: "*" for auto-generated)
|
|
16
|
+
# - `:maxlen => Integer`: maximum length of the stream
|
|
17
|
+
# - `:minid => String`: minimum ID to keep
|
|
18
|
+
# - `:approximate => true`: use approximate trimming
|
|
19
|
+
# - `:nomkstream => true`: do not create stream if it doesn't exist
|
|
20
|
+
# @return [String] entry ID
|
|
21
|
+
#
|
|
22
|
+
# @example Add entry with auto-generated ID
|
|
23
|
+
# valkey.xadd("mystream", { "field1" => "value1", "field2" => "value2" })
|
|
24
|
+
# # => "1234567890-0"
|
|
25
|
+
# @example Add entry with specific ID
|
|
26
|
+
# valkey.xadd("mystream", { "field1" => "value1" }, id: "1234567890-1")
|
|
27
|
+
# # => "1234567890-1"
|
|
28
|
+
# @example Add entry with maxlen trimming
|
|
29
|
+
# valkey.xadd("mystream", { "field1" => "value1" }, maxlen: 1000, approximate: true)
|
|
30
|
+
#
|
|
31
|
+
# @see https://valkey.io/commands/xadd/
|
|
32
|
+
def xadd(key, entry, approximate: nil, maxlen: nil, minid: nil, nomkstream: nil, id: "*")
|
|
33
|
+
args = [key]
|
|
34
|
+
|
|
35
|
+
# Handle maxlen/minid trimming
|
|
36
|
+
if maxlen
|
|
37
|
+
raise ArgumentError, "can't supply both maxlen and minid" if minid
|
|
38
|
+
|
|
39
|
+
args << "MAXLEN"
|
|
40
|
+
args << "~" if approximate
|
|
41
|
+
args << maxlen.to_s
|
|
42
|
+
elsif minid
|
|
43
|
+
args << "MINID"
|
|
44
|
+
args << "~" if approximate
|
|
45
|
+
args << minid
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
args << "NOMKSTREAM" if nomkstream
|
|
49
|
+
args << id
|
|
50
|
+
|
|
51
|
+
# Add field-value pairs
|
|
52
|
+
if entry.is_a?(Hash)
|
|
53
|
+
entry.each { |k, v| args << k.to_s << v.to_s }
|
|
54
|
+
else
|
|
55
|
+
args.concat(Array(entry).flatten)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
send_command(RequestType::X_ADD, args)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Remove one or more entries from a stream.
|
|
62
|
+
#
|
|
63
|
+
# @param [String] key stream key
|
|
64
|
+
# @param [String, Array<String>] ids entry ID(s) to delete
|
|
65
|
+
# @return [Integer] number of entries deleted
|
|
66
|
+
#
|
|
67
|
+
# @example Delete a single entry
|
|
68
|
+
# valkey.xdel("mystream", "1234567890-0")
|
|
69
|
+
# # => 1
|
|
70
|
+
# @example Delete multiple entries
|
|
71
|
+
# valkey.xdel("mystream", ["1234567890-0", "1234567890-1"])
|
|
72
|
+
# # => 2
|
|
73
|
+
#
|
|
74
|
+
# @see https://valkey.io/commands/xdel/
|
|
75
|
+
def xdel(key, *ids)
|
|
76
|
+
args = [key] + Array(ids).flatten
|
|
77
|
+
send_command(RequestType::X_DEL, args)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Get the length of a stream.
|
|
81
|
+
#
|
|
82
|
+
# @param [String] key stream key
|
|
83
|
+
# @return [Integer] number of entries in the stream
|
|
84
|
+
#
|
|
85
|
+
# @example
|
|
86
|
+
# valkey.xlen("mystream")
|
|
87
|
+
# # => 42
|
|
88
|
+
#
|
|
89
|
+
# @see https://valkey.io/commands/xlen/
|
|
90
|
+
def xlen(key)
|
|
91
|
+
send_command(RequestType::X_LEN, [key])
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Read entries from one or more streams.
|
|
95
|
+
#
|
|
96
|
+
# @param [Array<String>] keys stream keys
|
|
97
|
+
# @param [Array<String>] ids last read IDs for each stream
|
|
98
|
+
# @param [Hash] options optional parameters
|
|
99
|
+
# - `:count => Integer`: maximum number of entries per stream
|
|
100
|
+
# - `:block => Integer`: block for specified milliseconds (0 = no timeout)
|
|
101
|
+
# @return [Hash] hash of stream keys to arrays of entries (empty hash on timeout or no data)
|
|
102
|
+
#
|
|
103
|
+
# @example Read from a single stream
|
|
104
|
+
# valkey.xread(["mystream"], ["0"])
|
|
105
|
+
# # => { "mystream" => [["1234567890-0", ["field1", "value1"]]] }
|
|
106
|
+
# @example Read with count and block
|
|
107
|
+
# valkey.xread(["mystream"], ["0"], count: 10, block: 1000)
|
|
108
|
+
#
|
|
109
|
+
# @see https://valkey.io/commands/xread/
|
|
110
|
+
def xread(keys, ids, count: nil, block: nil)
|
|
111
|
+
args = []
|
|
112
|
+
|
|
113
|
+
args << "COUNT" << count.to_s if count
|
|
114
|
+
args << "BLOCK" << block.to_s if block
|
|
115
|
+
args << "STREAMS"
|
|
116
|
+
args.concat(Array(keys))
|
|
117
|
+
args.concat(Array(ids))
|
|
118
|
+
|
|
119
|
+
send_command(RequestType::X_READ, args) do |reply|
|
|
120
|
+
# Backend returns Array format: [stream_name, entries, stream_name2, entries2, ...]
|
|
121
|
+
# Convert to Hash format first
|
|
122
|
+
if reply.nil?
|
|
123
|
+
{}
|
|
124
|
+
elsif reply.is_a?(Array) && !reply.empty?
|
|
125
|
+
stream_hash = reply.each_slice(2).to_h
|
|
126
|
+
Utils::HashifyStreams.call(stream_hash)
|
|
127
|
+
else
|
|
128
|
+
Utils::HashifyStreams.call(reply)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Read entries from streams using a consumer group.
|
|
134
|
+
#
|
|
135
|
+
# @param [String] group consumer group name
|
|
136
|
+
# @param [String] consumer consumer name
|
|
137
|
+
# @param [Array<String>] keys stream keys
|
|
138
|
+
# @param [Array<String>] ids last read IDs for each stream
|
|
139
|
+
# @param [Hash] options optional parameters
|
|
140
|
+
# - `:count => Integer`: maximum number of entries per stream
|
|
141
|
+
# - `:block => Integer`: block for specified milliseconds (0 = no timeout)
|
|
142
|
+
# - `:noack => true`: do not add messages to pending list
|
|
143
|
+
# @return [Hash] hash of stream keys to arrays of entries (empty hash on timeout or no data)
|
|
144
|
+
#
|
|
145
|
+
# @example Read from consumer group
|
|
146
|
+
# valkey.xreadgroup("mygroup", "consumer1", ["mystream"], [">"])
|
|
147
|
+
#
|
|
148
|
+
# @see https://valkey.io/commands/xreadgroup/
|
|
149
|
+
def xreadgroup(group, consumer, keys, ids, count: nil, block: nil, noack: false)
|
|
150
|
+
args = ["GROUP", group, consumer]
|
|
151
|
+
|
|
152
|
+
args << "COUNT" << count.to_s if count
|
|
153
|
+
args << "BLOCK" << block.to_s if block
|
|
154
|
+
args << "NOACK" if noack
|
|
155
|
+
args << "STREAMS"
|
|
156
|
+
args.concat(Array(keys))
|
|
157
|
+
args.concat(Array(ids))
|
|
158
|
+
|
|
159
|
+
send_command(RequestType::X_READ_GROUP, args) do |reply|
|
|
160
|
+
# Backend returns Array format: [stream_name, entries, stream_name2, entries2, ...]
|
|
161
|
+
# Convert to Hash format first
|
|
162
|
+
if reply.nil?
|
|
163
|
+
{}
|
|
164
|
+
elsif reply.is_a?(Array) && !reply.empty?
|
|
165
|
+
stream_hash = reply.each_slice(2).to_h
|
|
166
|
+
Utils::HashifyStreams.call(stream_hash)
|
|
167
|
+
else
|
|
168
|
+
Utils::HashifyStreams.call(reply)
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Get entries from a stream within a range of IDs.
|
|
174
|
+
#
|
|
175
|
+
# @param [String] key stream key
|
|
176
|
+
# @param [String] start start ID ("-" for beginning, "+" for end)
|
|
177
|
+
# @param [String] end_id end ID ("-" for beginning, "+" for end)
|
|
178
|
+
# @param [Hash] options optional parameters
|
|
179
|
+
# - `:count => Integer`: maximum number of entries to return
|
|
180
|
+
# @return [Array] array of [id, [field, value, ...]] entries
|
|
181
|
+
#
|
|
182
|
+
# @example Get all entries
|
|
183
|
+
# valkey.xrange("mystream", "-", "+")
|
|
184
|
+
# @example Get entries with count limit
|
|
185
|
+
# valkey.xrange("mystream", "-", "+", count: 10)
|
|
186
|
+
#
|
|
187
|
+
# @see https://valkey.io/commands/xrange/
|
|
188
|
+
def xrange(key, start, end_id, **options)
|
|
189
|
+
args = [key, start, end_id]
|
|
190
|
+
args << "COUNT" << options[:count].to_s if options[:count]
|
|
191
|
+
send_command(RequestType::X_RANGE, args) do |reply|
|
|
192
|
+
Utils::HashifyStreamEntries.call(reply)
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Get entries from a stream within a range of IDs in reverse order.
|
|
197
|
+
#
|
|
198
|
+
# @param [String] key stream key
|
|
199
|
+
# @param [String] end_id end ID ("+" for end, "-" for beginning) - higher bound
|
|
200
|
+
# @param [String] start start ID ("-" for beginning, "+" for end) - lower bound
|
|
201
|
+
# @param [Hash] options optional parameters
|
|
202
|
+
# - `:count => Integer`: maximum number of entries to return
|
|
203
|
+
# @return [Array] array of [id, [field, value, ...]] entries in reverse order
|
|
204
|
+
#
|
|
205
|
+
# @example Get last 10 entries
|
|
206
|
+
# valkey.xrevrange("mystream", "+", "-", count: 10)
|
|
207
|
+
#
|
|
208
|
+
# @see https://valkey.io/commands/xrevrange/
|
|
209
|
+
def xrevrange(key, end_id = "+", start = "-", count: nil)
|
|
210
|
+
args = [key, end_id, start]
|
|
211
|
+
args << "COUNT" << count.to_s if count
|
|
212
|
+
send_command(RequestType::X_REV_RANGE, args) do |reply|
|
|
213
|
+
Utils::HashifyStreamEntries.call(reply)
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Trim a stream to a maximum length.
|
|
218
|
+
#
|
|
219
|
+
# @param [String] key stream key
|
|
220
|
+
# @param [Integer] maxlen maximum length of the stream
|
|
221
|
+
# @param [Hash] options trimming options
|
|
222
|
+
# - `:strategy => String`: trimming strategy (default: "MAXLEN")
|
|
223
|
+
# - `:approximate => true`: use approximate trimming (default: true)
|
|
224
|
+
# @return [Integer] number of entries removed
|
|
225
|
+
#
|
|
226
|
+
# @example Trim to maximum length
|
|
227
|
+
# valkey.xtrim("mystream", 1000)
|
|
228
|
+
# @example Trim with exact count
|
|
229
|
+
# valkey.xtrim("mystream", 1000, approximate: false)
|
|
230
|
+
#
|
|
231
|
+
# @see https://valkey.io/commands/xtrim/
|
|
232
|
+
def xtrim(key, maxlen, strategy: "MAXLEN", approximate: true)
|
|
233
|
+
args = [key, strategy]
|
|
234
|
+
args << "~" if approximate
|
|
235
|
+
args << maxlen.to_s
|
|
236
|
+
send_command(RequestType::X_TRIM, args)
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# Manage consumer groups (dispatcher method).
|
|
240
|
+
#
|
|
241
|
+
# @param [Symbol, String] subcommand subcommand (:create, :setid, :destroy, :createconsumer, :delconsumer)
|
|
242
|
+
# @param [String] key stream key
|
|
243
|
+
# @param [String] group consumer group name
|
|
244
|
+
# @param [Array] args additional arguments depending on subcommand
|
|
245
|
+
# @return [String, Integer] depends on subcommand
|
|
246
|
+
#
|
|
247
|
+
# @example Create group
|
|
248
|
+
# valkey.xgroup(:create, "mystream", "mygroup", "0")
|
|
249
|
+
# @example Create group with mkstream
|
|
250
|
+
# valkey.xgroup(:create, "mystream", "mygroup", "0", mkstream: true)
|
|
251
|
+
# @example Set group ID
|
|
252
|
+
# valkey.xgroup(:setid, "mystream", "mygroup", "1234567890-0")
|
|
253
|
+
# @example Destroy group
|
|
254
|
+
# valkey.xgroup(:destroy, "mystream", "mygroup")
|
|
255
|
+
# @example Create consumer
|
|
256
|
+
# valkey.xgroup(:createconsumer, "mystream", "mygroup", "consumer1")
|
|
257
|
+
# @example Delete consumer
|
|
258
|
+
# valkey.xgroup(:delconsumer, "mystream", "mygroup", "consumer1")
|
|
259
|
+
#
|
|
260
|
+
# @see https://valkey.io/commands/xgroup/
|
|
261
|
+
def xgroup(subcommand, key, group, *args, **options)
|
|
262
|
+
subcommand = subcommand.to_s.downcase
|
|
263
|
+
case subcommand
|
|
264
|
+
when "create"
|
|
265
|
+
xgroup_create(key, group, args[0], **options)
|
|
266
|
+
when "setid"
|
|
267
|
+
xgroup_setid(key, group, args[0])
|
|
268
|
+
when "destroy"
|
|
269
|
+
xgroup_destroy(key, group)
|
|
270
|
+
when "createconsumer"
|
|
271
|
+
xgroup_createconsumer(key, group, args[0])
|
|
272
|
+
when "delconsumer"
|
|
273
|
+
xgroup_delconsumer(key, group, args[0])
|
|
274
|
+
else
|
|
275
|
+
raise ArgumentError, "Unknown XGROUP subcommand: #{subcommand}"
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
private
|
|
280
|
+
|
|
281
|
+
def xgroup_create_impl(key, group, id, **options)
|
|
282
|
+
args = [key, group, id]
|
|
283
|
+
args << "MKSTREAM" if options[:mkstream]
|
|
284
|
+
send_command(RequestType::X_GROUP_CREATE, args)
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
public
|
|
288
|
+
|
|
289
|
+
# Create a consumer group for a stream.
|
|
290
|
+
#
|
|
291
|
+
# @param [String] key stream key
|
|
292
|
+
# @param [String] group consumer group name
|
|
293
|
+
# @param [String] id starting ID ("0" for beginning, "$" for end)
|
|
294
|
+
# @param [Hash] options optional parameters
|
|
295
|
+
# - `:mkstream => true`: create stream if it doesn't exist
|
|
296
|
+
# @return [String] "OK"
|
|
297
|
+
#
|
|
298
|
+
# @example Create group from beginning
|
|
299
|
+
# valkey.xgroup_create("mystream", "mygroup", "0")
|
|
300
|
+
# @example Create group from end and create stream if needed
|
|
301
|
+
# valkey.xgroup_create("mystream", "mygroup", "$", mkstream: true)
|
|
302
|
+
#
|
|
303
|
+
# @see https://valkey.io/commands/xgroup-create/
|
|
304
|
+
def xgroup_create(key, group, id, **options)
|
|
305
|
+
xgroup_create_impl(key, group, id, **options)
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
# Create a consumer in a consumer group.
|
|
309
|
+
#
|
|
310
|
+
# @param [String] key stream key
|
|
311
|
+
# @param [String] group consumer group name
|
|
312
|
+
# @param [String] consumer consumer name
|
|
313
|
+
# @return [Integer] number of pending messages for the consumer (0 for new consumer)
|
|
314
|
+
#
|
|
315
|
+
# @example
|
|
316
|
+
# valkey.xgroup_createconsumer("mystream", "mygroup", "consumer1")
|
|
317
|
+
# # => 0
|
|
318
|
+
#
|
|
319
|
+
# @see https://valkey.io/commands/xgroup-createconsumer/
|
|
320
|
+
def xgroup_createconsumer(key, group, consumer)
|
|
321
|
+
send_command(RequestType::X_GROUP_CREATE_CONSUMER, [key, group, consumer]) do |reply|
|
|
322
|
+
# Convert boolean to integer if needed (backend may return boolean)
|
|
323
|
+
if reply.is_a?(TrueClass)
|
|
324
|
+
1
|
|
325
|
+
elsif reply.is_a?(FalseClass)
|
|
326
|
+
0
|
|
327
|
+
else
|
|
328
|
+
reply
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
# Set the last-delivered ID for a consumer group.
|
|
334
|
+
#
|
|
335
|
+
# @param [String] key stream key
|
|
336
|
+
# @param [String] group consumer group name
|
|
337
|
+
# @param [String] id entry ID
|
|
338
|
+
# @return [String] "OK"
|
|
339
|
+
#
|
|
340
|
+
# @example
|
|
341
|
+
# valkey.xgroup_setid("mystream", "mygroup", "1234567890-0")
|
|
342
|
+
#
|
|
343
|
+
# @see https://valkey.io/commands/xgroup-setid/
|
|
344
|
+
def xgroup_setid(key, group, id)
|
|
345
|
+
send_command(RequestType::X_GROUP_SET_ID, [key, group, id])
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
# Destroy a consumer group.
|
|
349
|
+
#
|
|
350
|
+
# @param [String] key stream key
|
|
351
|
+
# @param [String] group consumer group name
|
|
352
|
+
# @return [Integer] number of pending messages (if any)
|
|
353
|
+
#
|
|
354
|
+
# @example
|
|
355
|
+
# valkey.xgroup_destroy("mystream", "mygroup")
|
|
356
|
+
# # => 0
|
|
357
|
+
#
|
|
358
|
+
# @see https://valkey.io/commands/xgroup-destroy/
|
|
359
|
+
def xgroup_destroy(key, group)
|
|
360
|
+
send_command(RequestType::X_GROUP_DESTROY, [key, group]) do |reply|
|
|
361
|
+
# Convert boolean to integer if needed (backend may return boolean)
|
|
362
|
+
if reply.is_a?(TrueClass)
|
|
363
|
+
1
|
|
364
|
+
elsif reply.is_a?(FalseClass)
|
|
365
|
+
0
|
|
366
|
+
else
|
|
367
|
+
reply
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
# Remove a consumer from a consumer group.
|
|
373
|
+
#
|
|
374
|
+
# @param [String] key stream key
|
|
375
|
+
# @param [String] group consumer group name
|
|
376
|
+
# @param [String] consumer consumer name
|
|
377
|
+
# @return [Integer] number of pending messages for the consumer
|
|
378
|
+
#
|
|
379
|
+
# @example
|
|
380
|
+
# valkey.xgroup_delconsumer("mystream", "mygroup", "consumer1")
|
|
381
|
+
# # => 5
|
|
382
|
+
#
|
|
383
|
+
# @see https://valkey.io/commands/xgroup-delconsumer/
|
|
384
|
+
def xgroup_delconsumer(key, group, consumer)
|
|
385
|
+
send_command(RequestType::X_GROUP_DEL_CONSUMER, [key, group, consumer])
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
# Acknowledge one or more messages in a consumer group.
|
|
389
|
+
#
|
|
390
|
+
# @param [String] key stream key
|
|
391
|
+
# @param [String] group consumer group name
|
|
392
|
+
# @param [String, Array<String>] ids entry ID(s) to acknowledge
|
|
393
|
+
# @return [Integer] number of messages acknowledged
|
|
394
|
+
#
|
|
395
|
+
# @example Acknowledge a single message
|
|
396
|
+
# valkey.xack("mystream", "mygroup", "1234567890-0")
|
|
397
|
+
# # => 1
|
|
398
|
+
# @example Acknowledge multiple messages
|
|
399
|
+
# valkey.xack("mystream", "mygroup", ["1234567890-0", "1234567890-1"])
|
|
400
|
+
# # => 2
|
|
401
|
+
#
|
|
402
|
+
# @see https://valkey.io/commands/xack/
|
|
403
|
+
def xack(key, group, *ids)
|
|
404
|
+
args = [key, group] + Array(ids).flatten
|
|
405
|
+
send_command(RequestType::X_ACK, args)
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
# Get information about pending messages in a consumer group.
|
|
409
|
+
#
|
|
410
|
+
# @param [String] key stream key
|
|
411
|
+
# @param [String] group consumer group name
|
|
412
|
+
# @param [Array] args optional arguments (start, end, count, consumer)
|
|
413
|
+
# @param [Hash] options optional parameters
|
|
414
|
+
# - `:idle => Integer`: filter by minimum idle time in milliseconds
|
|
415
|
+
# @return [Hash, Array] pending information
|
|
416
|
+
# - Without args: summary hash with keys 'size', 'min_entry_id', 'max_entry_id', 'consumers'
|
|
417
|
+
# - With start/end/count: array of Hashes with keys 'entry_id', 'consumer', 'elapsed', and 'count'
|
|
418
|
+
#
|
|
419
|
+
# @example Get summary
|
|
420
|
+
# valkey.xpending("mystream", "mygroup")
|
|
421
|
+
# # => {"size" => 5, "min_entry_id" => "1234567890-0",
|
|
422
|
+
# # "max_entry_id" => "1234567890-4", "consumers" => {"consumer1" => 3, "consumer2" => 2}}
|
|
423
|
+
# @example Get detailed pending entries
|
|
424
|
+
# valkey.xpending("mystream", "mygroup", "-", "+", 10)
|
|
425
|
+
#
|
|
426
|
+
# @see https://valkey.io/commands/xpending/
|
|
427
|
+
def xpending(key, group, *args, idle: nil)
|
|
428
|
+
cmd_args = [key, group]
|
|
429
|
+
cmd_args.concat(args)
|
|
430
|
+
cmd_args << "IDLE" << idle.to_s if idle
|
|
431
|
+
|
|
432
|
+
send_command(RequestType::X_PENDING, cmd_args) do |reply|
|
|
433
|
+
# If args provided (start, end, count), return detailed format
|
|
434
|
+
# Otherwise return summary format
|
|
435
|
+
if args.length >= 2
|
|
436
|
+
Utils::HashifyStreamPendingDetails.call(reply)
|
|
437
|
+
else
|
|
438
|
+
Utils::HashifyStreamPendings.call(reply)
|
|
439
|
+
end
|
|
440
|
+
end
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
# Claim ownership of pending messages in a consumer group.
|
|
444
|
+
#
|
|
445
|
+
# @param [String] key stream key
|
|
446
|
+
# @param [String] group consumer group name
|
|
447
|
+
# @param [String] consumer consumer name
|
|
448
|
+
# @param [Integer] min_idle_time minimum idle time in milliseconds
|
|
449
|
+
# @param [String, Array<String>] ids entry ID(s) to claim
|
|
450
|
+
# @param [Hash] options optional parameters
|
|
451
|
+
# - `:idle => Integer`: set idle time in milliseconds
|
|
452
|
+
# - `:time => Integer`: set time in milliseconds (Unix timestamp)
|
|
453
|
+
# - `:retrycount => Integer`: set retry count
|
|
454
|
+
# - `:force => true`: claim even if already assigned
|
|
455
|
+
# - `:justid => true`: return only IDs
|
|
456
|
+
# @return [Array] array of claimed entries or IDs
|
|
457
|
+
#
|
|
458
|
+
# @example Claim pending messages
|
|
459
|
+
# valkey.xclaim("mystream", "mygroup", "consumer2", 3600000, ["1234567890-0"])
|
|
460
|
+
#
|
|
461
|
+
# @see https://valkey.io/commands/xclaim/
|
|
462
|
+
def xclaim(key, group, consumer, min_idle_time, ids, **options)
|
|
463
|
+
args = [key, group, consumer, min_idle_time.to_s]
|
|
464
|
+
args.concat(Array(ids).flatten)
|
|
465
|
+
|
|
466
|
+
args << "IDLE" << options[:idle].to_s if options[:idle]
|
|
467
|
+
args << "TIME" << options[:time].to_s if options[:time]
|
|
468
|
+
args << "RETRYCOUNT" << options[:retrycount].to_s if options[:retrycount]
|
|
469
|
+
args << "FORCE" if options[:force]
|
|
470
|
+
args << "JUSTID" if options[:justid]
|
|
471
|
+
|
|
472
|
+
send_command(RequestType::X_CLAIM, args) do |reply|
|
|
473
|
+
if options[:justid]
|
|
474
|
+
reply
|
|
475
|
+
else
|
|
476
|
+
Utils::HashifyStreamEntries.call(reply)
|
|
477
|
+
end
|
|
478
|
+
end
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
# Automatically claim pending messages that have been idle for a specified time.
|
|
482
|
+
#
|
|
483
|
+
# @param [String] key stream key
|
|
484
|
+
# @param [String] group consumer group name
|
|
485
|
+
# @param [String] consumer consumer name
|
|
486
|
+
# @param [Integer] min_idle_time minimum idle time in milliseconds
|
|
487
|
+
# @param [String] start start ID for scanning
|
|
488
|
+
# @param [Hash] options optional parameters
|
|
489
|
+
# - `:count => Integer`: maximum number of entries to claim
|
|
490
|
+
# - `:idle => Integer`: set idle time in milliseconds
|
|
491
|
+
# - `:time => Integer`: set time in milliseconds (Unix timestamp)
|
|
492
|
+
# - `:retrycount => Integer`: set retry count
|
|
493
|
+
# - `:justid => true`: return only IDs
|
|
494
|
+
# @return [Hash] hash with 'next' key for next cursor ID and 'entries' key for array of claimed entries
|
|
495
|
+
#
|
|
496
|
+
# @example Auto-claim pending messages
|
|
497
|
+
# valkey.xautoclaim("mystream", "mygroup", "consumer2", 3600000, "0-0")
|
|
498
|
+
# # => { 'next' => "1234567890-5", 'entries' => [["1234567890-0", ["field1", "value1"]]] }
|
|
499
|
+
#
|
|
500
|
+
# @see https://valkey.io/commands/xautoclaim/
|
|
501
|
+
def xautoclaim(key, group, consumer, min_idle_time, start, **options)
|
|
502
|
+
args = [key, group, consumer, min_idle_time.to_s, start]
|
|
503
|
+
|
|
504
|
+
args << "COUNT" << options[:count].to_s if options[:count]
|
|
505
|
+
args << "IDLE" << options[:idle].to_s if options[:idle]
|
|
506
|
+
args << "TIME" << options[:time].to_s if options[:time]
|
|
507
|
+
args << "RETRYCOUNT" << options[:retrycount].to_s if options[:retrycount]
|
|
508
|
+
args << "JUSTID" if options[:justid]
|
|
509
|
+
|
|
510
|
+
send_command(RequestType::X_AUTO_CLAIM, args) do |reply|
|
|
511
|
+
return { 'next' => '0-0', 'entries' => [] } if reply.nil? || !reply.is_a?(Array)
|
|
512
|
+
|
|
513
|
+
if options[:justid]
|
|
514
|
+
Utils::HashifyStreamAutoclaimJustId.call(reply)
|
|
515
|
+
else
|
|
516
|
+
Utils::HashifyStreamAutoclaim.call(reply)
|
|
517
|
+
end
|
|
518
|
+
end
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
# Get information about streams, groups, and consumers (dispatcher method).
|
|
522
|
+
#
|
|
523
|
+
# @param [Symbol, String] subcommand subcommand (:stream, :groups, :consumers)
|
|
524
|
+
# @param [String] key stream key
|
|
525
|
+
# @param [String] group optional consumer group name (required for :consumers)
|
|
526
|
+
# @param [Hash] options optional parameters (for :stream)
|
|
527
|
+
# - `:full => true`: return full information including entries
|
|
528
|
+
# - `:count => Integer`: limit number of entries (requires :full)
|
|
529
|
+
# @return [Hash, Array] depends on subcommand
|
|
530
|
+
#
|
|
531
|
+
# @example Get stream info
|
|
532
|
+
# valkey.xinfo(:stream, "mystream")
|
|
533
|
+
# @example Get stream info with full details
|
|
534
|
+
# valkey.xinfo(:stream, "mystream", full: true, count: 10)
|
|
535
|
+
# @example Get groups info
|
|
536
|
+
# valkey.xinfo(:groups, "mystream")
|
|
537
|
+
# @example Get consumers info
|
|
538
|
+
# valkey.xinfo(:consumers, "mystream", "mygroup")
|
|
539
|
+
#
|
|
540
|
+
# @see https://valkey.io/commands/xinfo/
|
|
541
|
+
def xinfo(subcommand, key, group = nil, **options)
|
|
542
|
+
subcommand = subcommand.to_s.downcase
|
|
543
|
+
case subcommand
|
|
544
|
+
when "stream"
|
|
545
|
+
args = [key]
|
|
546
|
+
if options[:full]
|
|
547
|
+
args << "FULL"
|
|
548
|
+
args << "COUNT" << options[:count].to_s if options[:count]
|
|
549
|
+
end
|
|
550
|
+
send_command(RequestType::X_INFO_STREAM, args)
|
|
551
|
+
when "groups"
|
|
552
|
+
send_command(RequestType::X_INFO_GROUPS, [key])
|
|
553
|
+
when "consumers"
|
|
554
|
+
raise ArgumentError, "Group name required for XINFO CONSUMERS" unless group
|
|
555
|
+
|
|
556
|
+
send_command(RequestType::X_INFO_CONSUMERS, [key, group])
|
|
557
|
+
else
|
|
558
|
+
raise ArgumentError, "Unknown XINFO subcommand: #{subcommand}"
|
|
559
|
+
end
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
# Get information about a stream.
|
|
563
|
+
#
|
|
564
|
+
# @param [String] key stream key
|
|
565
|
+
# @param [Hash] options optional parameters
|
|
566
|
+
# - `:full => true`: return full information including entries
|
|
567
|
+
# - `:count => Integer`: limit number of entries (requires :full)
|
|
568
|
+
# @return [Array] stream information as flat array of key-value pairs
|
|
569
|
+
#
|
|
570
|
+
# @example Get basic stream info
|
|
571
|
+
# valkey.xinfo_stream("mystream")
|
|
572
|
+
# # => ["length", 42, "radix-tree-keys", 1, ...]
|
|
573
|
+
# @example Get full info with entries
|
|
574
|
+
# valkey.xinfo_stream("mystream", full: true, count: 10)
|
|
575
|
+
#
|
|
576
|
+
# @see https://valkey.io/commands/xinfo-stream/
|
|
577
|
+
def xinfo_stream(key, **options)
|
|
578
|
+
xinfo(:stream, key, **options)
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
# Get information about consumer groups of a stream.
|
|
582
|
+
#
|
|
583
|
+
# @param [String] key stream key
|
|
584
|
+
# @return [Array] array of consumer group information hashes
|
|
585
|
+
#
|
|
586
|
+
# @example
|
|
587
|
+
# valkey.xinfo_groups("mystream")
|
|
588
|
+
# # => [{"name" => "mygroup", "consumers" => 2, "pending" => 5, ...}]
|
|
589
|
+
#
|
|
590
|
+
# @see https://valkey.io/commands/xinfo-groups/
|
|
591
|
+
def xinfo_groups(key)
|
|
592
|
+
xinfo(:groups, key)
|
|
593
|
+
end
|
|
594
|
+
|
|
595
|
+
# Get information about consumers in a consumer group.
|
|
596
|
+
#
|
|
597
|
+
# @param [String] key stream key
|
|
598
|
+
# @param [String] group consumer group name
|
|
599
|
+
# @return [Array] array of consumer information hashes
|
|
600
|
+
#
|
|
601
|
+
# @example
|
|
602
|
+
# valkey.xinfo_consumers("mystream", "mygroup")
|
|
603
|
+
# # => [{"name" => "consumer1", "pending" => 3, "idle" => 12345, ...}]
|
|
604
|
+
#
|
|
605
|
+
# @see https://valkey.io/commands/xinfo-consumers/
|
|
606
|
+
def xinfo_consumers(key, group)
|
|
607
|
+
xinfo(:consumers, key, group)
|
|
608
|
+
end
|
|
609
|
+
|
|
610
|
+
# TODO: Implement xsetid command after enabling in glide-core
|
|
611
|
+
# Set the ID of the last entry in a stream.
|
|
612
|
+
#
|
|
613
|
+
# @param [String] key stream key
|
|
614
|
+
# @param [String] id entry ID
|
|
615
|
+
# @param [Hash] options optional parameters
|
|
616
|
+
# - `:entries_added => Integer`: set entries-added counter
|
|
617
|
+
# - `:max_deleted_id => String`: set max-deleted-id
|
|
618
|
+
# @return [String] "OK"
|
|
619
|
+
#
|
|
620
|
+
# @example
|
|
621
|
+
# valkey.xsetid("mystream", "1234567890-0")
|
|
622
|
+
# @example With additional options
|
|
623
|
+
# valkey.xsetid("mystream", "1234567890-0", entries_added: 100, max_deleted_id: "1234567890-50")
|
|
624
|
+
#
|
|
625
|
+
# @see https://valkey.io/commands/xsetid/
|
|
626
|
+
# def xsetid(key, id, **options)
|
|
627
|
+
# args = [key, id]
|
|
628
|
+
|
|
629
|
+
# args << "ENTRIESADDED" << options[:entries_added].to_s if options[:entries_added]
|
|
630
|
+
# args << "MAXDELETEDID" << options[:max_deleted_id] if options[:max_deleted_id]
|
|
631
|
+
|
|
632
|
+
# send_command(RequestType::X_SET_ID, args)
|
|
633
|
+
# end
|
|
634
|
+
end
|
|
635
|
+
end
|
|
636
|
+
end
|