Skip to content

add flag to disable raising exceptions with pipelined #187

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

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

# 0.21.1

Expand Down
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,27 @@ end
# => ["OK", 1]
```

#### Exception management

The `exception` flag in the `#pipelined` method of `RedisClient` is a feature that modifies the pipeline execution
behavior. When set to `false`, it doesn't raise an exception when a command error occurs. Instead, it allows the
pipeline to execute all commands, and any failed command will be available in the returned array. (Defaults to `true`)

```ruby
results = redis.pipelined(exception: false) do |pipeline|
pipeline.call("SET", "foo", "bar") # => nil
pipeline.call("DOESNOTEXIST", 12) # => nil
pipeline.call("INCR", "baz") # => nil
end
# results => ["OK", #<RedisClient::CommandError: ERR unknown command 'DOESNOTEXIST', with args beginning with: '12'>, 2]

results.each do |result|
if result.is_a?(RedisClient::CommandError)
# Do something with the failed result
end
end
```

### Transactions

You can use [`MULTI/EXEC` to run a number of commands in an atomic fashion](https://redis.io/topics/transactions).
Expand Down
4 changes: 2 additions & 2 deletions lib/redis_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ def disable_reconnection(&block)
ensure_connected(retryable: false, &block)
end

def pipelined
def pipelined(exception: true)
pipeline = Pipeline.new(@command_builder)
yield pipeline

Expand All @@ -431,7 +431,7 @@ def pipelined
results = ensure_connected(retryable: pipeline._retryable?) do |connection|
commands = pipeline._commands
@middlewares.call_pipelined(commands, config) do
connection.call_pipelined(commands, pipeline._timeouts)
connection.call_pipelined(commands, pipeline._timeouts, exception: exception)
end
end

Expand Down
10 changes: 5 additions & 5 deletions lib/redis_client/connection_mixin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ def call(command, timeout)
end
end

def call_pipelined(commands, timeouts)
exception = nil
def call_pipelined(commands, timeouts, exception: true)
first_exception = nil

size = commands.size
results = Array.new(commands.size)
Expand All @@ -61,14 +61,14 @@ def call_pipelined(commands, timeouts)
elsif result.is_a?(Error)
result._set_command(commands[index])
result._set_config(config)
exception ||= result
first_exception ||= result
end

results[index] = result
end

if exception
raise exception
if first_exception && exception
raise first_exception
else
results
end
Expand Down
4 changes: 2 additions & 2 deletions lib/redis_client/decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ def with(*args)
end
ruby2_keywords :with if respond_to?(:ruby2_keywords, true)

def pipelined
@client.pipelined { |p| yield @_pipeline_class.new(p) }
def pipelined(exception: true)
@client.pipelined(exception: exception) { |p| yield @_pipeline_class.new(p) }
end

def multi(**kwargs)
Expand Down
27 changes: 27 additions & 0 deletions test/shared/redis_client_tests.rb
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,33 @@ def test_pipelining_error
assert_equal "42", @redis.call("GET", "foo")
end

def test_pipelining_error_with_explicit_raising_exception
error = assert_raises RedisClient::CommandError do
@redis.pipelined(exception: true) do |pipeline|
pipeline.call("DOESNOTEXIST", 12)
pipeline.call("SET", "foo", "42")
end
end

assert_equal ["DOESNOTEXIST", "12"], error.command

assert_equal "42", @redis.call("GET", "foo")
end

def test_pipelining_error_without_raising_exception
result = @redis.pipelined(exception: false) do |pipeline|
pipeline.call("DOESNOTEXIST", 12)
pipeline.call("SET", "foo", "42")
end

assert result[0].is_a?(RedisClient::CommandError)
assert_equal ["DOESNOTEXIST", "12"], result[0].command

assert_equal "OK", result[1]

assert_equal "42", @redis.call("GET", "foo")
end

def test_multi_error
error = assert_raises RedisClient::CommandError do
@redis.multi do |pipeline|
Expand Down