Skip to content

Commit f239104

Browse files
author
Shaun Carlson
committed
handle cache errors in cache class, rewrite fetch method
1 parent 7b72849 commit f239104

8 files changed

Lines changed: 224 additions & 55 deletions

File tree

lib/active_remote/cached.rb

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,6 @@ def self.#{method_name}(#{expanded_method_args}, __active_remote_cached_options
190190
].compact
191191
192192
::ActiveRemote::Cached.cache.exist?(cache_key)
193-
rescue => e
194-
return false if e.message.include?("upstream failure")
195-
raise
196193
end
197194
RUBY
198195

@@ -220,9 +217,6 @@ def self.#{method_name}(#{expanded_method_args}, __active_remote_cached_options
220217
].compact
221218
222219
::ActiveRemote::Cached.cache.exist?(cache_key)
223-
rescue => e
224-
return false if e.message.include?("upstream failure")
225-
raise
226220
end
227221
RUBY
228222

@@ -269,9 +263,6 @@ def self.#{method_name}(#{expanded_method_args}, __active_remote_cached_options
269263
self.find(#{expanded_search_args})
270264
end
271265
end
272-
rescue => e
273-
return self.find(#{expanded_search_args}) if e.message.include?("upstream failure")
274-
raise
275266
end
276267
RUBY
277268
end
@@ -320,9 +311,6 @@ def self.#{method_name}(#{expanded_method_args}, __active_remote_cached_options
320311
self.search(#{expanded_search_args})
321312
end
322313
end
323-
rescue => e
324-
return self.search(#{expanded_search_args}) if e.message.include?("upstream failure")
325-
raise
326314
end
327315
RUBY
328316
end
@@ -381,13 +369,6 @@ def self.#{method_name}(#{expanded_method_args}, __active_remote_cached_options
381369
raise ::ActiveRemote::RemoteRecordNotFound.new(self.class) if results.first.nil?
382370
results
383371
end
384-
rescue => e
385-
if e.message.include?("upstream failure")
386-
results = self.search(#{expanded_search_args})
387-
raise ::ActiveRemote::RemoteRecordNotFound.new(self.class) if results.first.nil?
388-
return results
389-
end
390-
raise
391372
end
392373
RUBY
393374
end

lib/active_remote/cached/cache.rb

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ def initialize(new_cache_provider)
2020
def delete(*args)
2121
nested_cache_provider.delete(*args)
2222
super
23+
rescue => e
24+
raise e unless ::ActiveRemote::Cached.default_options[:fallback_on_cache_error]
25+
error_proc = ::ActiveRemote::Cached.default_options[:cache_error_proc]
26+
error_proc.call(e) if error_proc.respond_to?(:call)
27+
28+
nil
2329
end
2430

2531
def enable_nested_caching!
@@ -28,10 +34,20 @@ def enable_nested_caching!
2834

2935
def exist?(*args)
3036
nested_cache_provider.exist?(*args) || super
37+
rescue => e
38+
raise e unless ::ActiveRemote::Cached.default_options[:fallback_on_cache_error]
39+
error_proc = ::ActiveRemote::Cached.default_options[:cache_error_proc]
40+
error_proc.call(e) if error_proc.respond_to?(:call)
41+
42+
false
3143
end
3244

3345
def fetch(name, options = {})
34-
fetch_value = nested_cache_provider.fetch(name, options) { super }
46+
fetch_value = read(name, options)
47+
if fetch_value.nil?
48+
fetch_value = super if block_given?
49+
write(name, fetch_value, options) if valid_fetched_value?(fetch_value, options)
50+
end
3551

3652
unless valid_fetched_value?(fetch_value, options)
3753
delete(name)
@@ -42,11 +58,23 @@ def fetch(name, options = {})
4258

4359
def read(*args)
4460
nested_cache_provider.read(*args) || super
61+
rescue => e
62+
raise e unless ::ActiveRemote::Cached.default_options[:fallback_on_cache_error]
63+
error_proc = ::ActiveRemote::Cached.default_options[:cache_error_proc]
64+
error_proc.call(e) if error_proc.respond_to?(:call)
65+
66+
nil
4567
end
4668

4769
def write(*args)
4870
nested_cache_provider.write(*args)
4971
super
72+
rescue => e
73+
raise e unless ::ActiveRemote::Cached.default_options[:fallback_on_cache_error]
74+
error_proc = ::ActiveRemote::Cached.default_options[:cache_error_proc]
75+
error_proc.call(e) if error_proc.respond_to?(:call)
76+
77+
nil
5078
end
5179

5280
private

lib/active_remote/cached/railtie.rb

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,22 @@ class Railtie < ::Rails::Railtie
66
config.active_remote_cached = ::ActiveSupport::OrderedOptions.new
77

88
initializer "active_remote-cached.initialize_cache" do |app|
9+
config.active_remote_cached.cache_error_proc ||= nil
910
config.active_remote_cached.expires_in ||= 5.minutes
11+
config.active_remote_cached.fallback_on_cache_error ||= false
1012
config.active_remote_cached.race_condition_ttl ||= 5.seconds
1113

12-
::ActiveRemote::Cached.cache(Rails.cache)
14+
::ActiveRemote::Cached.cache(::Rails.cache)
1315

1416
if config.active_remote_cached.enable_nested_caching
1517
::ActiveRemote::Cached.cache.enable_nested_caching!
1618
end
1719

1820
::ActiveRemote::Cached.default_options(
19-
:expires_in => config.active_remote_cached.expires_in,
20-
:race_condition_ttl => config.active_remote_cached.race_condition_ttl
21+
:cache_error_proc => config.active_remote_cached.cache_error_proc,
22+
:expires_in => config.active_remote_cached.expires_in,
23+
:fallback_on_cache_error => config.active_remote_cached.fallback_on_cache_error,
24+
:race_condition_ttl => config.active_remote_cached.race_condition_ttl
2125
)
2226
end
2327

spec/active_remote/cached/cache_spec.rb

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
require 'spec_helper'
22

3+
class LoggerClass
4+
def self.log(_); end
5+
end
6+
37
describe ::ActiveRemote::Cached::Cache do
48
describe "API" do
59
it "validates #delete present" do
@@ -32,4 +36,180 @@
3236
_(error.message).must_match(/respond_to.*write/i)
3337
end
3438
end
39+
40+
describe "#delete" do
41+
it "returns nil when cache write attempt raises an error" do
42+
::ActiveRemote::Cached.default_options(:fallback_on_cache_error => true, :cache_error_proc => lambda { |e| LoggerClass.log(e) } )
43+
cache = HashCache.new
44+
cache.expects(:delete).raises(::RuntimeError, "kaBOOM")
45+
ar_cache = ::ActiveRemote::Cached::Cache.new(cache)
46+
_(ar_cache.delete("some_key")).must_be_nil
47+
end
48+
49+
it "calls error proc" do
50+
::ActiveRemote::Cached.default_options(:fallback_on_cache_error => true, :cache_error_proc => lambda { |e| LoggerClass.log(e) } )
51+
cache = HashCache.new
52+
cache.expects(:delete).raises(::RuntimeError, "kaBOOM")
53+
ar_cache = ::ActiveRemote::Cached::Cache.new(cache)
54+
::ActiveRemote::Cached.default_options[:cache_error_proc].expects(:call)
55+
ar_cache.delete("some_key")
56+
end
57+
58+
describe "when fallback_on_cache_error is false" do
59+
it "raises error" do
60+
::ActiveRemote::Cached.default_options(:fallback_on_cache_error => false, :cache_error_proc => lambda { |e| LoggerClass.log(e) } )
61+
cache = HashCache.new
62+
cache.expects(:delete).raises(::RuntimeError, "kaBOOM")
63+
ar_cache = ::ActiveRemote::Cached::Cache.new(cache)
64+
_ { ar_cache.delete("some_key") }.must_raise(::RuntimeError, "kaBOOM")
65+
end
66+
67+
it "doesn't invoke error proc" do
68+
::ActiveRemote::Cached.default_options(:fallback_on_cache_error => false, :cache_error_proc => lambda { |e| LoggerClass.log(e) } )
69+
cache = HashCache.new
70+
cache.expects(:delete).raises(::RuntimeError, "kaBOOM")
71+
ar_cache = ::ActiveRemote::Cached::Cache.new(cache)
72+
::ActiveRemote::Cached.default_options[:cache_error_proc].expects(:log).never
73+
_ { ar_cache.delete("some_key") }.must_raise(::RuntimeError, "kaBOOM")
74+
end
75+
end
76+
end
77+
78+
describe "#exist?" do
79+
it "returns false when cache exist attempt check raises an error" do
80+
::ActiveRemote::Cached.default_options(:fallback_on_cache_error => true, :cache_error_proc => lambda { |e| LoggerClass.log(e) } )
81+
cache = HashCache.new
82+
cache.expects(:exist?).raises(::RuntimeError, "kaBOOM")
83+
ar_cache = ::ActiveRemote::Cached::Cache.new(cache)
84+
_(ar_cache.exist?("some_key")).must_equal(false)
85+
end
86+
87+
it "calls error proc" do
88+
::ActiveRemote::Cached.default_options(:fallback_on_cache_error => true, :cache_error_proc => lambda { |e| LoggerClass.log(e) } )
89+
cache = HashCache.new
90+
cache.expects(:exist?).raises(::RuntimeError, "kaBOOM")
91+
ar_cache = ::ActiveRemote::Cached::Cache.new(cache)
92+
::ActiveRemote::Cached.default_options[:cache_error_proc].expects(:call)
93+
ar_cache.exist?("some_key")
94+
end
95+
96+
describe "when fallback_on_cache_error is false" do
97+
it "raises error" do
98+
::ActiveRemote::Cached.default_options(:fallback_on_cache_error => false, :cache_error_proc => lambda { |e| LoggerClass.log(e) } )
99+
cache = HashCache.new
100+
cache.expects(:exist?).raises(::RuntimeError, "kaBOOM")
101+
ar_cache = ::ActiveRemote::Cached::Cache.new(cache)
102+
_ { ar_cache.exist?("some_key") }.must_raise(::RuntimeError, "kaBOOM")
103+
end
104+
105+
it "doesn't invoke error proc" do
106+
::ActiveRemote::Cached.default_options(:fallback_on_cache_error => false, :cache_error_proc => lambda { |e| LoggerClass.log(e) } )
107+
cache = HashCache.new
108+
cache.expects(:delete).raises(::RuntimeError, "kaBOOM")
109+
ar_cache = ::ActiveRemote::Cached::Cache.new(cache)
110+
::ActiveRemote::Cached.default_options[:cache_error_proc].expects(:log).never
111+
_ { ar_cache.delete("some_key") }.must_raise(::RuntimeError, "kaBOOM")
112+
end
113+
end
114+
end
115+
116+
describe "#fetch" do
117+
it "returns cached value when read finds something" do
118+
cache = HashCache.new
119+
cache.write("some_key", "tada!")
120+
ar_cache = ::ActiveRemote::Cached::Cache.new(cache)
121+
_(ar_cache.fetch("some_key")).must_equal("tada!")
122+
end
123+
124+
it "invokes block when read returns nothing" do
125+
cache = HashCache.new
126+
ar_cache = ::ActiveRemote::Cached::Cache.new(cache)
127+
_(ar_cache.fetch("some_key") { "tada!" }).must_equal("tada!")
128+
end
129+
130+
it "removes cache key when returned value is invalid" do
131+
cache = HashCache.new
132+
cache.write("some_key", [])
133+
ar_cache = ::ActiveRemote::Cached::Cache.new(cache)
134+
_(cache.exist?("some_key")).must_equal(true)
135+
ar_cache.fetch("some_key")
136+
_(cache.exist?("some_key")).must_equal(false)
137+
end
138+
end
139+
140+
describe "#read" do
141+
it "returns nil when cache read attempt raises an error" do
142+
::ActiveRemote::Cached.default_options(:fallback_on_cache_error => true, :cache_error_proc => lambda { |e| LoggerClass.log(e) } )
143+
cache = HashCache.new
144+
cache.expects(:read).raises(::RuntimeError, "kaBOOM")
145+
ar_cache = ::ActiveRemote::Cached::Cache.new(cache)
146+
_(ar_cache.read("some_key")).must_be_nil
147+
end
148+
149+
it "calls error proc" do
150+
::ActiveRemote::Cached.default_options(:fallback_on_cache_error => true, :cache_error_proc => lambda { |e| LoggerClass.log(e) } )
151+
cache = HashCache.new
152+
cache.expects(:read).raises(::RuntimeError, "kaBOOM")
153+
ar_cache = ::ActiveRemote::Cached::Cache.new(cache)
154+
::ActiveRemote::Cached.default_options[:cache_error_proc].expects(:call)
155+
ar_cache.read("some_key")
156+
end
157+
158+
describe "when fallback_on_cache_error is false" do
159+
it "raises error" do
160+
::ActiveRemote::Cached.default_options(:fallback_on_cache_error => false, :cache_error_proc => lambda { |e| LoggerClass.log(e) } )
161+
cache = HashCache.new
162+
cache.expects(:read).raises(::RuntimeError, "kaBOOM")
163+
ar_cache = ::ActiveRemote::Cached::Cache.new(cache)
164+
_ { ar_cache.read("some_key") }.must_raise(::RuntimeError, "kaBOOM")
165+
end
166+
167+
it "doesn't invoke error proc" do
168+
::ActiveRemote::Cached.default_options(:fallback_on_cache_error => false, :cache_error_proc => lambda { |e| LoggerClass.log(e) } )
169+
cache = HashCache.new
170+
cache.expects(:read).raises(::RuntimeError, "kaBOOM")
171+
ar_cache = ::ActiveRemote::Cached::Cache.new(cache)
172+
::ActiveRemote::Cached.default_options[:cache_error_proc].expects(:log).never
173+
_ { ar_cache.read("some_key") }.must_raise(::RuntimeError, "kaBOOM")
174+
end
175+
end
176+
end
177+
178+
describe "#write" do
179+
it "returns nil when cache write attempt raises an error" do
180+
::ActiveRemote::Cached.default_options(:fallback_on_cache_error => true, :cache_error_proc => lambda { |e| LoggerClass.log(e) } )
181+
cache = HashCache.new
182+
cache.expects(:write).raises(::RuntimeError, "kaBOOM")
183+
ar_cache = ::ActiveRemote::Cached::Cache.new(cache)
184+
_(ar_cache.write("some_key", "some_value")).must_be_nil
185+
end
186+
187+
it "calls error proc" do
188+
::ActiveRemote::Cached.default_options(:fallback_on_cache_error => true, :cache_error_proc => lambda { |e| LoggerClass.log(e) } )
189+
cache = HashCache.new
190+
cache.expects(:write).raises(::RuntimeError, "kaBOOM")
191+
ar_cache = ::ActiveRemote::Cached::Cache.new(cache)
192+
::ActiveRemote::Cached.default_options[:cache_error_proc].expects(:call)
193+
ar_cache.write("some_key", "some_value")
194+
end
195+
196+
describe "when fallback_on_cache_error is false" do
197+
it "raises error" do
198+
::ActiveRemote::Cached.default_options(:fallback_on_cache_error => false, :cache_error_proc => lambda { |e| LoggerClass.log(e) } )
199+
cache = HashCache.new
200+
cache.expects(:write).raises(::RuntimeError, "kaBOOM")
201+
ar_cache = ::ActiveRemote::Cached::Cache.new(cache)
202+
_ { ar_cache.write("some_key", "some_value") }.must_raise(::RuntimeError, "kaBOOM")
203+
end
204+
205+
it "doesn't invoke error proc" do
206+
::ActiveRemote::Cached.default_options(:fallback_on_cache_error => false, :cache_error_proc => lambda { |e| LoggerClass.log(e) } )
207+
cache = HashCache.new
208+
cache.expects(:write).raises(::RuntimeError, "kaBOOM")
209+
ar_cache = ::ActiveRemote::Cached::Cache.new(cache)
210+
::ActiveRemote::Cached.default_options[:cache_error_proc].expects(:log).never
211+
_ { ar_cache.write("some_key", "some_value") }.must_raise(::RuntimeError, "kaBOOM")
212+
end
213+
end
214+
end
35215
end

spec/active_remote/cached_exist_methods_spec.rb

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -129,16 +129,10 @@ def self.search; nil; end
129129

130130
describe "when cache raises upstream failure redis error" do
131131
it "returns false" do
132+
::ActiveRemote::Cached.default_options(:fallback_on_cache_error => true)
132133
::ActiveRemote::Cached.cache.expects(:exist?).raises(::RuntimeError, "upstream failure")
133134
_(ExistMethodClass.cached_exist_search_by_guid?(:guid)).must_equal(false)
134135
end
135136
end
136-
137-
describe "when cache raises any other kind of error" do
138-
it "allows error to pass through" do
139-
::ActiveRemote::Cached.cache.expects(:exist?).raises(::RuntimeError, "kaBOOM")
140-
assert_raises(::RuntimeError, "kaBOOM") { ExistMethodClass.cached_exist_search_by_guid?(:guid) }
141-
end
142-
end
143137
end
144138
end

spec/active_remote/cached_find_methods_spec.rb

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -102,23 +102,14 @@ def self.search; nil; end
102102

103103
describe "when cache raises upstream failure redis error" do
104104
it "falls back to calling find" do
105-
::ActiveRemote::Cached.cache.expects(:fetch).raises(::RuntimeError, "upstream failure")
105+
::ActiveRemote::Cached.default_options(:fallback_on_cache_error => true)
106+
::ActiveRemote::Cached.cache.send(:nested_cache_provider).expects(:read).raises(::RuntimeError, "upstream failure")
106107

107108
FindMethodClass.stub(:find, "foo") do
108109
_(FindMethodClass.cached_find_by_guid(:guid)).must_equal("foo")
109110
end
110111
end
111112
end
112-
113-
describe "when cache raises any other kind of error" do
114-
it "allows error to pass through" do
115-
::ActiveRemote::Cached.cache.expects(:fetch).raises(::RuntimeError, "kaBOOM")
116-
117-
FindMethodClass.stub(:find, "foo") do
118-
assert_raises(::RuntimeError, "kaBOOM") { FindMethodClass.cached_find_by_guid(:guid) }
119-
end
120-
end
121-
end
122113
end
123114

124115
describe "#cached_find_by_foo" do

spec/active_remote/cached_search_methods_spec.rb

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -169,23 +169,14 @@ def self.search; nil; end
169169

170170
describe "when cache raises upstream failure redis error" do
171171
it "falls back to calling search" do
172-
::ActiveRemote::Cached.cache.expects(:fetch).raises(::RuntimeError, "upstream failure")
172+
::ActiveRemote::Cached.default_options(:fallback_on_cache_error => true)
173+
::ActiveRemote::Cached.cache.send(:nested_cache_provider).expects(:read).raises(::RuntimeError, "upstream failure")
173174

174175
SearchMethodClass.stub(:search, "foo") do
175176
_(SearchMethodClass.cached_search_by_guid(:guid)).must_equal("foo")
176177
end
177178
end
178179
end
179-
180-
describe "when cache raises any other kind of error" do
181-
it "allows error to pass through" do
182-
::ActiveRemote::Cached.cache.expects(:fetch).raises(::RuntimeError, "kaBOOM")
183-
184-
SearchMethodClass.stub(:search, "foo") do
185-
assert_raises(::RuntimeError, "kaBOOM") { SearchMethodClass.cached_search_by_guid(:guid) }
186-
end
187-
end
188-
end
189180
end
190181

191182
describe "#cached_search_by_foo" do

0 commit comments

Comments
 (0)