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,237 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Valkey
|
|
4
|
+
module Commands
|
|
5
|
+
# This module contains commands related to Valkey Pub/Sub.
|
|
6
|
+
#
|
|
7
|
+
# @see https://valkey.io/commands/#pubsub
|
|
8
|
+
#
|
|
9
|
+
module PubSubCommands
|
|
10
|
+
# Subscribe to one or more channels.
|
|
11
|
+
#
|
|
12
|
+
# @example Subscribe to channels
|
|
13
|
+
# valkey.subscribe("channel1", "channel2")
|
|
14
|
+
# # => "OK"
|
|
15
|
+
#
|
|
16
|
+
# @param [Array<String>] channels the channels to subscribe to
|
|
17
|
+
# @return [String] "OK"
|
|
18
|
+
#
|
|
19
|
+
# @see https://valkey.io/commands/subscribe/
|
|
20
|
+
def subscribe(*channels)
|
|
21
|
+
send_command(RequestType::SUBSCRIBE, channels)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Unsubscribe from one or more channels.
|
|
25
|
+
#
|
|
26
|
+
# @example Unsubscribe from channels
|
|
27
|
+
# valkey.unsubscribe("channel1", "channel2")
|
|
28
|
+
# # => "OK"
|
|
29
|
+
# @example Unsubscribe from all channels
|
|
30
|
+
# valkey.unsubscribe
|
|
31
|
+
# # => "OK"
|
|
32
|
+
#
|
|
33
|
+
# @param [Array<String>] channels the channels to unsubscribe from (empty for all)
|
|
34
|
+
# @return [String] "OK"
|
|
35
|
+
#
|
|
36
|
+
# @see https://valkey.io/commands/unsubscribe/
|
|
37
|
+
def unsubscribe(*channels)
|
|
38
|
+
send_command(RequestType::UNSUBSCRIBE, channels)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Subscribe to one or more patterns.
|
|
42
|
+
#
|
|
43
|
+
# @example Subscribe to patterns
|
|
44
|
+
# valkey.psubscribe("news.*", "events.*")
|
|
45
|
+
# # => "OK"
|
|
46
|
+
#
|
|
47
|
+
# @param [Array<String>] patterns the patterns to subscribe to
|
|
48
|
+
# @return [String] "OK"
|
|
49
|
+
#
|
|
50
|
+
# @see https://valkey.io/commands/psubscribe/
|
|
51
|
+
def psubscribe(*patterns)
|
|
52
|
+
send_command(RequestType::PSUBSCRIBE, patterns)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Unsubscribe from one or more patterns.
|
|
56
|
+
#
|
|
57
|
+
# @example Unsubscribe from patterns
|
|
58
|
+
# valkey.punsubscribe("news.*", "events.*")
|
|
59
|
+
# # => "OK"
|
|
60
|
+
# @example Unsubscribe from all patterns
|
|
61
|
+
# valkey.punsubscribe
|
|
62
|
+
# # => "OK"
|
|
63
|
+
#
|
|
64
|
+
# @param [Array<String>] patterns the patterns to unsubscribe from (empty for all)
|
|
65
|
+
# @return [String] "OK"
|
|
66
|
+
#
|
|
67
|
+
# @see https://valkey.io/commands/punsubscribe/
|
|
68
|
+
def punsubscribe(*patterns)
|
|
69
|
+
send_command(RequestType::PUNSUBSCRIBE, patterns)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Publish a message to a channel.
|
|
73
|
+
#
|
|
74
|
+
# @example Publish a message
|
|
75
|
+
# valkey.publish("channel1", "Hello, World!")
|
|
76
|
+
# # => 2
|
|
77
|
+
#
|
|
78
|
+
# @param [String] channel the channel to publish to
|
|
79
|
+
# @param [String] message the message to publish
|
|
80
|
+
# @return [Integer] the number of clients that received the message
|
|
81
|
+
#
|
|
82
|
+
# @see https://valkey.io/commands/publish/
|
|
83
|
+
def publish(channel, message)
|
|
84
|
+
send_command(RequestType::PUBLISH, [channel, message])
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Subscribe to one or more shard channels.
|
|
88
|
+
#
|
|
89
|
+
# @example Subscribe to shard channels
|
|
90
|
+
# valkey.ssubscribe("shard1", "shard2")
|
|
91
|
+
# # => "OK"
|
|
92
|
+
#
|
|
93
|
+
# @param [Array<String>] channels the shard channels to subscribe to
|
|
94
|
+
# @return [String] "OK"
|
|
95
|
+
#
|
|
96
|
+
# @see https://valkey.io/commands/ssubscribe/
|
|
97
|
+
def ssubscribe(*channels)
|
|
98
|
+
send_command(RequestType::SSUBSCRIBE, channels)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Unsubscribe from one or more shard channels.
|
|
102
|
+
#
|
|
103
|
+
# @example Unsubscribe from shard channels
|
|
104
|
+
# valkey.sunsubscribe("shard1", "shard2")
|
|
105
|
+
# # => "OK"
|
|
106
|
+
# @example Unsubscribe from all shard channels
|
|
107
|
+
# valkey.sunsubscribe
|
|
108
|
+
# # => "OK"
|
|
109
|
+
#
|
|
110
|
+
# @param [Array<String>] channels the shard channels to unsubscribe from (empty for all)
|
|
111
|
+
# @return [String] "OK"
|
|
112
|
+
#
|
|
113
|
+
# @see https://valkey.io/commands/sunsubscribe/
|
|
114
|
+
def sunsubscribe(*channels)
|
|
115
|
+
send_command(RequestType::SUNSUBSCRIBE, channels)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Publish a message to a shard channel.
|
|
119
|
+
#
|
|
120
|
+
# @example Publish a message to a shard channel
|
|
121
|
+
# valkey.spublish("shard1", "Hello, Shard!")
|
|
122
|
+
# # => 1
|
|
123
|
+
#
|
|
124
|
+
# @param [String] channel the shard channel to publish to
|
|
125
|
+
# @param [String] message the message to publish
|
|
126
|
+
# @return [Integer] the number of clients that received the message
|
|
127
|
+
#
|
|
128
|
+
# @see https://valkey.io/commands/spublish/
|
|
129
|
+
def spublish(channel, message)
|
|
130
|
+
send_command(RequestType::SPUBLISH, [channel, message])
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# List active channels.
|
|
134
|
+
#
|
|
135
|
+
# @example List all active channels
|
|
136
|
+
# valkey.pubsub_channels
|
|
137
|
+
# # => ["channel1", "channel2"]
|
|
138
|
+
# @example List active channels matching a pattern
|
|
139
|
+
# valkey.pubsub_channels("news.*")
|
|
140
|
+
# # => ["news.sports", "news.tech"]
|
|
141
|
+
#
|
|
142
|
+
# @param [String] pattern optional pattern to filter channels
|
|
143
|
+
# @return [Array<String>] list of active channels
|
|
144
|
+
#
|
|
145
|
+
# @see https://valkey.io/commands/pubsub-channels/
|
|
146
|
+
def pubsub_channels(pattern = nil)
|
|
147
|
+
args = pattern ? [pattern] : []
|
|
148
|
+
send_command(RequestType::PUBSUB_CHANNELS, args)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Get the number of unique patterns subscribed to.
|
|
152
|
+
#
|
|
153
|
+
# @example Get pattern count
|
|
154
|
+
# valkey.pubsub_numpat
|
|
155
|
+
# # => 3
|
|
156
|
+
#
|
|
157
|
+
# @return [Integer] the number of patterns
|
|
158
|
+
#
|
|
159
|
+
# @see https://valkey.io/commands/pubsub-numpat/
|
|
160
|
+
def pubsub_numpat
|
|
161
|
+
send_command(RequestType::PUBSUB_NUM_PAT)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Get the number of subscribers for channels.
|
|
165
|
+
#
|
|
166
|
+
# @example Get subscriber counts
|
|
167
|
+
# valkey.pubsub_numsub("channel1", "channel2")
|
|
168
|
+
# # => ["channel1", 5, "channel2", 3]
|
|
169
|
+
#
|
|
170
|
+
# @param [Array<String>] channels the channels to check
|
|
171
|
+
# @return [Array] channel names and subscriber counts
|
|
172
|
+
#
|
|
173
|
+
# @see https://valkey.io/commands/pubsub-numsub/
|
|
174
|
+
def pubsub_numsub(*channels)
|
|
175
|
+
send_command(RequestType::PUBSUB_NUM_SUB, channels)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# List active shard channels.
|
|
179
|
+
#
|
|
180
|
+
# @example List all active shard channels
|
|
181
|
+
# valkey.pubsub_shardchannels
|
|
182
|
+
# # => ["shard1", "shard2"]
|
|
183
|
+
# @example List active shard channels matching a pattern
|
|
184
|
+
# valkey.pubsub_shardchannels("shard.*")
|
|
185
|
+
# # => ["shard.1", "shard.2"]
|
|
186
|
+
#
|
|
187
|
+
# @param [String] pattern optional pattern to filter shard channels
|
|
188
|
+
# @return [Array<String>] list of active shard channels
|
|
189
|
+
#
|
|
190
|
+
# @see https://valkey.io/commands/pubsub-shardchannels/
|
|
191
|
+
def pubsub_shardchannels(pattern = nil)
|
|
192
|
+
args = pattern ? [pattern] : []
|
|
193
|
+
send_command(RequestType::PUBSUB_SHARD_CHANNELS, args)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Get the number of subscribers for shard channels.
|
|
197
|
+
#
|
|
198
|
+
# @example Get shard subscriber counts
|
|
199
|
+
# valkey.pubsub_shardnumsub("shard1", "shard2")
|
|
200
|
+
# # => ["shard1", 2, "shard2", 1]
|
|
201
|
+
#
|
|
202
|
+
# @param [Array<String>] channels the shard channels to check
|
|
203
|
+
# @return [Array] shard channel names and subscriber counts
|
|
204
|
+
#
|
|
205
|
+
# @see https://valkey.io/commands/pubsub-shardnumsub/
|
|
206
|
+
def pubsub_shardnumsub(*channels)
|
|
207
|
+
send_command(RequestType::PUBSUB_SHARD_NUM_SUB, channels)
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Control pub/sub operations (convenience method).
|
|
211
|
+
#
|
|
212
|
+
# @example List active channels
|
|
213
|
+
# valkey.pubsub(:channels)
|
|
214
|
+
# # => ["channel1", "channel2"]
|
|
215
|
+
# @example Get pattern count
|
|
216
|
+
# valkey.pubsub(:numpat)
|
|
217
|
+
# # => 3
|
|
218
|
+
# @example Get subscriber counts
|
|
219
|
+
# valkey.pubsub(:numsub, "channel1", "channel2")
|
|
220
|
+
# # => ["channel1", 5, "channel2", 3]
|
|
221
|
+
# @example List active shard channels
|
|
222
|
+
# valkey.pubsub(:shardchannels)
|
|
223
|
+
# # => ["shard1", "shard2"]
|
|
224
|
+
# @example Get shard subscriber counts
|
|
225
|
+
# valkey.pubsub(:shardnumsub, "shard1", "shard2")
|
|
226
|
+
# # => ["shard1", 2, "shard2", 1]
|
|
227
|
+
#
|
|
228
|
+
# @param [String, Symbol] subcommand the subcommand (channels, numpat, numsub, shardchannels, shardnumsub)
|
|
229
|
+
# @param [Array] args arguments for the subcommand
|
|
230
|
+
# @return [Object] depends on subcommand
|
|
231
|
+
def pubsub(subcommand, *args)
|
|
232
|
+
subcommand = subcommand.to_s.downcase
|
|
233
|
+
send("pubsub_#{subcommand}", *args)
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
end
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Valkey
|
|
4
|
+
module Commands
|
|
5
|
+
# this module contains commands related to list data type.
|
|
6
|
+
#
|
|
7
|
+
# @see https://valkey.io/commands/#scripting
|
|
8
|
+
#
|
|
9
|
+
module ScriptingCommands
|
|
10
|
+
# Control remote script registry.
|
|
11
|
+
#
|
|
12
|
+
# @example Load a script
|
|
13
|
+
# sha = valkey.script(:load, "return 1")
|
|
14
|
+
# # => <sha of this script>
|
|
15
|
+
# @example Check if a script exists
|
|
16
|
+
# valkey.script(:exists, sha)
|
|
17
|
+
# # => true
|
|
18
|
+
# @example Check if multiple scripts exist
|
|
19
|
+
# valkey.script(:exists, [sha, other_sha])
|
|
20
|
+
# # => [true, false]
|
|
21
|
+
# @example Flush the script registry
|
|
22
|
+
# valkey.script(:flush)
|
|
23
|
+
# # => "OK"
|
|
24
|
+
# @example Kill a running script
|
|
25
|
+
# valkey.script(:kill)
|
|
26
|
+
# # => "OK"
|
|
27
|
+
#
|
|
28
|
+
# @param [String] subcommand e.g. `exists`, `flush`, `load`, `kill`
|
|
29
|
+
# @param [Array<String>] args depends on subcommand
|
|
30
|
+
# @return [String, Boolean, Array<Boolean>, ...] depends on subcommand
|
|
31
|
+
#
|
|
32
|
+
# @see #eval
|
|
33
|
+
# @see #evalsha
|
|
34
|
+
def script(subcommand, args = nil, options: {})
|
|
35
|
+
subcommand = subcommand.to_s.downcase
|
|
36
|
+
|
|
37
|
+
if args.nil?
|
|
38
|
+
send("script_#{subcommand}", **options)
|
|
39
|
+
else
|
|
40
|
+
send("script_#{subcommand}", args)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# if subcommand == "exists"
|
|
44
|
+
# arg = args.first
|
|
45
|
+
#
|
|
46
|
+
# send_command([:script, :exists, arg]) do |reply|
|
|
47
|
+
# reply = reply.map { |r| Boolify.call(r) }
|
|
48
|
+
#
|
|
49
|
+
# if arg.is_a?(Array)
|
|
50
|
+
# reply
|
|
51
|
+
# else
|
|
52
|
+
# reply.first
|
|
53
|
+
# end
|
|
54
|
+
# end
|
|
55
|
+
# else
|
|
56
|
+
# send_command([:script, subcommand] + args)
|
|
57
|
+
# end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def script_flush(sync: false, async: false)
|
|
61
|
+
args = []
|
|
62
|
+
|
|
63
|
+
if async
|
|
64
|
+
args << "async"
|
|
65
|
+
elsif sync
|
|
66
|
+
args << "sync"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
send_command(RequestType::SCRIPT_FLUSH, args)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def script_exists(args)
|
|
73
|
+
send_command(RequestType::SCRIPT_EXISTS, Array(args)) do |reply|
|
|
74
|
+
if args.is_a?(Array)
|
|
75
|
+
reply
|
|
76
|
+
else
|
|
77
|
+
reply.first
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def script_kill
|
|
83
|
+
send_command(RequestType::SCRIPT_KILL)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Set the debug mode for subsequent scripts executed with EVAL.
|
|
87
|
+
#
|
|
88
|
+
# @param [String] mode debug mode: "YES", "SYNC", or "NO"
|
|
89
|
+
# @return [String] "OK"
|
|
90
|
+
#
|
|
91
|
+
# @example Enable script debugging
|
|
92
|
+
# valkey.script_debug("YES")
|
|
93
|
+
# # => "OK"
|
|
94
|
+
# @example Disable script debugging
|
|
95
|
+
# valkey.script_debug("NO")
|
|
96
|
+
# # => "OK"
|
|
97
|
+
#
|
|
98
|
+
# @see https://valkey.io/commands/script-debug/
|
|
99
|
+
def script_debug(mode)
|
|
100
|
+
send_command(RequestType::SCRIPT_DEBUG, [mode.to_s.upcase])
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def script_load(script)
|
|
104
|
+
script = script.first if script.is_a?(Array)
|
|
105
|
+
|
|
106
|
+
buf = FFI::MemoryPointer.new(:char, script.bytesize)
|
|
107
|
+
buf.put_bytes(0, script)
|
|
108
|
+
|
|
109
|
+
result = Bindings.store_script(buf, script.bytesize)
|
|
110
|
+
|
|
111
|
+
hash_buffer = Bindings::ScriptHashBuffer.new(result)
|
|
112
|
+
hash_buffer[:ptr].read_string(hash_buffer[:len])
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Execute a Lua script on the server.
|
|
116
|
+
#
|
|
117
|
+
# @param [String] script the Lua script to execute
|
|
118
|
+
# @param [Array<String>] keys array of key names that the script will access
|
|
119
|
+
# @param [Array<Object>] args array of arguments to pass to the script
|
|
120
|
+
# @return [Object] the result of the script execution
|
|
121
|
+
# @raise [ArgumentError] if script is empty
|
|
122
|
+
# @raise [CommandError] if script execution fails
|
|
123
|
+
#
|
|
124
|
+
# @example Execute a simple script
|
|
125
|
+
# valkey.eval("return 1")
|
|
126
|
+
# # => 1
|
|
127
|
+
# @example Execute script with keys and arguments
|
|
128
|
+
# valkey.eval("return KEYS[1] .. ARGV[1]", keys: ["mykey"], args: ["myarg"])
|
|
129
|
+
# # => "mykeynyarg"
|
|
130
|
+
# @example Execute script with multiple keys and arguments
|
|
131
|
+
# valkey.eval("return #KEYS + #ARGV", keys: ["key1", "key2"], args: ["arg1", "arg2", "arg3"])
|
|
132
|
+
# # => 5
|
|
133
|
+
# @example Execute script that returns different data types
|
|
134
|
+
# valkey.eval("return {1, 'hello', true, nil}")
|
|
135
|
+
# # => [1, "hello", true, nil]
|
|
136
|
+
# Since the eval is not available in the rust backend
|
|
137
|
+
# using the load and invoke script
|
|
138
|
+
def eval(script, keys: [], args: [])
|
|
139
|
+
# Validate script parameter
|
|
140
|
+
raise ArgumentError, "script must be a string" unless script.is_a?(String)
|
|
141
|
+
raise ArgumentError, "script cannot be empty" if script.empty?
|
|
142
|
+
|
|
143
|
+
# Validate and convert keys and args to strings
|
|
144
|
+
begin
|
|
145
|
+
keys = Array(keys).map(&:to_s)
|
|
146
|
+
args = Array(args).map(&:to_s)
|
|
147
|
+
rescue StandardError => e
|
|
148
|
+
raise ArgumentError, "failed to convert keys or args to strings: #{e.message}"
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Load script to get SHA1 hash, then execute via invoke_script
|
|
152
|
+
sha = script_load(script)
|
|
153
|
+
invoke_script(sha, keys: keys, args: args)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Execute a cached Lua script by its SHA1 hash.
|
|
157
|
+
#
|
|
158
|
+
# @param [String] sha the SHA1 hash of the script to execute
|
|
159
|
+
# @param [Array<String>] keys array of key names that the script will access
|
|
160
|
+
# @param [Array<Object>] args array of arguments to pass to the script
|
|
161
|
+
# @return [Object] the result of the script execution
|
|
162
|
+
# @raise [ArgumentError] if SHA1 hash format is invalid
|
|
163
|
+
# @raise [CommandError] if script is not found or execution fails
|
|
164
|
+
#
|
|
165
|
+
# @example Execute a cached script
|
|
166
|
+
# sha = valkey.script_load("return 1")
|
|
167
|
+
# valkey.evalsha(sha)
|
|
168
|
+
# # => 1
|
|
169
|
+
# @example Execute cached script with parameters
|
|
170
|
+
# script = "return KEYS[1] .. ':' .. ARGV[1]"
|
|
171
|
+
# sha = valkey.script_load(script)
|
|
172
|
+
# valkey.evalsha(sha, keys: ["user"], args: ["123"])
|
|
173
|
+
# # => "user:123"
|
|
174
|
+
# @example Handle script not found error
|
|
175
|
+
# begin
|
|
176
|
+
# valkey.evalsha("nonexistent_sha", keys: [], args: [])
|
|
177
|
+
# rescue Valkey::CommandError => e
|
|
178
|
+
# puts "Script not found: #{e.message}"
|
|
179
|
+
# end
|
|
180
|
+
# Since evalsha is not available in rust backend
|
|
181
|
+
# using invoke script
|
|
182
|
+
def evalsha(sha, keys: [], args: [])
|
|
183
|
+
# Validate SHA1 hash parameter
|
|
184
|
+
raise ArgumentError, "sha1 hash must be a string" unless sha.is_a?(String)
|
|
185
|
+
raise ArgumentError, "sha1 hash must be a 40-character hexadecimal string" unless valid_sha1?(sha)
|
|
186
|
+
|
|
187
|
+
# Validate and convert keys and args to strings
|
|
188
|
+
begin
|
|
189
|
+
keys = Array(keys).map(&:to_s)
|
|
190
|
+
args = Array(args).map(&:to_s)
|
|
191
|
+
rescue StandardError => e
|
|
192
|
+
raise ArgumentError, "failed to convert keys or args to strings: #{e.message}"
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Execute cached script via invoke_script
|
|
196
|
+
invoke_script(sha, keys: keys, args: args)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Execute a read-only Lua script on the server.
|
|
200
|
+
#
|
|
201
|
+
# This is a read-only variant of EVAL that cannot execute commands
|
|
202
|
+
# that modify data. It can be routed to read replicas.
|
|
203
|
+
#
|
|
204
|
+
# @param [String] script the Lua script to execute
|
|
205
|
+
# @param [Array<String>] keys array of key names that the script will access
|
|
206
|
+
# @param [Array<Object>] args array of arguments to pass to the script
|
|
207
|
+
# @return [Object] the result of the script execution
|
|
208
|
+
#
|
|
209
|
+
# @example Execute a read-only script
|
|
210
|
+
# valkey.eval_ro("return redis.call('get', KEYS[1])", keys: ["mykey"])
|
|
211
|
+
# # => "myvalue"
|
|
212
|
+
#
|
|
213
|
+
# @see https://valkey.io/commands/eval_ro/
|
|
214
|
+
def eval_ro(script, keys: [], args: [])
|
|
215
|
+
raise ArgumentError, "script must be a string" unless script.is_a?(String)
|
|
216
|
+
raise ArgumentError, "script cannot be empty" if script.empty?
|
|
217
|
+
|
|
218
|
+
keys = Array(keys).map(&:to_s)
|
|
219
|
+
args = Array(args).map(&:to_s)
|
|
220
|
+
|
|
221
|
+
sha = script_load(script)
|
|
222
|
+
invoke_script(sha, keys: keys, args: args)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Execute a cached read-only Lua script by its SHA1 hash.
|
|
226
|
+
#
|
|
227
|
+
# This is a read-only variant of EVALSHA that cannot execute commands
|
|
228
|
+
# that modify data. It can be routed to read replicas.
|
|
229
|
+
#
|
|
230
|
+
# @param [String] sha the SHA1 hash of the script to execute
|
|
231
|
+
# @param [Array<String>] keys array of key names that the script will access
|
|
232
|
+
# @param [Array<Object>] args array of arguments to pass to the script
|
|
233
|
+
# @return [Object] the result of the script execution
|
|
234
|
+
#
|
|
235
|
+
# @example Execute a cached read-only script
|
|
236
|
+
# sha = valkey.script_load("return redis.call('get', KEYS[1])")
|
|
237
|
+
# valkey.evalsha_ro(sha, keys: ["mykey"])
|
|
238
|
+
# # => "myvalue"
|
|
239
|
+
#
|
|
240
|
+
# @see https://valkey.io/commands/evalsha_ro/
|
|
241
|
+
def evalsha_ro(sha, keys: [], args: [])
|
|
242
|
+
raise ArgumentError, "sha1 hash must be a string" unless sha.is_a?(String)
|
|
243
|
+
raise ArgumentError, "sha1 hash must be a 40-character hexadecimal string" unless valid_sha1?(sha)
|
|
244
|
+
|
|
245
|
+
keys = Array(keys).map(&:to_s)
|
|
246
|
+
args = Array(args).map(&:to_s)
|
|
247
|
+
|
|
248
|
+
invoke_script(sha, keys: keys, args: args)
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def invoke_script(script, args: [], keys: [])
|
|
252
|
+
arg_ptrs, arg_lens = build_command_args(args)
|
|
253
|
+
keys_ptrs, keys_lens = build_command_args(keys)
|
|
254
|
+
|
|
255
|
+
route = ""
|
|
256
|
+
route_buf = FFI::MemoryPointer.from_string(route)
|
|
257
|
+
|
|
258
|
+
sha = FFI::MemoryPointer.new(:char, script.bytesize + 1)
|
|
259
|
+
sha.put_bytes(0, script)
|
|
260
|
+
|
|
261
|
+
res = Bindings.invoke_script(
|
|
262
|
+
@connection,
|
|
263
|
+
0,
|
|
264
|
+
sha,
|
|
265
|
+
keys.size,
|
|
266
|
+
keys_ptrs,
|
|
267
|
+
keys_lens,
|
|
268
|
+
args.size,
|
|
269
|
+
arg_ptrs,
|
|
270
|
+
arg_lens,
|
|
271
|
+
route_buf,
|
|
272
|
+
route.bytesize
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
convert_response(res)
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
private
|
|
279
|
+
|
|
280
|
+
# Validate SHA1 hash format (40-character hexadecimal string)
|
|
281
|
+
def valid_sha1?(sha)
|
|
282
|
+
sha.is_a?(String) && sha.length == 40 && sha.match?(/\A[a-fA-F0-9]{40}\z/)
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
end
|