valkey-glide-rb 0.9.2 → 1.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 17396d39342cbcc8a293f5e8b252e1689841b436664c630e330d06b0a1cff9c9
4
- data.tar.gz: 7fa260b6afbb060a1c79a8dcb264aae5b211a24e69b042c720e242d39b7b3ab9
3
+ metadata.gz: 31e06a1cd3c0ecb6cd124d2d65c232cd080f5d5b9c3beaec4edac11ca8c9ddd4
4
+ data.tar.gz: a72a457f3533a2ee91c3b4d3a33633d4a5b19f73482c71fd6405c7c5229b3a1e
5
5
  SHA512:
6
- metadata.gz: e96a09b0077bd54a30722201d03ccd8bb8baf7a47c0005d5c3d87441bfeb015479181548dce68bd1e882ee9e266be4658e68b2562e32dca6c48c101ca4e0e2cc
7
- data.tar.gz: dbee972964d0a2e54dbf7f4916c2bab6427525ace1d97a679845409b9d55390419631c8407df7502ca9b3561bd51ea0bba1ebecd8bbef680cdaa45b5ba28f405
6
+ metadata.gz: 3f3384dfc9de4a87b7efd6464e2b724534339e2b56f748f4399283a290303ab54361f057c8069c19bb38ce29f621aa0056ac2f96975f81717c023bdf3a2c75fe
7
+ data.tar.gz: bb8378c230ffde26c7abe0f3755d002c5f2bd67e345b2e380036fd07b5b9731b61c7c61bf36d2302a559d33f769daaa86386986de0f69285839a5638235a7f75
@@ -137,22 +137,13 @@ class Valkey
137
137
  # @example Execute script that returns different data types
138
138
  # valkey.eval("return {1, 'hello', true, nil}")
139
139
  # # => [1, "hello", true, nil]
140
- # @example Positional form, matching redis-rb's eval(script, keys, argv)
141
- # valkey.eval("return KEYS[1] .. ARGV[1]", ["mykey"], ["myarg"])
142
- # # => "mykeynyarg"
143
140
  # Since the eval is not available in the rust backend
144
141
  # using the load and invoke script
145
- def eval(script, *rest, keys: nil, args: nil)
142
+ def eval(script, keys: [], args: [])
146
143
  # Validate script parameter
147
144
  raise ArgumentError, "script must be a string" unless script.is_a?(String)
148
145
  raise ArgumentError, "script cannot be empty" if script.empty?
149
146
 
150
- # Accept redis-rb's flexible positional form - eval(script, keys, argv)
151
- # - in addition to the keyword form, mirroring the same treatment
152
- # applied to evalsha (see that method for the full rationale).
153
- keys ||= rest[0] || []
154
- args ||= rest[1] || []
155
-
156
147
  # Validate and convert keys and args to strings
157
148
  begin
158
149
  keys = Array(keys).map(&:to_s)
@@ -190,22 +181,13 @@ class Valkey
190
181
  # rescue Valkey::CommandError => e
191
182
  # puts "Script not found: #{e.message}"
192
183
  # end
193
- # @example Positional form, matching redis-rb's evalsha(sha, keys, argv)
194
- # valkey.evalsha(sha, ["user"], ["123"])
195
- # # => "user:123"
196
184
  # Since evalsha is not available in rust backend
197
185
  # using invoke script
198
- def evalsha(sha, *rest, keys: nil, args: nil)
186
+ def evalsha(sha, keys: [], args: [])
199
187
  # Validate SHA1 hash parameter
200
188
  raise ArgumentError, "sha1 hash must be a string" unless sha.is_a?(String)
201
189
  raise ArgumentError, "sha1 hash must be a 40-character hexadecimal string" unless valid_sha1?(sha)
202
190
 
203
- # Accept redis-rb's flexible positional form - evalsha(sha, keys, argv)
204
- # - in addition to the keyword form, so code written against redis-rb's
205
- # API (e.g. redis_display_id) doesn't need to special-case this client.
206
- keys ||= rest[0] || []
207
- args ||= rest[1] || []
208
-
209
191
  # Validate and convert keys and args to strings
210
192
  begin
211
193
  keys = Array(keys).map(&:to_s)
@@ -129,14 +129,7 @@ class Valkey
129
129
  # @param [String] value
130
130
  # @return [Boolean] whether the key was set or not
131
131
  def setnx(key, value)
132
- # &Utils::Boolify (not glide-core's generic Boolean-coercion table) is
133
- # deliberately used here: glide-core's per-command coercion is keyed by
134
- # command name alone, so it can't distinguish this dedicated SETNX
135
- # RequestType from a raw `customCommand(["SETNX", ...])` call, which
136
- # other GLIDE bindings' existing contracts expect to keep returning a
137
- # plain 0/1 integer. Doing the conversion here keeps it scoped to this
138
- # one Ruby-level method - see hexists/hsetnx for the same pattern.
139
- send_command(RequestType::SET_NX, [key, value], &Utils::Boolify)
132
+ send_command(RequestType::SET_NX, [key, value])
140
133
  end
141
134
 
142
135
  # Set one or more values.
@@ -16,12 +16,8 @@ class Valkey
16
16
  # end # => ["OK", 6]
17
17
  #
18
18
  # @yield [multi] the commands that are called inside this block are cached
19
- # locally (no server round-trip per command) and sent to the server as a
20
- # single atomic batch once the block returns - GLIDE wraps them in a real
21
- # MULTI/EXEC transaction internally. If the block raises, nothing has been
22
- # sent to the server yet, so the exception simply propagates - there is no
23
- # transaction to discard.
24
- # @yieldparam [Valkey::Pipeline] multi collects the block's commands
19
+ # and written to the server upon returning from it
20
+ # @yieldparam [Valkey] multi `self`
25
21
  #
26
22
  # @return [Array<...>]
27
23
  # - an array with replies
@@ -30,12 +26,17 @@ class Valkey
30
26
  # @see #unwatch
31
27
  def multi
32
28
  if block_given?
33
- pipeline = Pipeline.new
34
- yield pipeline
35
-
36
- return [] if pipeline.commands.empty?
37
-
38
- send_batch_commands(pipeline.commands, exception: true, is_atomic: true)
29
+ begin
30
+ @in_multi_block = true
31
+ start_multi
32
+ yield(self)
33
+ exec
34
+ rescue StandardError
35
+ discard
36
+ raise
37
+ ensure
38
+ @in_multi_block = false
39
+ end
39
40
  else
40
41
  start_multi
41
42
  self
@@ -113,12 +114,11 @@ class Valkey
113
114
  # @see #discard
114
115
  def exec
115
116
  if @in_multi
116
- queued_commands = @queued_commands
117
117
  begin
118
118
  begin
119
119
  result = send_command(RequestType::EXEC)
120
120
  # If EXEC returns an error object (from array), it's already handled
121
- result.is_a?(Array) ? reconvert_queued_replies(result, queued_commands) : result
121
+ result
122
122
  rescue CommandError => e
123
123
  # If EXEC itself raises an error (like when transaction is aborted),
124
124
  # return an array with the error to match expected behavior in tests
@@ -157,59 +157,8 @@ class Valkey
157
157
  @queued_commands = []
158
158
  end
159
159
 
160
- # Commands whose reply is boolean when run standalone (via native return-type
161
- # coercion server-side), but arrives as a raw 0/1 integer inside an EXEC array,
162
- # since that coercion is keyed by the single command actually being run - which,
163
- # for a queued command, is EXEC itself, not the original command.
164
- #
165
- # This list mirrors glide-core's own Boolean-coercion table in
166
- # `value_conversion.rs::expected_type_for_cmd` (HEXISTS, HSETNX, EXPIRE, EXPIREAT,
167
- # PEXPIRE, PEXPIREAT, SISMEMBER, PERSIST, SMOVE, PFADD, RENAMENX, MOVE, COPY,
168
- # MSETNX, XGROUP DESTROY, XGROUP CREATECONSUMER) MINUS the commands whose Ruby
169
- # method already passes its own explicit conversion block (`hexists`/`hsetnx`
170
- # use `&Utils::Boolify`; `xgroup_destroy`/`xgroup_createconsumer` use a custom
171
- # bool->int block). Those are already handled correctly by the `if block`
172
- # branch below, before this list is even consulted - listing them here too
173
- # would be redundant, not wrong. Every RequestType below calls `send_command`
174
- # with NO block at all, so this static list is the only place their boolean-ness
175
- # is recorded.
176
- #
177
- # NOTE: SETNX is deliberately NOT in glide-core's coercion table (and so isn't
178
- # here as a "no block" entry either) - it's `redis-rb`-style boolean-ness only,
179
- # not something the server/`glide-core` treats as boolean, since coercion there
180
- # is keyed by command name alone and can't distinguish this dedicated RequestType
181
- # from a raw `customCommand(["SETNX", ...])` call, which other bindings' existing
182
- # contracts expect to keep returning a plain integer. `setnx`'s own Ruby method
183
- # passes `&Utils::Boolify` directly instead - see string_commands.rb - so it's
184
- # already covered by the `if block` branch, same as hexists/hsetnx.
185
- BOOLEAN_REQUEST_TYPES = [
186
- RequestType::EXPIRE, RequestType::EXPIRE_AT, RequestType::PEXPIRE, RequestType::PEXPIRE_AT,
187
- RequestType::PERSIST, RequestType::SISMEMBER, RequestType::S_MOVE, RequestType::PFADD,
188
- RequestType::RENAME_NX, RequestType::MOVE, RequestType::COPY, RequestType::MSET_NX
189
- ].freeze
190
-
191
160
  private
192
161
 
193
- # Re-applies each queued command's own reply conversion (e.g. `&Utils::Boolify`)
194
- # to EXEC's raw array, since redis-rb's Future-based design does the equivalent
195
- # (a Future remembers its conversion and re-applies it once EXEC resolves), but
196
- # queued commands here go through the plain single-command path with no such
197
- # memory - see BOOLEAN_REQUEST_TYPES above for the case with no explicit block.
198
- def reconvert_queued_replies(result, queued_commands)
199
- return result unless result.size == queued_commands.size
200
-
201
- result.each_with_index.map do |value, i|
202
- command_type, _args, block = queued_commands[i]
203
- if block
204
- block.call(value)
205
- elsif BOOLEAN_REQUEST_TYPES.include?(command_type)
206
- Utils::Boolify.call(value)
207
- else
208
- value
209
- end
210
- end
211
- end
212
-
213
162
  # Start a MULTI block if one isn't already active.
214
163
  #
215
164
  # This mirrors the behaviour of popular Valkey/Redis clients where
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Valkey
4
- VERSION = "0.9.2"
4
+ VERSION = "1.0.0-rc1"
5
5
  end
data/lib/valkey.rb CHANGED
@@ -457,9 +457,7 @@ class Valkey
457
457
  RequestType::MULTI, RequestType::EXEC, RequestType::DISCARD,
458
458
  RequestType::WATCH, RequestType::UNWATCH
459
459
  ]
460
- if !tx_commands.include?(command_type) && result == "QUEUED"
461
- @queued_commands << [command_type, command_args.dup, block]
462
- end
460
+ @queued_commands << [command_type, command_args.dup] if !tx_commands.include?(command_type) && result == "QUEUED"
463
461
  end
464
462
 
465
463
  result
@@ -467,29 +465,23 @@ class Valkey
467
465
 
468
466
  private
469
467
 
470
- def send_batch_commands(commands, exception: true, is_atomic: false)
468
+ def send_batch_commands(commands, exception: true)
471
469
  # WORKAROUND: The underlying Glide FFI backend has stability issues when
472
- # batching LITERAL MULTI / EXEC / DISCARD commands (e.g. a `pipelined` block
473
- # that manually calls `pipeline.multi`/`pipeline.exec`). To avoid native
474
- # crashes we fall back to issuing those commands sequentially instead of via
475
- # `Bindings.batch`. This never applies to a real `is_atomic: true` batch
476
- # (see `multi`'s block form) - that path never contains literal MULTI/EXEC
477
- # commands, since the server-side transaction wrapping is handled by GLIDE
478
- # itself based on the `is_atomic` flag, not by commands in the list.
479
- unless is_atomic
480
- tx_types = [RequestType::MULTI, RequestType::EXEC, RequestType::DISCARD]
481
-
482
- if commands.any? { |(command_type, _args, _block)| tx_types.include?(command_type) }
483
- results = []
484
-
485
- commands.each do |command_type, command_args, block|
486
- res = send_command(command_type, command_args)
487
- res = block.call(res) if block
488
- results << res
489
- end
490
-
491
- return results
470
+ # batching transactional commands like MULTI / EXEC / DISCARD. To avoid
471
+ # native crashes we fall back to issuing those commands sequentially
472
+ # instead of via `Bindings.batch`.
473
+ tx_types = [RequestType::MULTI, RequestType::EXEC, RequestType::DISCARD]
474
+
475
+ if commands.any? { |(command_type, _args, _block)| tx_types.include?(command_type) }
476
+ results = []
477
+
478
+ commands.each do |command_type, command_args, block|
479
+ res = send_command(command_type, command_args)
480
+ res = block.call(res) if block
481
+ results << res
492
482
  end
483
+
484
+ return results
493
485
  end
494
486
 
495
487
  cmds = []
@@ -519,7 +511,7 @@ class Valkey
519
511
  batch_info = Bindings::BatchInfo.new
520
512
  batch_info[:cmd_count] = cmds.size
521
513
  batch_info[:cmds] = cmd_ptrs
522
- batch_info[:is_atomic] = is_atomic
514
+ batch_info[:is_atomic] = false
523
515
 
524
516
  batch_options = Bindings::BatchOptionsInfo.new
525
517
  batch_options[:retry_server_error] = true
@@ -708,10 +700,7 @@ class Valkey
708
700
 
709
701
  response = convert_response.call(result)
710
702
 
711
- # Don't run the caller's converter (e.g. Utils::Boolify) over the MULTI-queued
712
- # "QUEUED" sentinel - send_command's own `result == "QUEUED"` check (used to
713
- # track queued commands) needs to see the literal string, not e.g. `true`.
714
- if block_given? && response != "QUEUED"
703
+ if block_given?
715
704
  block.call(response)
716
705
  else
717
706
  response
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: valkey-glide-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.2
4
+ version: 1.0.0.pre.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Valkey GLIDE Maintainers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-07-22 00:00:00.000000000 Z
11
+ date: 2026-07-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi