Skip to content

Commit af56b57

Browse files
committed
feat(pipeline): add flag to disable raising exceptions
1 parent 75f8dab commit af56b57

File tree

6 files changed

+67
-9
lines changed

6 files changed

+67
-9
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
- Made various performance optimizations to the Ruby driver. See #184.
44
- Always assume UTF-8 encoding instead of relying on `Encoding.default_external`.
5+
- Add `raise_exception` flag in `pipelined` allowing failed commands to be returned in the result array when set to `false`. See #187.
56

67
# 0.21.1
78

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,27 @@ end
332332
# => ["OK", 1]
333333
```
334334

335+
#### Exception management
336+
337+
The `raise_exception` flag in the `#pipelined` method of `RedisClient` is a feature that modifies the pipeline execution
338+
behavior. When set to `false`, it doesn't raise an exception when a command error occurs. Instead, it allows the
339+
pipeline to execute all commands, and any failed command will be available in the returned array. (Defaults to `true`)
340+
341+
```ruby
342+
results = redis.pipelined(raise_exception: false) do |pipeline|
343+
pipeline.call("SET", "foo", "bar") # => nil
344+
pipeline.call("DOESNOTEXIST", 12) # => raises RedisClient::CommandError
345+
pipeline.call("INCR", "baz") # => nil
346+
end
347+
# results => ["OK", #<RedisClient::CommandError: ERR unknown command 'DOESNOTEXIST', with args beginning with: '12'>, 2]
348+
349+
results.each do |result|
350+
if result.is_a?(RedisClient::CommandError)
351+
# Do something with the failed result
352+
end
353+
end
354+
```
355+
335356
### Transactions
336357

337358
You can use [`MULTI/EXEC` to run a number of commands in an atomic fashion](https://redis.io/topics/transactions).

lib/redis_client.rb

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -421,8 +421,8 @@ def disable_reconnection(&block)
421421
ensure_connected(retryable: false, &block)
422422
end
423423

424-
def pipelined
425-
pipeline = Pipeline.new(@command_builder)
424+
def pipelined(raise_exception: true)
425+
pipeline = Pipeline.new(@command_builder, raise_exception: raise_exception)
426426
yield pipeline
427427

428428
if pipeline._size == 0
@@ -431,7 +431,7 @@ def pipelined
431431
results = ensure_connected(retryable: pipeline._retryable?) do |connection|
432432
commands = pipeline._commands
433433
@middlewares.call_pipelined(commands, config) do
434-
connection.call_pipelined(commands, pipeline._timeouts)
434+
connection.call_pipelined(commands, pipeline._timeouts, pipeline._raise_exception)
435435
end
436436
end
437437

@@ -579,6 +579,10 @@ def _timeouts
579579
nil
580580
end
581581

582+
def _raise_exception
583+
true
584+
end
585+
582586
def _retryable?
583587
@retryable
584588
end
@@ -600,9 +604,10 @@ def _coerce!(results)
600604
end
601605

602606
class Pipeline < Multi
603-
def initialize(_command_builder)
604-
super
607+
def initialize(_command_builder, raise_exception: true)
608+
super(_command_builder)
605609
@timeouts = nil
610+
@raise_exception = raise_exception
606611
end
607612

608613
def blocking_call(timeout, *command, **kwargs, &block)
@@ -627,6 +632,10 @@ def _timeouts
627632
@timeouts
628633
end
629634

635+
def _raise_exception
636+
@raise_exception
637+
end
638+
630639
def _empty?
631640
@commands.empty?
632641
end

lib/redis_client/connection_mixin.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def call(command, timeout)
3939
end
4040
end
4141

42-
def call_pipelined(commands, timeouts)
42+
def call_pipelined(commands, timeouts, raise_exception = true)
4343
exception = nil
4444

4545
size = commands.size
@@ -67,7 +67,7 @@ def call_pipelined(commands, timeouts)
6767
results[index] = result
6868
end
6969

70-
if exception
70+
if exception && raise_exception
7171
raise exception
7272
else
7373
results

lib/redis_client/decorator.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ def with(*args)
4747
end
4848
ruby2_keywords :with if respond_to?(:ruby2_keywords, true)
4949

50-
def pipelined
51-
@client.pipelined { |p| yield @_pipeline_class.new(p) }
50+
def pipelined(**kwargs)
51+
@client.pipelined(**kwargs) { |p| yield @_pipeline_class.new(p) }
5252
end
5353

5454
def multi(**kwargs)

test/shared/redis_client_tests.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,33 @@ def test_pipelining_error
251251
assert_equal "42", @redis.call("GET", "foo")
252252
end
253253

254+
def test_pipelining_error_with_explicit_raising_exception
255+
error = assert_raises RedisClient::CommandError do
256+
@redis.pipelined(raise_exception: true) do |pipeline|
257+
pipeline.call("DOESNOTEXIST", 12)
258+
pipeline.call("SET", "foo", "42")
259+
end
260+
end
261+
262+
assert_equal ["DOESNOTEXIST", "12"], error.command
263+
264+
assert_equal "42", @redis.call("GET", "foo")
265+
end
266+
267+
def test_pipelining_error_without_raising_exception
268+
result = @redis.pipelined(raise_exception: false) do |pipeline|
269+
pipeline.call("DOESNOTEXIST", 12)
270+
pipeline.call("SET", "foo", "42")
271+
end
272+
273+
assert result[0].is_a?(RedisClient::CommandError)
274+
assert_equal ["DOESNOTEXIST", "12"], result[0].command
275+
276+
assert_equal "OK", result[1]
277+
278+
assert_equal "42", @redis.call("GET", "foo")
279+
end
280+
254281
def test_multi_error
255282
error = assert_raises RedisClient::CommandError do
256283
@redis.multi do |pipeline|

0 commit comments

Comments
 (0)