Skip to content

Commit 33d4233

Browse files
authored
Merge pull request #1244 from zendesk/ktsanaktsidis/known_subclasses
Handle unknown subclasses of RedisClient::Error in clustering
2 parents 935f64f + 7cc4156 commit 33d4233

File tree

3 files changed

+49
-22
lines changed

3 files changed

+49
-22
lines changed

cluster/lib/redis/cluster/client.rb

+25-16
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class Client < RedisClient::Cluster
1212
RedisClient::Cluster::ErrorCollection => Redis::Cluster::CommandErrorCollection,
1313
RedisClient::Cluster::Transaction::ConsistencyError => Redis::Cluster::TransactionConsistencyError,
1414
RedisClient::Cluster::NodeMightBeDown => Redis::Cluster::NodeMightBeDown,
15-
).freeze
15+
)
1616

1717
class << self
1818
def config(**kwargs)
@@ -22,6 +22,29 @@ def config(**kwargs)
2222
def sentinel(**kwargs)
2323
super(protocol: 2, **kwargs)
2424
end
25+
26+
def translate_error!(error, mapping: ERROR_MAPPING)
27+
case error
28+
when RedisClient::Cluster::ErrorCollection
29+
error.errors.each do |_node, node_error|
30+
if node_error.is_a?(RedisClient::AuthenticationError)
31+
raise mapping.fetch(node_error.class), node_error.message, node_error.backtrace
32+
end
33+
end
34+
35+
remapped_node_errors = error.errors.map do |node_key, node_error|
36+
remapped = mapping.fetch(node_error.class, node_error.class).new(node_error.message)
37+
remapped.set_backtrace node_error.backtrace
38+
[node_key, remapped]
39+
end.to_h
40+
41+
raise(Redis::Cluster::CommandErrorCollection.new(remapped_node_errors, error.message).tap do |remapped|
42+
remapped.set_backtrace error.backtrace
43+
end)
44+
else
45+
Redis::Client.translate_error!(error, mapping: mapping)
46+
end
47+
end
2548
end
2649

2750
def initialize(*)
@@ -79,22 +102,8 @@ def multi(watch: nil, &block)
79102

80103
def handle_errors
81104
yield
82-
rescue RedisClient::Cluster::ErrorCollection => error
83-
error.errors.each do |_node, node_error|
84-
if node_error.is_a?(RedisClient::AuthenticationError)
85-
raise ERROR_MAPPING.fetch(node_error.class), node_error.message, node_error.backtrace
86-
end
87-
end
88-
remapped_node_errors = error.errors.map do |node_key, node_error|
89-
remapped = ERROR_MAPPING.fetch(node_error.class, node_error.class).new(node_error.message)
90-
remapped.set_backtrace node_error.backtrace
91-
[node_key, remapped]
92-
end.to_h
93-
raise(Redis::Cluster::CommandErrorCollection.new(remapped_node_errors, error.message).tap do |remapped|
94-
remapped.set_backtrace error.backtrace
95-
end)
96105
rescue ::RedisClient::Error => error
97-
raise ERROR_MAPPING.fetch(error.class), error.message, error.backtrace
106+
Redis::Cluster::Client.translate_error!(error)
98107
end
99108
end
100109
end

cluster/test/client_pipelining_test.rb

+18
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,22 @@ def test_pipelining_without_hash_tags
5858
end
5959
assert_equal 1.upto(6).map(&:to_s), result
6060
end
61+
62+
def test_pipeline_unmapped_errors_are_bubbled_up
63+
ex = Class.new(StandardError)
64+
assert_raises(ex) do
65+
redis.pipelined do |_pipe|
66+
raise ex, "boom"
67+
end
68+
end
69+
end
70+
71+
def test_pipeline_error_subclasses_are_mapped
72+
ex = Class.new(RedisClient::ConnectionError)
73+
assert_raises(Redis::ConnectionError) do
74+
redis.pipelined do |_pipe|
75+
raise ex, "tick tock"
76+
end
77+
end
78+
end
6179
end

lib/redis/client.rb

+6-6
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,18 @@ def sentinel(**kwargs)
2727
super(protocol: 2, **kwargs, client_implementation: ::RedisClient)
2828
end
2929

30-
def translate_error!(error)
31-
redis_error = translate_error_class(error.class)
30+
def translate_error!(error, mapping: ERROR_MAPPING)
31+
redis_error = translate_error_class(error.class, mapping: mapping)
3232
raise redis_error, error.message, error.backtrace
3333
end
3434

3535
private
3636

37-
def translate_error_class(error_class)
38-
ERROR_MAPPING.fetch(error_class)
37+
def translate_error_class(error_class, mapping: ERROR_MAPPING)
38+
mapping.fetch(error_class)
3939
rescue IndexError
40-
if (client_error = error_class.ancestors.find { |a| ERROR_MAPPING[a] })
41-
ERROR_MAPPING[error_class] = ERROR_MAPPING[client_error]
40+
if (client_error = error_class.ancestors.find { |a| mapping[a] })
41+
mapping[error_class] = mapping[client_error]
4242
else
4343
raise
4444
end

0 commit comments

Comments
 (0)