I am trying to make a nice user-friendly failsafe recovery for Redis in Rails. I'm using the built in redis_cache_store
and have reconnect_attempts
set, but I would like to send the user to a custom error page in the event that Redis remains down, rather than have them be stuck on a nonresponsive page with no idea what is going on.
What I tried was to monkey-patch the failsafe
method in RedisCacheStore
by re-raising a new type of error, RedisUnavailableError
, which I then have my application_controller
catch and redirect_to
a static 500 page.
Where this breaks down is that I would like my application to just stop after the redirect. In the byebug
trace below, you can see that before the second-last block I've included, the redirect
line is reached.
But it does not stop; I've included just the first of a long sequence of further methods invoked that eventually results in another attempt to write to Redis, which is still down, which then re-raises the same sequence of exceptions, which Rails then will not catch a second time (indeed even if it did, that would be an infinite loop).
So my question is: is there any way to get the rescue_from
block to just stop and not continue triggering anything else after a certain line is reached?
Alternately, is there any way to on the fly "disable" Redis or change the cache store/settings to some nullary value in the rescue_from
block so that anything further triggered won't try to talk to Redis?
[40, 49] in (DELETED)/redis_failsafe.rb
40: rescue ::Redis::BaseConnectionError => e
41: byebug
42: handle_exception(exception: e, method: method, returning: returning)
43: returning
44: byebug
=> 45: raise RedisUnavailableError
46: # Re-raise the exception when reconnect attempt fails, so our application controller can handle it.
47: end
48: end
49:
(byebug) c
[INFO][15:50:51] [3cf7] [GET] [(DELETED):3000] [/suppliers]
∙ Redirecting to 500 page due to: RedisUnavailableError
[30, 39] in /(DELETED)/application_controller.rb
30: authorize_resource class: false
31:
32: rescue_from RedisUnavailableError do |exception|
33: byebug
34: Rails.logger.info "Redirecting to 500 page due to: #{exception.message}"
=> 35: redirect_to '/500.html'
36: byebug
37: end
38:
39: rescue_from ActiveRecord::RecordNotFound do
(byebug) n
∙ Redirected to http://(DELETED)/500.html
Return value is: nil
[32, 41] in /(DELETED)/application_controller.rb
32: rescue_from RedisUnavailableError do |exception|
33: byebug
34: Rails.logger.info "Redirecting to 500 page due to: #{exception.message}"
35: redirect_to '/500.html'
36: byebug
=> 37: end
38:
39: rescue_from ActiveRecord::RecordNotFound do
40: render status: :not_found, plain: 'Not found'
41: end
(byebug) n
[51, 60] in /(DELETED)/rescuable.rb
51: def rescue_with_handler(exception, object: self, visited_exceptions: [])
52: visited_exceptions << exception
53:
54: if handler = handler_for_rescue(exception, object: object)
55: handler.call exception
=> 56: exception
57: elsif exception
58: if visited_exceptions.include?(exception.cause)
59: nil
60: else
(byebug)