valkey-rb 0.3.5

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 +43 -0
  3. data/.rubocop_todo.yml +22 -0
  4. data/README.md +34 -0
  5. data/Rakefile +23 -0
  6. data/lib/valkey/bindings.rb +173 -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 +454 -0
  12. data/lib/valkey/commands/geo_commands.rb +87 -0
  13. data/lib/valkey/commands/hash_commands.rb +586 -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 +217 -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 +756 -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 +69 -0
  28. data/lib/valkey/errors.rb +41 -0
  29. data/lib/valkey/libglide_ffi.dylib +0 -0
  30. data/lib/valkey/libglide_ffi.so +0 -0
  31. data/lib/valkey/pipeline.rb +20 -0
  32. data/lib/valkey/protobuf/command_request_pb.rb +30 -0
  33. data/lib/valkey/protobuf/connection_request_pb.rb +28 -0
  34. data/lib/valkey/protobuf/response_pb.rb +18 -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 +477 -0
  42. metadata +119 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 569490790efc0901b11b6cd5d4c75d9a162b495601e602ed0b6a9f5307c20884
4
+ data.tar.gz: a731a0406f728eb47f2e65e2a7d8fca6c7719eabbe5a2c7acff184ba2b5ae3f9
5
+ SHA512:
6
+ metadata.gz: 61c6127c71d7d3bbe22de3cc0a0eb7eddc34b6b6a7c439bb26ea1b0f1e95a5ce5b0e4fd497a2c8b0b6527cc5a5420b5c3ebce3bcaeb460c0efc17fd95ec92124
7
+ data.tar.gz: 5f304a28f904a99fd0a8fc46b59930ba20c5dbaebe25116c9801e37bd134cc77065715baabd7b945b256b58063789f05c7369394b71e5a459a910a88399a8eed
data/.rubocop.yml ADDED
@@ -0,0 +1,43 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ AllCops:
4
+ TargetRubyVersion: 2.6
5
+ Exclude:
6
+ - 'vendor/**/*'
7
+ - 'lib/valkey/protobuf/**/*.rb'
8
+
9
+ Style/StringLiterals:
10
+ Enabled: false
11
+
12
+ Metrics/ParameterLists:
13
+ Enabled: false
14
+
15
+ Metrics/PerceivedComplexity:
16
+ Enabled: false
17
+
18
+ Style/StringLiterals:
19
+ Enabled: false
20
+
21
+ Style/Documentation:
22
+ Enabled: false
23
+ Naming/MethodParameterName:
24
+ Enabled: false
25
+
26
+ Metrics/AbcSize:
27
+ Enabled: false
28
+
29
+ Metrics/CyclomaticComplexity:
30
+ Enabled: false
31
+
32
+ Metrics/MethodLength:
33
+ Max: 20
34
+ Exclude:
35
+ - 'lib/valkey.rb'
36
+ - 'test/**/*.rb'
37
+
38
+ Metrics/ModuleLength:
39
+ Exclude:
40
+ - 'lib/valkey/request_type.rb'
41
+ - 'lib/valkey/utils.rb'
42
+ - 'lib/valkey/commands/*.rb'
43
+ - 'test/**/*.rb'
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,22 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2025-07-15 13:27:05 UTC using RuboCop version 1.78.0.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 1
10
+ # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
11
+ # AllowedMethods: refine
12
+ Metrics/BlockLength:
13
+ Max: 35
14
+ Exclude:
15
+ - 'lib/valkey.rb'
16
+
17
+ # Offense count: 1
18
+ # Configuration parameters: CountComments, CountAsOne.
19
+ Metrics/ClassLength:
20
+ Max: 111
21
+ Exclude:
22
+ - 'lib/valkey.rb'
data/README.md ADDED
@@ -0,0 +1,34 @@
1
+ # Valkey
2
+
3
+ A Ruby client library for [Valkey][valkey-home] built with [Valkey Glide Core][valkey-glide-home] that tries to provide a drop in replacement for redis-rb.
4
+
5
+ ## Getting started
6
+
7
+ Install with:
8
+
9
+ ```
10
+ $ gem install valkey
11
+ ```
12
+
13
+ You can connect to Valkey by instantiating the `Valkey` class:
14
+
15
+ ```ruby
16
+ require "valkey"
17
+
18
+ valkey = Valkey.new
19
+
20
+ valkey.set("mykey", "hello world")
21
+ # => "OK"
22
+
23
+ valkey.get("mykey")
24
+ # => "hello world"
25
+ ```
26
+
27
+
28
+ Checkout [the implementation status of the Valkey commands][commands-implementation-progress].
29
+
30
+
31
+ [valkey-home]: https://valkey.io
32
+ [valkey-glide-home]: https://github.com/valkey-io/valkey-glide
33
+ [commands-implementation-progress]: https://github.com/valkey-io/valkey-glide-ruby/wiki/The-implementation-status-of-the-Valkey-commands
34
+
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rake/testtask'
5
+
6
+ namespace :test do
7
+ groups = %i[valkey cluster]
8
+ groups.each do |group|
9
+ Rake::TestTask.new(group) do |t|
10
+ t.libs << "test"
11
+ t.libs << "lib"
12
+ t.test_files = FileList["test/#{group}/**/*_test.rb"]
13
+ t.options = '-v' if ENV['CI'] || ENV['VERBOSE']
14
+ end
15
+ end
16
+
17
+ lost_tests = Dir["test/**/*_test.rb"] - groups.map { |g| Dir["test/#{g}/**/*_test.rb"] }.flatten
18
+ abort "The following test files are in no group:\n#{lost_tests.join("\n")}" unless lost_tests.empty?
19
+ end
20
+
21
+ task test: ["test:valkey"]
22
+
23
+ task default: :test
@@ -0,0 +1,173 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Valkey
4
+ module Bindings
5
+ extend FFI::Library
6
+
7
+ # Detect platform and load appropriate library
8
+ lib_name = case RbConfig::CONFIG['host_os']
9
+ when /darwin|mac os/
10
+ 'libglide_ffi.dylib'
11
+ when /linux/
12
+ 'libglide_ffi.so'
13
+ else
14
+ raise "Unsupported platform: #{RbConfig::CONFIG['host_os']}"
15
+ end
16
+
17
+ ffi_lib File.expand_path("./#{lib_name}", __dir__)
18
+
19
+ class ClientType < FFI::Struct
20
+ layout(
21
+ :tag, :uint # 0 = AsyncClient, 1 = SyncClient
22
+ )
23
+ end
24
+
25
+ class ConnectionResponse < FFI::Struct
26
+ layout(
27
+ :conn_ptr, :pointer, # *const c_void
28
+ :connection_error_message, :string # *const c_char (null-terminated C string)
29
+ )
30
+ end
31
+
32
+ class CommandError < FFI::Struct
33
+ layout(
34
+ :command_error_message, :string,
35
+ :command_error_type, :int # Assuming RequestErrorType is repr(C) enum
36
+ )
37
+ end
38
+
39
+ class BatchOptionsInfo < FFI::Struct
40
+ layout(
41
+ :retry_server_error, :bool,
42
+ :retry_connection_error, :bool,
43
+ :has_timeout, :bool,
44
+ :timeout, :uint, # Assuming u32 is represented as uint in C
45
+ :route_info, :pointer # *const RouteInfo
46
+ )
47
+ end
48
+
49
+ class CmdInfo < FFI::Struct
50
+ layout(
51
+ :request_type, :int, # Assuming RequestType is repr(C) enum
52
+ :args, :pointer, # *const *const u8 (pointer to array of pointers to args)
53
+ :arg_count, :ulong, # usize (number of arguments)
54
+ :args_len, :pointer # *const usize (pointer to array of argument lengths)
55
+ )
56
+ end
57
+
58
+ class ScriptHashBuffer < FFI::Struct
59
+ layout(
60
+ :ptr, :pointer, # *mut u8 (pointer to the script hash)
61
+ :len, :ulong, # usize (length of the script hash)
62
+ :capacity, :ulong # usize (capacity of the buffer)
63
+ )
64
+ end
65
+
66
+ class BatchInfo < FFI::Struct
67
+ layout(
68
+ :cmd_count, :ulong, # usize
69
+ :cmds, :pointer, # *const *const CmdInfo
70
+ :is_atomic, :bool # bool
71
+ )
72
+ end
73
+
74
+ class CommandResponse < FFI::Struct
75
+ layout(
76
+ :response_type, :int, # Assuming ResponseType is repr(C) enum
77
+ :int_value, :int64,
78
+ :float_value, :double,
79
+ :bool_value, :bool,
80
+ :string_value, :pointer, # points to C string
81
+ :string_value_len, :long,
82
+ :array_value, :pointer, # points to CommandResponse array
83
+ :array_value_len, :long,
84
+ :map_key, :pointer, # CommandResponse*
85
+ :map_value, :pointer, # CommandResponse*
86
+ :sets_value, :pointer, # CommandResponse*
87
+ :sets_value_len, :long
88
+ )
89
+ end
90
+
91
+ callback :success_callback, %i[ulong pointer], :void
92
+ callback :failure_callback, %i[ulong string int], :void
93
+
94
+ class AsyncClientData < FFI::Struct
95
+ layout(
96
+ :success_callback, :success_callback,
97
+ :failure_callback, :failure_callback
98
+ )
99
+ end
100
+
101
+ class ClientData < FFI::Union
102
+ layout(
103
+ :async_client, AsyncClientData
104
+ )
105
+ end
106
+
107
+ class CommandResult < FFI::Struct
108
+ layout(
109
+ :response, CommandResponse.by_ref,
110
+ :command_error, CommandError.by_ref
111
+ )
112
+ end
113
+
114
+ callback :pubsub_callback, [
115
+ :ulong, # client_ptr
116
+ :int, # kind (PushKind enum)
117
+ :pointer, :long, # message + length
118
+ :pointer, :long, # channel + length
119
+ :pointer, :long # pattern + length
120
+ ], :void
121
+
122
+ attach_function :create_client, [
123
+ :pointer, # *const u8 (connection_request_bytes)
124
+ :ulong, # usize (connection_request_len)
125
+ ClientType.by_ref, # *const ClientType
126
+ :pubsub_callback # callback
127
+ ], :pointer # *const ConnectionResponse
128
+
129
+ attach_function :close_client, [
130
+ :pointer # client_adapter_ptr
131
+ ], :void
132
+
133
+ attach_function :command, [
134
+ :pointer, # client_adapter_ptr
135
+ :ulong, # request_id
136
+ :int, # command_type
137
+ :ulong, # arg_count
138
+ :pointer, # args (pointer to usize[])
139
+ :pointer, # args_len (pointer to c_ulong[])
140
+ :pointer, # route_bytes
141
+ :ulong, # route_bytes_len
142
+ :ulong # span_ptr (u64)
143
+ ], :pointer # returns *mut CommandResult
144
+
145
+ attach_function :batch, [
146
+ :pointer, # client_ptr
147
+ :ulong, # callback_index
148
+ BatchInfo.by_ref, # *const BatchInfo
149
+ :bool, # raise_on_error
150
+ :pointer, # *const BatchOptionsInfo
151
+ :ulong # span_ptr (u64)
152
+ ], :pointer # returns *mut CommandResult
153
+
154
+ attach_function :store_script, [
155
+ :pointer, # *const u8 (script_bytes)
156
+ :ulong # usize (script_len)
157
+ ], :pointer # returns *mut ScriptHashBuffer
158
+
159
+ attach_function :invoke_script, [
160
+ :pointer, # client_ptr
161
+ :ulong, # request_id
162
+ :pointer, # hash (pointer to C string)
163
+ :ulong, # keys_count (number of keys)
164
+ :pointer, # keys (pointer to usize[])
165
+ :pointer, # keys_len (pointer to c_ulong[])
166
+ :ulong, # args_count (number of args)
167
+ :pointer, # args (pointer to usize[])
168
+ :pointer, # args_len (pointer to c_ulong[])
169
+ :pointer, # route_bytes (pointer to u8)
170
+ :ulong # route_bytes_len (usize)
171
+ ], :pointer # returns *mut CommandResult
172
+ end
173
+ 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