valkey-glide-rb 0.9.0.pre.rc1

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 (50) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +69 -0
  3. data/.rubocop_todo.yml +22 -0
  4. data/AGENTS.md +224 -0
  5. data/CLAUDE.md +130 -0
  6. data/CONTRIBUTING.md +115 -0
  7. data/DEVELOPER.md +453 -0
  8. data/README.md +268 -0
  9. data/Rakefile +126 -0
  10. data/examples/README.md +55 -0
  11. data/examples/cluster.rb +24 -0
  12. data/examples/opentelemetry.rb +50 -0
  13. data/examples/pipelining.rb +29 -0
  14. data/examples/standalone.rb +22 -0
  15. data/examples/statistics.rb +24 -0
  16. data/lib/valkey/bindings.rb +319 -0
  17. data/lib/valkey/commands/bitmap_commands.rb +86 -0
  18. data/lib/valkey/commands/cluster_commands.rb +259 -0
  19. data/lib/valkey/commands/connection_commands.rb +318 -0
  20. data/lib/valkey/commands/function_commands.rb +255 -0
  21. data/lib/valkey/commands/generic_commands.rb +525 -0
  22. data/lib/valkey/commands/geo_commands.rb +87 -0
  23. data/lib/valkey/commands/hash_commands.rb +592 -0
  24. data/lib/valkey/commands/hyper_log_log_commands.rb +51 -0
  25. data/lib/valkey/commands/json_commands.rb +389 -0
  26. data/lib/valkey/commands/list_commands.rb +348 -0
  27. data/lib/valkey/commands/module_commands.rb +125 -0
  28. data/lib/valkey/commands/pubsub_commands.rb +237 -0
  29. data/lib/valkey/commands/scripting_commands.rb +287 -0
  30. data/lib/valkey/commands/server_commands.rb +961 -0
  31. data/lib/valkey/commands/set_commands.rb +220 -0
  32. data/lib/valkey/commands/sorted_set_commands.rb +971 -0
  33. data/lib/valkey/commands/stream_commands.rb +636 -0
  34. data/lib/valkey/commands/string_commands.rb +371 -0
  35. data/lib/valkey/commands/transaction_commands.rb +175 -0
  36. data/lib/valkey/commands/vector_search_commands.rb +271 -0
  37. data/lib/valkey/commands.rb +68 -0
  38. data/lib/valkey/errors.rb +41 -0
  39. data/lib/valkey/native/aarch64-unknown-linux-gnu/libglide_ffi.so +0 -0
  40. data/lib/valkey/native/x86_64-unknown-linux-gnu/libglide_ffi.so +0 -0
  41. data/lib/valkey/opentelemetry.rb +207 -0
  42. data/lib/valkey/pipeline.rb +20 -0
  43. data/lib/valkey/pubsub_callback.rb +10 -0
  44. data/lib/valkey/request_error_type.rb +10 -0
  45. data/lib/valkey/request_type.rb +436 -0
  46. data/lib/valkey/response_type.rb +20 -0
  47. data/lib/valkey/utils.rb +267 -0
  48. data/lib/valkey/version.rb +5 -0
  49. data/lib/valkey.rb +617 -0
  50. metadata +107 -0
@@ -0,0 +1,319 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Valkey
4
+ module Bindings
5
+ extend FFI::Library
6
+
7
+ # Determine platform-specific library extension and directory name
8
+ def self.platform_info
9
+ os = if FFI::Platform.mac?
10
+ "darwin"
11
+ elsif FFI::Platform.windows?
12
+ "windows"
13
+ else
14
+ "linux"
15
+ end
16
+
17
+ # Detect architecture
18
+ arch = case RbConfig::CONFIG["host_cpu"]
19
+ when /x86_64|amd64/i
20
+ "x86_64"
21
+ when /aarch64|arm64/i
22
+ "aarch64"
23
+ when /i[3-6]86/i
24
+ "x86"
25
+ else
26
+ RbConfig::CONFIG["host_cpu"]
27
+ end
28
+
29
+ lib_ext = case os
30
+ when "darwin"
31
+ "dylib"
32
+ when "windows"
33
+ "dll"
34
+ else
35
+ "so"
36
+ end
37
+
38
+ # Platform directory name matches Rust target triple convention
39
+ platform_dir = case os
40
+ when "darwin"
41
+ "#{arch}-apple-darwin"
42
+ when "linux"
43
+ "#{arch}-unknown-linux-gnu"
44
+ when "windows"
45
+ "#{arch}-pc-windows-msvc"
46
+ end
47
+
48
+ { os: os, arch: arch, lib_ext: lib_ext, platform_dir: platform_dir }
49
+ end
50
+
51
+ platform = platform_info
52
+ lib_ext = platform[:lib_ext]
53
+ platform_dir = platform[:platform_dir]
54
+
55
+ # Look for the native library in the following locations (in order):
56
+ # 1. valkey-glide submodule build output (development/source builds)
57
+ # 2. Platform-specific bundled library in lib/valkey/native/{platform}/ (for gem distribution)
58
+ # 3. Legacy bundled library in lib/valkey (fallback for old gem structure)
59
+ lib_paths = [
60
+ # Submodule build output (release) - from lib/valkey/ go up 2 levels to repo root
61
+ File.expand_path("../../valkey-glide/ffi/target/release/libglide_ffi.#{lib_ext}", __dir__),
62
+ # Submodule build output (debug)
63
+ File.expand_path("../../valkey-glide/ffi/target/debug/libglide_ffi.#{lib_ext}", __dir__),
64
+ # Platform-specific bundled library (for gem distribution)
65
+ File.expand_path("./native/#{platform_dir}/libglide_ffi.#{lib_ext}", __dir__),
66
+ # Legacy bundled library (fallback)
67
+ File.expand_path("./libglide_ffi.#{lib_ext}", __dir__)
68
+ ]
69
+
70
+ lib_path = lib_paths.find { |path| File.exist?(path) }
71
+
72
+ unless lib_path
73
+ raise LoadError, <<~ERROR
74
+ Could not find libglide_ffi native library for platform: #{platform_dir}
75
+
76
+ Searched in:
77
+ #{lib_paths.map { |p| " - #{p}" }.join("\n")}
78
+
79
+ To build from source:
80
+ 1. Initialize the submodule: git submodule update --init --recursive
81
+ 2. Build the FFI library: cd valkey-glide/ffi && cargo build --release
82
+
83
+ Detected platform: OS=#{platform[:os]}, Arch=#{platform[:arch]}
84
+ ERROR
85
+ end
86
+
87
+ ffi_lib lib_path
88
+
89
+ class ClientType < FFI::Struct
90
+ layout(
91
+ :tag, :uint # 0 = AsyncClient, 1 = SyncClient
92
+ )
93
+ end
94
+
95
+ class ConnectionResponse < FFI::Struct
96
+ layout(
97
+ :conn_ptr, :pointer, # *const c_void
98
+ :connection_error_message, :string # *const c_char (null-terminated C string)
99
+ )
100
+ end
101
+
102
+ class CommandError < FFI::Struct
103
+ layout(
104
+ :command_error_message, :string,
105
+ :command_error_type, :int # Assuming RequestErrorType is repr(C) enum
106
+ )
107
+ end
108
+
109
+ class BatchOptionsInfo < FFI::Struct
110
+ layout(
111
+ :retry_server_error, :bool,
112
+ :retry_connection_error, :bool,
113
+ :has_timeout, :bool,
114
+ :timeout, :uint, # Assuming u32 is represented as uint in C
115
+ :route_info, :pointer # *const RouteInfo
116
+ )
117
+ end
118
+
119
+ class CmdInfo < FFI::Struct
120
+ layout(
121
+ :request_type, :int, # Assuming RequestType is repr(C) enum
122
+ :args, :pointer, # *const *const u8 (pointer to array of pointers to args)
123
+ :arg_count, :ulong, # usize (number of arguments)
124
+ :args_len, :pointer # *const usize (pointer to array of argument lengths)
125
+ )
126
+ end
127
+
128
+ class ScriptHashBuffer < FFI::Struct
129
+ layout(
130
+ :ptr, :pointer, # *mut u8 (pointer to the script hash)
131
+ :len, :ulong, # usize (length of the script hash)
132
+ :capacity, :ulong # usize (capacity of the buffer)
133
+ )
134
+ end
135
+
136
+ class BatchInfo < FFI::Struct
137
+ layout(
138
+ :cmd_count, :ulong, # usize
139
+ :cmds, :pointer, # *const *const CmdInfo
140
+ :is_atomic, :bool # bool
141
+ )
142
+ end
143
+
144
+ class CommandResponse < FFI::Struct
145
+ layout(
146
+ :response_type, :int, # Assuming ResponseType is repr(C) enum
147
+ :int_value, :int64,
148
+ :float_value, :double,
149
+ :bool_value, :bool,
150
+ :string_value, :pointer, # points to C string
151
+ :string_value_len, :long,
152
+ :array_value, :pointer, # points to CommandResponse array
153
+ :array_value_len, :long,
154
+ :map_key, :pointer, # CommandResponse*
155
+ :map_value, :pointer, # CommandResponse*
156
+ :sets_value, :pointer, # CommandResponse*
157
+ :sets_value_len, :long,
158
+ :arena_ptr, :pointer # *mut c_void - arena allocator pointer
159
+ )
160
+ end
161
+
162
+ callback :success_callback, %i[ulong pointer], :void
163
+ callback :failure_callback, %i[ulong string int], :void
164
+
165
+ class AsyncClientData < FFI::Struct
166
+ layout(
167
+ :success_callback, :success_callback,
168
+ :failure_callback, :failure_callback
169
+ )
170
+ end
171
+
172
+ class ClientData < FFI::Union
173
+ layout(
174
+ :async_client, AsyncClientData
175
+ )
176
+ end
177
+
178
+ class CommandResult < FFI::Struct
179
+ layout(
180
+ :response, CommandResponse.by_ref,
181
+ :command_error, CommandError.by_ref,
182
+ :arena, :pointer # *mut ResponseArena
183
+ )
184
+ end
185
+
186
+ callback :pubsub_callback, [
187
+ :ulong, # client_ptr
188
+ :int, # kind (PushKind enum)
189
+ :pointer, :long, # message + length
190
+ :pointer, :long, # channel + length
191
+ :pointer, :long # pattern + length
192
+ ], :void
193
+
194
+ attach_function :create_client, [
195
+ :pointer, # *const u8 (connection_request_bytes)
196
+ :ulong, # usize (connection_request_len)
197
+ ClientType.by_ref, # *const ClientType
198
+ :pubsub_callback # callback
199
+ ], :pointer # *const ConnectionResponse
200
+
201
+ attach_function :create_client_from_uri, [
202
+ :string, # *const c_char (uri_str)
203
+ :string, # *const c_char (extra_options_json)
204
+ ClientType.by_ref, # *const ClientType
205
+ :pubsub_callback # callback
206
+ ], :pointer # *const ConnectionResponse
207
+
208
+ attach_function :free_connection_response, [
209
+ :pointer # *mut ConnectionResponse
210
+ ], :void
211
+
212
+ attach_function :close_client, [
213
+ :pointer # client_adapter_ptr
214
+ ], :void
215
+
216
+ attach_function :command, [
217
+ :pointer, # client_adapter_ptr
218
+ :ulong, # request_id
219
+ :int, # command_type
220
+ :ulong, # arg_count
221
+ :pointer, # args (pointer to usize[])
222
+ :pointer, # args_len (pointer to c_ulong[])
223
+ :pointer, # route_bytes
224
+ :ulong, # route_bytes_len
225
+ :ulong # span_ptr (u64)
226
+ ], :pointer, blocking: true # returns *mut CommandResult, releases GVL during I/O
227
+
228
+ attach_function :batch, [
229
+ :pointer, # client_ptr
230
+ :ulong, # callback_index
231
+ BatchInfo.by_ref, # *const BatchInfo
232
+ :bool, # raise_on_error
233
+ :pointer, # *const BatchOptionsInfo
234
+ :ulong # span_ptr (u64)
235
+ ], :pointer, blocking: true # returns *mut CommandResult, releases GVL during I/O
236
+
237
+ attach_function :store_script, [
238
+ :pointer, # *const u8 (script_bytes)
239
+ :ulong # usize (script_len)
240
+ ], :pointer # returns *mut ScriptHashBuffer
241
+
242
+ attach_function :invoke_script, [
243
+ :pointer, # client_ptr
244
+ :ulong, # request_id
245
+ :pointer, # hash (pointer to C string)
246
+ :ulong, # keys_count (number of keys)
247
+ :pointer, # keys (pointer to usize[])
248
+ :pointer, # keys_len (pointer to c_ulong[])
249
+ :ulong, # args_count (number of args)
250
+ :pointer, # args (pointer to usize[])
251
+ :pointer, # args_len (pointer to c_ulong[])
252
+ :pointer, # route_bytes (pointer to u8)
253
+ :ulong, # route_bytes_len (usize)
254
+ :uint64 # span_ptr (OpenTelemetry span pointer)
255
+ ], :pointer, blocking: true # returns *mut CommandResult, releases GVL during I/O
256
+
257
+ # OpenTelemetry structures
258
+ class OpenTelemetryTracesConfig < FFI::Struct
259
+ layout(
260
+ :endpoint, :pointer, # const char* (trace collector endpoint)
261
+ :has_sample_percentage, :bool, # whether sample_percentage is set
262
+ :sample_percentage, :uint32 # sampling percentage (0-100)
263
+ )
264
+ end
265
+
266
+ class OpenTelemetryMetricsConfig < FFI::Struct
267
+ layout(
268
+ :endpoint, :pointer # const char* (metrics collector endpoint)
269
+ )
270
+ end
271
+
272
+ class OpenTelemetryConfig < FFI::Struct
273
+ layout(
274
+ :traces, :pointer, # OpenTelemetryTracesConfig*
275
+ :metrics, :pointer, # OpenTelemetryMetricsConfig*
276
+ :has_flush_interval_ms, :bool, # whether flush_interval_ms is set
277
+ :flush_interval_ms, :int64 # flush interval in milliseconds
278
+ )
279
+ end
280
+
281
+ # Statistics structure
282
+ class Statistics < FFI::Struct
283
+ layout(
284
+ :total_connections, :ulong,
285
+ :total_clients, :ulong,
286
+ :total_values_compressed, :ulong,
287
+ :total_values_decompressed, :ulong,
288
+ :total_original_bytes, :ulong,
289
+ :total_bytes_compressed, :ulong,
290
+ :total_bytes_decompressed, :ulong,
291
+ :compression_skipped_count, :ulong,
292
+ :subscription_out_of_sync_count, :ulong,
293
+ :subscription_last_sync_timestamp, :ulong
294
+ )
295
+ end
296
+
297
+ # OpenTelemetry functions
298
+ attach_function :init_open_telemetry, [
299
+ OpenTelemetryConfig.by_ref # OpenTelemetry configuration
300
+ ], :pointer # returns error string or NULL on success
301
+
302
+ attach_function :free_c_string, [
303
+ :pointer # C string to free
304
+ ], :void
305
+
306
+ attach_function :create_otel_span, [
307
+ :int # request_type (RequestType enum value)
308
+ ], :uint64 # returns span pointer (u64) or 0 on failure
309
+
310
+ attach_function :create_batch_otel_span, [], :uint64 # returns span pointer (u64) or 0 on failure
311
+
312
+ attach_function :drop_otel_span, [
313
+ :uint64 # span_ptr to close
314
+ ], :void
315
+
316
+ # Statistics function
317
+ attach_function :get_statistics, [], Statistics.by_value # returns statistics by value
318
+ end
319
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Valkey
4
+ module Commands
5
+ # this module contains commands related to BITMAP data type.
6
+ #
7
+ # @see https://valkey.io/commands/#bitmap
8
+ #
9
+ module BitmapCommands
10
+ # Sets or clears the bit at offset in the string value stored at key.
11
+ #
12
+ # @param [String] key
13
+ # @param [Integer] offset bit offset
14
+ # @param [Integer] value bit value `0` or `1`
15
+ # @return [Integer] the original bit value stored at `offset`
16
+ def setbit(key, offset, value)
17
+ send_command(RequestType::SET_BIT, [key, offset, value])
18
+ end
19
+
20
+ # Returns the bit value at offset in the string value stored at key.
21
+ #
22
+ # @param [String] key
23
+ # @param [Integer] offset bit offset
24
+ # @return [Integer] `0` or `1`
25
+ def getbit(key, offset)
26
+ send_command(RequestType::GET_BIT, [key, offset])
27
+ end
28
+
29
+ # Count the number of set bits in a range of the string value stored at key.
30
+ #
31
+ # @param [String] key
32
+ # @param [Integer] start start index
33
+ # @param [Integer] stop stop index
34
+ # @param [String, Symbol] scale the scale of the offset range
35
+ # e.g. 'BYTE' - interpreted as a range of bytes, 'BIT' - interpreted as a range of bits
36
+ # @return [Integer] the number of bits set to 1
37
+ def bitcount(key, start = 0, stop = -1, scale: nil)
38
+ args = [key, start, stop]
39
+ args << scale if scale
40
+ send_command(RequestType::BIT_COUNT, args)
41
+ end
42
+
43
+ # Perform a bitwise operation between strings and store the resulting string in a key.
44
+ #
45
+ # @param [String] operation e.g. `and`, `or`, `xor`, `not`
46
+ # @param [String] destkey destination key
47
+ # @param [String, Array<String>] keys one or more source keys to perform `operation`
48
+ # @return [Integer] the length of the string stored in `destkey`
49
+ def bitop(operation, destkey, *keys)
50
+ keys.flatten!(1)
51
+ args = [operation, destkey]
52
+ args.concat(keys)
53
+
54
+ send_command(RequestType::BIT_OP, args)
55
+ end
56
+
57
+ def bitfield(key, *args)
58
+ send_command(RequestType::BIT_FIELD, [key] + args.map(&:to_s))
59
+ end
60
+
61
+ def bitfield_ro(key, *args)
62
+ send_command(RequestType::BIT_FIELD_READ_ONLY, [key] + args.map(&:to_s))
63
+ end
64
+
65
+ # Return the position of the first bit set to 1 or 0 in a string.
66
+ #
67
+ # @param [String] key
68
+ # @param [Integer] bit whether to look for the first 1 or 0 bit
69
+ # @param [Integer] start start index
70
+ # @param [Integer] stop stop index
71
+ # @param [String, Symbol] scale the scale of the offset range
72
+ # e.g. 'BYTE' - interpreted as a range of bytes, 'BIT' - interpreted as a range of bits
73
+ # @return [Integer] the position of the first 1/0 bit.
74
+ # -1 if looking for 1 and it is not found or start and stop are given.
75
+ def bitpos(key, bit, start = nil, stop = nil, scale: nil)
76
+ raise(ArgumentError, 'stop parameter specified without start parameter') if stop && !start
77
+
78
+ args = [key, bit]
79
+ args << start if start
80
+ args << stop if stop
81
+ args << scale if scale
82
+ send_command(RequestType::BIT_POS, args)
83
+ end
84
+ end
85
+ end
86
+ end
@@ -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