solid-redis 1.0.7 → 1.0.9

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: 6bec7101ec6b158672f2419765b2d7aaad5ed70ca2c80062dd68a6fc57a0b4c5
4
- data.tar.gz: bc629f38d3cf771b7948d475c962e183cc6abd791b8195995bc93ae033c96443
3
+ metadata.gz: 6607abf91dca5bb6bdd3bd5545d516da558b139b10e69637383625757b8d0546
4
+ data.tar.gz: 68eb1f40407b5a6bb3f4dce69f54033baffa9f9f553e8d62dae5e72abf8fa278
5
5
  SHA512:
6
- metadata.gz: 590521ea57999193e9310448445ff843c602ed0f41064329c4ec5e1d6958d390e7d4fe1246a34b1566a861bc015e90b9bec61e37b922e4f056b6ab1650bf31d6
7
- data.tar.gz: d82981e8ca5ab260d0efa7413392fdb69cc8e3fc33a12b45c4ed5f847d1196917a4f12a84f453162a576c87fdc7accf5e9a86e9d1fae19982b7fdcbc61eca10a
6
+ metadata.gz: a871aa25c83c6fa53bb3a3c8d204699d3a6cb99f1811c233d35e71ac881fcd2216e109417b2cf1c4a34c1e3bbd409fdb64abf79ede22cb147daacc7c9e4d7e0f
7
+ data.tar.gz: d9555c58bbe771001d8ae4d47a2f628b1960530dcb455c7de0ddf6a0e4700f824f8db3e74a51b1dc3a77304dbf0a26123cc091a30878293c0d9c76c30bf801ce
data/CHANGELOG.md CHANGED
@@ -4,6 +4,20 @@ All notable changes to this project are documented in this file. The format
4
4
  follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the
5
5
  project adheres to [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [1.0.9] - 2026-09-29
8
+
9
+ ### Changed
10
+
11
+ - Require `solid-resp-ractor` 0.1.1 for threshold-based buffer compaction.
12
+
13
+ ## [1.0.8] - 2026-09-29
14
+
15
+ ### Changed
16
+
17
+ - Use `solid-resp-ractor` for RESP2/RESP3 encoding and parsing while
18
+ preserving SolidRedis's existing error classes and `SolidRedis::RESP`
19
+ compatibility API.
20
+
7
21
  ## [1.0.7] - 2026-09-29
8
22
 
9
23
  ### Changed
@@ -140,6 +154,8 @@ project adheres to [Semantic Versioning](https://semver.org/).
140
154
  TLS, reconnection), `Pool` owned by a single Ractor, pipelines, and
141
155
  lifecycle callbacks via `callback-collection`.
142
156
 
157
+ [1.0.9]: https://github.com/nicolasva/solid-redis/compare/v1.0.8...v1.0.9
158
+ [1.0.8]: https://github.com/nicolasva/solid-redis/compare/v1.0.7...v1.0.8
143
159
  [1.0.7]: https://github.com/nicolasva/solid-redis/compare/v1.0.6...v1.0.7
144
160
  [1.0.6]: https://github.com/nicolasva/solid-redis/compare/v1.0.5...v1.0.6
145
161
  [1.0.5]: https://github.com/nicolasva/solid-redis/compare/v1.0.4...v1.0.5
@@ -2,253 +2,52 @@
2
2
 
3
3
  module SolidRedis
4
4
  module RESP
5
- CRLF = "\r\n"
5
+ CRLF = SolidRespRactor::Encoder::CRLF
6
6
 
7
- module_function
8
-
9
- def encode(command)
10
- length = command.sum { |argument| argument.is_a?(Array) ? argument.length : 1 }
11
- raise ArgumentError, "Redis command cannot be empty" if length.zero?
7
+ module ErrorMapper
8
+ module_function
12
9
 
13
- command.each_with_object(+"*#{length}#{CRLF}") do |argument, buffer|
14
- if argument.is_a?(Array)
15
- argument.each { |value| append_argument(buffer, value) }
10
+ def call(message, blob:)
11
+ error_class = if message.start_with?("NOAUTH", "WRONGPASS")
12
+ AuthenticationError
16
13
  else
17
- append_argument(buffer, argument)
14
+ CommandError
18
15
  end
16
+ error_class.new(message)
19
17
  end
20
18
  end
21
19
 
22
- def append_argument(buffer, argument)
23
- value = encode_argument(argument)
24
- buffer << "$#{value.bytesize}#{CRLF}#{value}#{CRLF}"
25
- end
20
+ CODEC = SolidRespRactor::Codec.new(error_mapper: ErrorMapper)
21
+ Ractor.make_shareable(CODEC)
26
22
 
27
- def encode_argument(value)
28
- case value
29
- when String then value
30
- when Symbol, Integer, Float then value.to_s
31
- when true then "1"
32
- when false then "0"
33
- when nil then ""
34
- else value.to_s
35
- end
23
+ module_function
24
+
25
+ def encode(command)
26
+ CODEC.encode(command)
36
27
  end
37
28
 
38
- class Reader
29
+ class Reader < SolidRespRactor::Reader
39
30
  def initialize(io, read_timeout:)
40
- @io = io
41
- @read_timeout = read_timeout
42
- @buffer = +""
43
- @offset = 0
31
+ super(io, read_timeout: read_timeout, error_mapper: ErrorMapper)
44
32
  end
45
33
 
46
- # Temporarily overrides the read timeout. +nil+ waits forever, which is
47
- # what blocking commands such as BLPOP with a 0 timeout require.
48
- def with_timeout(timeout)
49
- previous = @read_timeout
50
- @read_timeout = timeout
51
- yield
52
- ensure
53
- @read_timeout = previous
54
- end
55
-
56
- # Waits until at least one byte is available without consuming it.
57
- # Returns +false+ on timeout. Unlike a timed-out +read+, this never
58
- # leaves a partially consumed frame behind, so it is the safe way to
59
- # poll for the next Pub/Sub message.
60
- def wait_readable(timeout)
61
- return true if available_bytes.positive?
62
- return true unless @io.respond_to?(:to_io)
63
-
64
- !IO.select([@io], nil, nil, timeout).nil?
65
- rescue IOError, SystemCallError => error
34
+ def read(...)
35
+ super
36
+ rescue SolidRespRactor::ProtocolError => error
37
+ raise ProtocolError, error.message, cause: error
38
+ rescue SolidRespRactor::TimeoutError => error
39
+ raise TimeoutError, error.message, cause: error
40
+ rescue SolidRespRactor::ConnectionError => error
66
41
  raise ConnectionError, error.message, cause: error
67
42
  end
68
43
 
69
- def read(exception: true)
70
- case (type = read_bytes(1))
71
- when "+" then read_line
72
- when "-" then error_response(read_line, exception)
73
- when ":" then Integer(read_line)
74
- when "$" then read_bulk
75
- when "*" then read_array(exception)
76
- when "_" then read_line && nil
77
- when "#" then read_line == "t"
78
- when "," then Float(read_line)
79
- when "(" then Integer(read_line)
80
- when "%" then read_map(exception)
81
- when "~", ">" then read_collection(exception)
82
- when "=" then read_verbatim
83
- when "!" then error_response(read_sized_string, exception)
84
- when "|" then read_attribute(exception)
85
- else raise ProtocolError, "Unknown RESP type byte: #{type.inspect}"
86
- end
87
- rescue EOFError
88
- raise ConnectionError, "Redis closed the connection"
89
- rescue ArgumentError => error
90
- raise ProtocolError, "Invalid Redis response: #{error.message}", cause: error
91
- end
92
-
93
- private
94
-
95
- def read_bulk
96
- length = Integer(read_line)
97
- return if length == -1
98
-
99
- read_sized_value(length)
100
- end
101
-
102
- def read_array(exception)
103
- length = Integer(read_line)
104
- return if length == -1
105
-
106
- finish_collection(Array.new(length) { read(exception: false) }, exception)
107
- end
108
-
109
- def read_collection(exception)
110
- length = Integer(read_line)
111
- finish_collection(Array.new(length) { read(exception: false) }, exception)
112
- end
113
-
114
- def read_map(exception)
115
- length = Integer(read_line)
116
- map = {}.tap do |result|
117
- length.times { result[read(exception: false)] = read(exception: false) }
118
- end
119
- finish_collection(map, exception)
120
- end
121
-
122
- def read_attribute(exception)
123
- attributes = read_map(false)
124
- value = read(exception: false)
125
- raise_nested_error(attributes) if exception
126
- raise_nested_error(value) if exception
127
- value
128
- end
129
-
130
- def finish_collection(value, exception)
131
- raise_nested_error(value) if exception
132
- value
133
- end
134
-
135
- def raise_nested_error(value)
136
- case value
137
- when CommandError
138
- raise value
139
- when Array
140
- value.each { |element| raise_nested_error(element) }
141
- when Hash
142
- value.each do |key, element|
143
- raise_nested_error(key)
144
- raise_nested_error(element)
145
- end
146
- end
147
- end
148
-
149
- def read_verbatim
150
- read_sized_string.byteslice(4..)
151
- end
152
-
153
- def read_sized_string
154
- read_sized_value(Integer(read_line))
155
- end
156
-
157
- def read_sized_value(length)
158
- value = read_bytes(length)
159
- actual = read_bytes(2)
160
- raise ProtocolError, "Expected CRLF, got #{actual.inspect}" unless actual == CRLF
161
-
162
- value
163
- end
164
-
165
- def read_line
166
- loop do
167
- if (index = @buffer.index(CRLF, @offset))
168
- value = @buffer.byteslice(@offset, index - @offset)
169
- @offset = index + 2
170
- clear_consumed_buffer
171
- return value
172
- end
173
- fill_buffer
174
- end
175
- end
176
-
177
- def read_bytes(length)
178
- fill_buffer while available_bytes < length
179
- value = @buffer.byteslice(@offset, length)
180
- @offset += length
181
- clear_consumed_buffer
182
- value
183
- end
184
-
185
- def fill_buffer
186
- compact_buffer
187
- wait_for = :readable
188
- deadline = nil
189
- loop do
190
- if @io.respond_to?(:to_io)
191
- chunk = @io.read_nonblock(16_384, exception: false)
192
- else
193
- chunk = @io.read(16_384)
194
- end
195
-
196
- if chunk == :wait_readable || chunk == :wait_writable
197
- wait_for = chunk == :wait_readable ? :readable : :writable
198
- deadline ||= monotonic_time + @read_timeout if @read_timeout
199
- remaining = deadline && deadline - monotonic_time
200
- if remaining && remaining <= 0
201
- raise TimeoutError, "Redis read timed out after #{@read_timeout}s"
202
- end
203
-
204
- readers = wait_for == :readable ? [@io] : nil
205
- writers = wait_for == :writable ? [@io] : nil
206
- unless IO.select(readers, writers, nil, remaining)
207
- raise TimeoutError, "Redis read timed out after #{@read_timeout}s"
208
- end
209
- next
210
- end
211
- raise EOFError if chunk.nil?
212
-
213
- @buffer << chunk
214
- return
215
- end
216
- rescue IOError, SystemCallError => error
44
+ def wait_readable(...)
45
+ super
46
+ rescue SolidRespRactor::TimeoutError => error
47
+ raise TimeoutError, error.message, cause: error
48
+ rescue SolidRespRactor::ConnectionError => error
217
49
  raise ConnectionError, error.message, cause: error
218
50
  end
219
-
220
- def available_bytes
221
- @buffer.bytesize - @offset
222
- end
223
-
224
- def clear_consumed_buffer
225
- return unless @offset == @buffer.bytesize
226
-
227
- @buffer.clear
228
- @offset = 0
229
- end
230
-
231
- def compact_buffer
232
- return if @offset.zero?
233
-
234
- @buffer = @buffer.byteslice(@offset..) || +""
235
- @offset = 0
236
- end
237
-
238
- def monotonic_time
239
- Process.clock_gettime(Process::CLOCK_MONOTONIC)
240
- end
241
-
242
- def error_response(message, exception)
243
- error = if message.start_with?("NOAUTH", "WRONGPASS")
244
- AuthenticationError.new(message)
245
- else
246
- CommandError.new(message)
247
- end
248
- raise error if exception
249
-
250
- error
251
- end
252
51
  end
253
52
  end
254
53
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SolidRedis
4
- VERSION = "1.0.7"
4
+ VERSION = "1.0.9"
5
5
  end
data/lib/solid_redis.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "base_service"
4
4
  require "callback_collection"
5
+ require "solid_resp_ractor"
5
6
 
6
7
  require_relative "solid_redis/version"
7
8
  require_relative "solid_redis/errors"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solid-redis
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.7
4
+ version: 1.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nicolas Vandenbogaerde
@@ -37,6 +37,20 @@ dependencies:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
39
  version: '0.2'
40
+ - !ruby/object:Gem::Dependency
41
+ name: solid-resp-ractor
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: 0.1.1
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: 0.1.1
40
54
  - !ruby/object:Gem::Dependency
41
55
  name: minitest
42
56
  requirement: !ruby/object:Gem::Requirement
@@ -104,7 +118,7 @@ licenses:
104
118
  - MIT
105
119
  metadata:
106
120
  rubygems_mfa_required: 'true'
107
- documentation_uri: https://www.rubydoc.info/gems/solid-redis/1.0.7
121
+ documentation_uri: https://www.rubydoc.info/gems/solid-redis/1.0.9
108
122
  source_code_uri: https://github.com/nicolasva/solid-redis
109
123
  changelog_uri: https://github.com/nicolasva/solid-redis/blob/main/CHANGELOG.md
110
124
  rdoc_options: []