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.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +58 -0
  3. data/.rubocop_todo.yml +22 -0
  4. data/README.md +95 -0
  5. data/Rakefile +23 -0
  6. data/lib/valkey/bindings.rb +224 -0
  7. data/lib/valkey/commands/bitmap_commands.rb +86 -0
  8. data/lib/valkey/commands/cluster_commands.rb +259 -0
  9. data/lib/valkey/commands/connection_commands.rb +318 -0
  10. data/lib/valkey/commands/function_commands.rb +255 -0
  11. data/lib/valkey/commands/generic_commands.rb +525 -0
  12. data/lib/valkey/commands/geo_commands.rb +87 -0
  13. data/lib/valkey/commands/hash_commands.rb +587 -0
  14. data/lib/valkey/commands/hyper_log_log_commands.rb +51 -0
  15. data/lib/valkey/commands/json_commands.rb +389 -0
  16. data/lib/valkey/commands/list_commands.rb +348 -0
  17. data/lib/valkey/commands/module_commands.rb +125 -0
  18. data/lib/valkey/commands/pubsub_commands.rb +237 -0
  19. data/lib/valkey/commands/scripting_commands.rb +286 -0
  20. data/lib/valkey/commands/server_commands.rb +961 -0
  21. data/lib/valkey/commands/set_commands.rb +220 -0
  22. data/lib/valkey/commands/sorted_set_commands.rb +971 -0
  23. data/lib/valkey/commands/stream_commands.rb +636 -0
  24. data/lib/valkey/commands/string_commands.rb +359 -0
  25. data/lib/valkey/commands/transaction_commands.rb +175 -0
  26. data/lib/valkey/commands/vector_search_commands.rb +271 -0
  27. data/lib/valkey/commands.rb +68 -0
  28. data/lib/valkey/errors.rb +41 -0
  29. data/lib/valkey/libglide_ffi.so +0 -0
  30. data/lib/valkey/opentelemetry.rb +207 -0
  31. data/lib/valkey/pipeline.rb +20 -0
  32. data/lib/valkey/protobuf/command_request_pb.rb +51 -0
  33. data/lib/valkey/protobuf/connection_request_pb.rb +51 -0
  34. data/lib/valkey/protobuf/response_pb.rb +39 -0
  35. data/lib/valkey/pubsub_callback.rb +10 -0
  36. data/lib/valkey/request_error_type.rb +10 -0
  37. data/lib/valkey/request_type.rb +436 -0
  38. data/lib/valkey/response_type.rb +20 -0
  39. data/lib/valkey/utils.rb +253 -0
  40. data/lib/valkey/version.rb +5 -0
  41. data/lib/valkey.rb +551 -0
  42. metadata +119 -0
@@ -0,0 +1,259 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Valkey
4
+ module Commands
5
+ # This module contains commands related to Redis Cluster management.
6
+ #
7
+ # @see https://valkey.io/commands/#cluster
8
+ #
9
+ module ClusterCommands
10
+ # Send ASKING command to the server.
11
+ #
12
+ # @return [String] `"OK"`
13
+ def asking
14
+ send_command(RequestType::ASKING)
15
+ end
16
+
17
+ # Add slots to the cluster.
18
+ #
19
+ # @param [Array<Integer>] slots array of slot numbers
20
+ # @return [String] `"OK"`
21
+ def cluster_addslots(*slots)
22
+ send_command(RequestType::CLUSTER_ADD_SLOTS, slots)
23
+ end
24
+
25
+ # Add a range of slots to the cluster.
26
+ #
27
+ # @param [Integer] start_slot starting slot number
28
+ # @param [Integer] end_slot ending slot number
29
+ # @return [String] `"OK"`
30
+ def cluster_addslotsrange(start_slot, end_slot)
31
+ send_command(RequestType::CLUSTER_ADD_SLOTS_RANGE, [start_slot, end_slot])
32
+ end
33
+
34
+ # Bump the epoch of the cluster.
35
+ #
36
+ # @return [String] `"OK"`
37
+ def cluster_bumpepoch
38
+ send_command(RequestType::CLUSTER_BUMP_EPOCH)
39
+ end
40
+
41
+ # Count failure reports for a node.
42
+ #
43
+ # @param [String] node_id the node ID
44
+ # @return [Integer] number of failure reports
45
+ def cluster_count_failure_reports(node_id)
46
+ send_command(RequestType::CLUSTER_COUNT_FAILURE_REPORTS, [node_id])
47
+ end
48
+
49
+ # Count keys in a specific slot.
50
+ #
51
+ # @param [Integer] slot the slot number
52
+ # @return [Integer] number of keys in the slot
53
+ def cluster_countkeysinslot(slot)
54
+ send_command(RequestType::CLUSTER_COUNT_KEYS_IN_SLOT, [slot])
55
+ end
56
+
57
+ # Delete slots from the cluster.
58
+ #
59
+ # @param [Array<Integer>] slots array of slot numbers
60
+ # @return [String] `"OK"`
61
+ def cluster_delslots(*slots)
62
+ send_command(RequestType::CLUSTER_DEL_SLOTS, slots)
63
+ end
64
+
65
+ # Delete a range of slots from the cluster.
66
+ #
67
+ # @param [Integer] start_slot starting slot number
68
+ # @param [Integer] end_slot ending slot number
69
+ # @return [String] `"OK"`
70
+ def cluster_delslotsrange(start_slot, end_slot)
71
+ send_command(RequestType::CLUSTER_DEL_SLOTS_RANGE, [start_slot, end_slot])
72
+ end
73
+
74
+ # Force a failover of the cluster.
75
+ #
76
+ # @param [String] force force the failover
77
+ # @return [String] `"OK"`
78
+ def cluster_failover(force = nil)
79
+ args = []
80
+ args << "FORCE" if force
81
+ send_command(RequestType::CLUSTER_FAILOVER, args)
82
+ end
83
+
84
+ # Flush all slots from the cluster.
85
+ #
86
+ # @return [String] `"OK"`
87
+ def cluster_flushslots
88
+ send_command(RequestType::CLUSTER_FLUSH_SLOTS)
89
+ end
90
+
91
+ # Remove a node from the cluster.
92
+ #
93
+ # @param [String] node_id the node ID to forget
94
+ # @return [String] `"OK"`
95
+ def cluster_forget(node_id)
96
+ send_command(RequestType::CLUSTER_FORGET, [node_id])
97
+ end
98
+
99
+ # Get keys in a specific slot.
100
+ #
101
+ # @param [Integer] slot the slot number
102
+ # @param [Integer] count maximum number of keys to return
103
+ # @return [Array<String>] array of keys
104
+ def cluster_getkeysinslot(slot, count)
105
+ send_command(RequestType::CLUSTER_GET_KEYS_IN_SLOT, [slot, count])
106
+ end
107
+
108
+ # Get information about the cluster.
109
+ #
110
+ # @return [Hash<String, String>] cluster information
111
+ def cluster_info
112
+ send_command(RequestType::CLUSTER_INFO) do |reply|
113
+ Utils::HashifyInfo.call(reply)
114
+ end
115
+ end
116
+
117
+ # Get the slot for a key.
118
+ #
119
+ # @param [String] key the key name
120
+ # @return [Integer] slot number
121
+ def cluster_keyslot(key)
122
+ send_command(RequestType::CLUSTER_KEY_SLOT, [key])
123
+ end
124
+
125
+ # Get information about cluster links.
126
+ #
127
+ # @return [Array<Hash>] array of link information
128
+ def cluster_links
129
+ send_command(RequestType::CLUSTER_LINKS)
130
+ end
131
+
132
+ # Meet another node in the cluster.
133
+ #
134
+ # @param [String] ip IP address of the node
135
+ # @param [Integer] port port of the node
136
+ # @return [String] `"OK"`
137
+ def cluster_meet(ip, port)
138
+ send_command(RequestType::CLUSTER_MEET, [ip, port])
139
+ end
140
+
141
+ # Get the ID of the current node.
142
+ #
143
+ # @return [String] node ID
144
+ def cluster_myid
145
+ send_command(RequestType::CLUSTER_MY_ID)
146
+ end
147
+
148
+ # Get the shard ID of the current node.
149
+ #
150
+ # @return [String] shard ID
151
+ def cluster_myshardid
152
+ send_command(RequestType::CLUSTER_MY_SHARD_ID)
153
+ end
154
+
155
+ # Get information about all nodes in the cluster.
156
+ #
157
+ # @return [Array<Hash>] array of node information
158
+ def cluster_nodes
159
+ send_command(RequestType::CLUSTER_NODES) do |reply|
160
+ Utils::HashifyClusterNodes.call(reply)
161
+ end
162
+ end
163
+
164
+ # Get information about replica nodes.
165
+ #
166
+ # @param [String] node_id the master node ID
167
+ # @return [Array<Hash>] array of replica information
168
+ def cluster_replicas(node_id)
169
+ send_command(RequestType::CLUSTER_REPLICAS, [node_id]) do |reply|
170
+ Utils::HashifyClusterSlaves.call(reply)
171
+ end
172
+ end
173
+
174
+ # Set a node as a replica of another node.
175
+ #
176
+ # @param [String] node_id the master node ID
177
+ # @return [String] `"OK"`
178
+ def cluster_replicate(node_id)
179
+ send_command(RequestType::CLUSTER_REPLICATE, [node_id])
180
+ end
181
+
182
+ # Reset the cluster.
183
+ #
184
+ # @param [String] hard hard reset
185
+ # @return [String] `"OK"`
186
+ def cluster_reset(hard = nil)
187
+ args = []
188
+ args << "HARD" if hard
189
+ send_command(RequestType::CLUSTER_RESET, args)
190
+ end
191
+
192
+ # Save the cluster configuration.
193
+ #
194
+ # @return [String] `"OK"`
195
+ def cluster_saveconfig
196
+ send_command(RequestType::CLUSTER_SAVE_CONFIG)
197
+ end
198
+
199
+ # Set the config epoch for a node.
200
+ #
201
+ # @param [Integer] epoch the config epoch
202
+ # @return [String] `"OK"`
203
+ def cluster_set_config_epoch(epoch)
204
+ send_command(RequestType::CLUSTER_SET_CONFIG_EPOCH, [epoch])
205
+ end
206
+
207
+ # Set the state of a slot.
208
+ #
209
+ # @param [Integer] slot the slot number
210
+ # @param [String] state the state (importing, migrating, node, stable)
211
+ # @param [String] node_id the node ID (optional)
212
+ # @return [String] `"OK"`
213
+ def cluster_setslot(slot, state, node_id = nil)
214
+ args = [slot, state]
215
+ args << node_id if node_id
216
+ send_command(RequestType::CLUSTER_SETSLOT, args)
217
+ end
218
+
219
+ # Get information about cluster shards.
220
+ #
221
+ # @return [Array<Hash>] array of shard information
222
+ def cluster_shards
223
+ send_command(RequestType::CLUSTER_SHARDS)
224
+ end
225
+
226
+ # Get information about slave nodes (deprecated, use cluster_replicas).
227
+ #
228
+ # @return [Array<Hash>] array of slave information
229
+ def cluster_slaves(node_id)
230
+ send_command(RequestType::CLUSTER_SLAVES, [node_id]) do |reply|
231
+ Utils::HashifyClusterSlaves.call(reply)
232
+ end
233
+ end
234
+
235
+ # Get information about cluster slots.
236
+ #
237
+ # @return [Array<Hash>] array of slot information
238
+ def cluster_slots
239
+ send_command(RequestType::CLUSTER_SLOTS) do |reply|
240
+ Utils::HashifyClusterSlots.call(reply)
241
+ end
242
+ end
243
+
244
+ # Set the connection to read-only mode.
245
+ #
246
+ # @return [String] "OK"
247
+ def readonly
248
+ send_command(RequestType::READ_ONLY)
249
+ end
250
+
251
+ # Set the connection to read-write mode.
252
+ #
253
+ # @return [String] "OK"
254
+ def readwrite
255
+ send_command(RequestType::READ_WRITE)
256
+ end
257
+ end
258
+ end
259
+ end
@@ -0,0 +1,318 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Valkey
4
+ module Commands
5
+ # This module contains commands related to connection management.
6
+ #
7
+ # @see https://valkey.io/commands/#connection
8
+ #
9
+ module ConnectionCommands
10
+ # Authenticate to the server.
11
+ #
12
+ # @param [Array<String>] args includes both username and password
13
+ # or only password
14
+ # @return [String] `OK`
15
+ def auth(*args)
16
+ send_command(RequestType::AUTH, args)
17
+ end
18
+
19
+ # Ping the server.
20
+ #
21
+ # @param [optional, String] message
22
+ # @return [String] `PONG`
23
+ def ping(message = nil)
24
+ send_command(RequestType::PING, [message].compact)
25
+ end
26
+
27
+ # Echo the given string.
28
+ #
29
+ # @param [String] value
30
+ # @return [String]
31
+ def echo(value)
32
+ send_command(RequestType::ECHO, [value])
33
+ end
34
+
35
+ # Change the selected database for the current connection.
36
+ #
37
+ # @param [Integer] db zero-based index of the DB to use (0 to 15)
38
+ # @return [String] `OK`
39
+ def select(db)
40
+ send_command(RequestType::SELECT, [db])
41
+ end
42
+
43
+ # Close the connection.
44
+ #
45
+ # @deprecated The QUIT command is deprecated since Redis 7.2.0 / Valkey 7.2+.
46
+ # Clients should use the `close` method directly instead.
47
+ # This avoids lingering TIME_WAIT sockets on the server side.
48
+ #
49
+ # @return [String] `OK` or nil if connection already closed
50
+ # @see https://redis.io/docs/latest/commands/quit/
51
+ def quit
52
+ # For compatibility, we still support QUIT but recommend using close() instead
53
+ send_command(RequestType::QUIT)
54
+ rescue ConnectionError
55
+ # Server closes connection immediately after QUIT
56
+ nil
57
+ ensure
58
+ # Clean up our side of the connection
59
+ close if respond_to?(:close)
60
+ end
61
+
62
+ # Switch to a different protocol version and handshake with the server.
63
+ #
64
+ # @param [Integer] protover Protocol version (2 or 3)
65
+ # @param [Hash] options Optional parameters like AUTH, SETNAME
66
+ # @return [Array] Server information as flat array (TODO: should be Hash for RESP3)
67
+ def hello(protover = 3, **options)
68
+ args = [protover]
69
+
70
+ if options[:auth]
71
+ args << "AUTH"
72
+ args.concat(Array(options[:auth]))
73
+ end
74
+
75
+ args << "SETNAME" << options[:setname] if options[:setname]
76
+
77
+ send_command(RequestType::HELLO, args)
78
+ end
79
+
80
+ # Reset the connection state.
81
+ #
82
+ # @return [String] `RESET`
83
+ def reset
84
+ send_command(RequestType::RESET)
85
+ end
86
+
87
+ # Send a generic CLIENT subcommand.
88
+ #
89
+ # @param [Symbol, String] subcommand The CLIENT subcommand to run, e.g. :list, :id, :kill, etc.
90
+ # @param [Array] args Arguments for the subcommand
91
+ # @return [Object] Depends on subcommand
92
+ # @example
93
+ # client(:id) # => 12345
94
+ # client(:set_name, "my_app") # => "OK"
95
+ # client(:list) # => [{"id" => "1", ...}, ...]
96
+ def client(subcommand, *args)
97
+ send("client_#{subcommand.to_s.downcase}", *args)
98
+ end
99
+
100
+ # Get the current client's ID.
101
+ #
102
+ # @return [Integer] Unique client ID
103
+ def client_id
104
+ send_command(RequestType::CLIENT_ID)
105
+ end
106
+
107
+ # Get the current client's name.
108
+ #
109
+ # @return [String, nil] Client name or nil if not set
110
+ def client_get_name
111
+ send_command(RequestType::CLIENT_GET_NAME)
112
+ end
113
+
114
+ # Set the current client's name.
115
+ #
116
+ # @param [String] name New name for the client connection
117
+ # @return [String] `OK`
118
+ def client_set_name(name)
119
+ send_command(RequestType::CLIENT_SET_NAME, [name])
120
+ end
121
+
122
+ # Get a list of client connections.
123
+ #
124
+ # @param [String] type Optional filter by client type (normal, master, slave, pubsub)
125
+ # @param [Array<String>] ids Optional filter by client IDs
126
+ # @return [Array<Hash>] List of clients, each represented as a Hash of attributes
127
+ def client_list(type: nil, ids: nil)
128
+ args = []
129
+
130
+ args << "TYPE" << type if type
131
+
132
+ if ids
133
+ args << "ID"
134
+ args.concat(Array(ids))
135
+ end
136
+
137
+ send_command(RequestType::CLIENT_LIST, args) do |reply|
138
+ reply.lines.map do |line|
139
+ entries = line.chomp.split(/[ =]/)
140
+ entries.each_slice(2).to_a.to_h
141
+ end
142
+ end
143
+ end
144
+
145
+ # Get information about the current client connection.
146
+ #
147
+ # @return [String] Client connection information
148
+ def client_info
149
+ send_command(RequestType::CLIENT_INFO)
150
+ end
151
+
152
+ # Kill client connections.
153
+ #
154
+ # @param [String] addr Client address (ip:port)
155
+ # @param [Hash] options Optional filters (id, type, user, addr, laddr, skipme)
156
+ # @return [Integer] Number of clients killed
157
+ def client_kill(addr = nil, **options)
158
+ if addr && options.empty?
159
+ send_command(RequestType::CLIENT_KILL_SIMPLE, [addr])
160
+ else
161
+ send_command(RequestType::CLIENT_KILL, build_client_kill_args(addr, options))
162
+ end
163
+ end
164
+
165
+ # Kill a client connection by address (simple form).
166
+ #
167
+ # @param [String] addr Client address (ip:port)
168
+ # @return [String] `OK`
169
+ def client_kill_simple(addr)
170
+ send_command(RequestType::CLIENT_KILL_SIMPLE, [addr])
171
+ end
172
+
173
+ private
174
+
175
+ def build_client_kill_args(addr, options)
176
+ args = []
177
+ args << "ADDR" << addr if addr
178
+ options.each do |key, value|
179
+ case key
180
+ when :id then args << "ID" << value.to_s
181
+ when :type then args << "TYPE" << value.to_s
182
+ when :user then args << "USER" << value.to_s
183
+ when :addr then args << "ADDR" << value.to_s
184
+ when :laddr then args << "LADDR" << value.to_s
185
+ when :skipme then args << "SKIPME" << (value ? "yes" : "no")
186
+ end
187
+ end
188
+ args
189
+ end
190
+
191
+ public
192
+
193
+ # Pause client processing.
194
+ #
195
+ # @param [Integer] timeout Pause duration in milliseconds
196
+ # @param [String] mode Optional mode (WRITE, ALL)
197
+ # @return [String] `OK`
198
+ def client_pause(timeout, mode = nil)
199
+ args = [timeout]
200
+ args << mode if mode
201
+ send_command(RequestType::CLIENT_PAUSE, args)
202
+ end
203
+
204
+ # Unpause client processing.
205
+ #
206
+ # @return [String] `OK`
207
+ def client_unpause
208
+ send_command(RequestType::CLIENT_UNPAUSE)
209
+ end
210
+
211
+ # Configure client reply mode.
212
+ #
213
+ # @param [String] mode Reply mode (ON, OFF, SKIP)
214
+ # @return [String] `OK`
215
+ def client_reply(mode)
216
+ send_command(RequestType::CLIENT_REPLY, [mode])
217
+ end
218
+
219
+ # Unblock a client blocked in a blocking operation.
220
+ #
221
+ # @param [Integer] client_id ID of the client to unblock
222
+ # @param [String] unblock_type Optional unblock type (TIMEOUT, ERROR)
223
+ # @return [Integer] 1 if client was unblocked, 0 otherwise
224
+ def client_unblock(client_id, unblock_type = nil)
225
+ args = [client_id]
226
+ args << unblock_type if unblock_type
227
+ send_command(RequestType::CLIENT_UNBLOCK, args)
228
+ end
229
+
230
+ # Set client connection information.
231
+ #
232
+ # @param [String] attr Attribute to set (lib-name, lib-ver)
233
+ # @param [String] value Value to set for the attribute
234
+ # @return [String] `OK`
235
+ def client_set_info(attr, value)
236
+ send_command(RequestType::CLIENT_SET_INFO, [attr, value])
237
+ end
238
+
239
+ # Enable/disable client caching.
240
+ #
241
+ # @param [String] mode Caching mode (YES, NO)
242
+ # @return [String] `OK`
243
+ def client_caching(mode)
244
+ send_command(RequestType::CLIENT_CACHING, [mode])
245
+ end
246
+
247
+ # Configure client tracking.
248
+ #
249
+ # @param [String] status Tracking status (ON, OFF)
250
+ # @param [Array] args Additional positional arguments (REDIRECT, PREFIX, BCAST, OPTIN, OPTOUT, NOLOOP)
251
+ # @param [Hash] options Optional parameters (for keyword argument style)
252
+ # @return [String] `OK`
253
+ # @example Positional style
254
+ # client_tracking("ON", "OPTIN")
255
+ # @example Keyword style
256
+ # client_tracking("ON", optin: true, redirect: 123)
257
+ def client_tracking(status, *args, **options)
258
+ cmd_args = [status]
259
+ cmd_args.concat(args.any? ? args : build_client_tracking_args(options))
260
+ send_command(RequestType::CLIENT_TRACKING, cmd_args)
261
+ end
262
+
263
+ # Get client tracking information.
264
+ #
265
+ # @return [Array] Tracking information
266
+ def client_tracking_info
267
+ send_command(RequestType::CLIENT_TRACKING_INFO)
268
+ end
269
+
270
+ # Get the client ID used for tracking redirection.
271
+ #
272
+ # @return [Integer] Client ID for tracking redirection
273
+ def client_getredir
274
+ send_command(RequestType::CLIENT_GET_REDIR)
275
+ end
276
+
277
+ # Enable/disable client no-evict mode.
278
+ #
279
+ # @param [String] mode Mode (ON, OFF)
280
+ # @return [String] `OK`
281
+ def client_no_evict(mode)
282
+ send_command(RequestType::CLIENT_NO_EVICT, [mode.to_s.upcase])
283
+ end
284
+
285
+ # Enable/disable client no-touch mode.
286
+ #
287
+ # @param [String] mode Mode (ON, OFF)
288
+ # @return [String] `OK`
289
+ def client_no_touch(mode)
290
+ send_command(RequestType::CLIENT_NO_TOUCH, [mode.to_s.upcase])
291
+ end
292
+
293
+ private
294
+
295
+ def build_client_tracking_args(options)
296
+ args = []
297
+ options.each do |key, value|
298
+ case key
299
+ when :redirect
300
+ args << "REDIRECT" << value.to_s
301
+ when :prefix
302
+ args << "PREFIX"
303
+ Array(value).each { |prefix| args << prefix }
304
+ when :bcast
305
+ args << "BCAST" if value
306
+ when :optin
307
+ args << "OPTIN" if value
308
+ when :optout
309
+ args << "OPTOUT" if value
310
+ when :noloop
311
+ args << "NOLOOP" if value
312
+ end
313
+ end
314
+ args
315
+ end
316
+ end
317
+ end
318
+ end