Catch initialization error in Ruby on Rails - ruby-on-rails

What I want to do: redirect the user to a special error page if the database is down.
I'm using an Oracle database with the OCI adapter.
It appears that, if the database is unavailable (say, down for backup), that the OCI adapter throws an error before I ever hit a controller (based on the stack trace, it's while it's setting up the connection pool). So I can't use rescue_from or rescue_action, even in ApplicationController -- the rescue line is never reached.
Is there any way to "wrap" the initialization or otherwise rescue a particular error at a higher level (or earlier point) than ApplicationController?

Try overriding ActionController::Failsafe, or sticking another middleware before it on the stack, you should be able to catch almost all exceptions from there, and 302 at will.
Example:
class DbFailsafe
def new(app)
#app = app
end
def call(env)
#app.call(env)
rescue DataBaseError => e
[302, {"Content-Type" => "text/plain", "Location" => "http://otherserver.com/sorrt-the-site-is-down.html"}, ["You are being redirected to http://otherserver.com/sorrt-the-site-is-down.html"]]
end
end
and in your environment.rb
config.middleware.insert_after ::ActionController::Failsafe, DbFailsafe
I'd recommend (because a we should be returing a 5xx not a 3xx) rendering a simple page with a js redirect. This is simple to do, just edit public/500.html.

Related

How to rescue or prevent rails database connection exception and allow controller action to continue

I am trying to catch database connection issues on a specific request and take a different action when the database is down.
for example:
config/routes.rb
get 'my_route' => 'my_controller#my_action'
app/controllers/my_controller.rb
class MyController < Public::ApplicationController
def my_action
begin
url = database_lookup
rescue Mysql2::Error => e
url = fallback_lookup
end
redirect_to url
end
def database_lookup
# get info from db
end
def fallback_lookup
# lookup info in redis cache instead
end
end
This might work in certain situations, however if the database goes down and a new request comes in, active record middleware raises an exception long before ever reaching the controller.
I have been messing with Middleware to try and catch the error and do something else, but its not looking too promising.
What i'm trying is:
application.rb
config.middleware.insert_after 'ActionDispatch::RemoteIp', 'DatabaseExceptionHandler'
app/middleware/database_exception_handler.rb
class DatabaseExceptionHandler
def initialize app
#app = app
end
def call env
#status, #headers, #response = #app.call(env)
[#status, #headers, #response]
rescue Mysql2::Error => e
[#status, #headers, #response]
end
end
This is allowing me to catch connection exceptions that are raised when the request runs, but it doesn't help me much. I need to somehow get to a controller action still.
I think a simpler approach would be to skip all the active record connection nonsense for a specific controller action.
It seems silly to force a database connection for something that might not even need the database.
Anyone have any better ideas than what i've come up with so far?
I think a simpler approach would be to skip all the active record
connection nonsense for a specific controller action.
No doable or even a good idea. Rails does not process the configuration and middleware on a per request basis and that would not work with any kind of server that speeds the process up by booting up rails in the background.
This is allowing me to catch connection exceptions that are raised
when the request runs, but it doesn't help me much. I need to somehow
get to a controller action still.
This is probably a fools errand. If Rails bailed in the initialization process the integrity of the system is probably not great and you can't just continue on like its business as usual.
What you can do is set config.exceptions_app to customize the error pages. And get a less flaky database.

Can I put retry in rescue block without begin ... end?

I ran into a problem, when PG fails out of sync (well known problem)(example).
PG fails out of sync, sequence of id stops incrementing and raises ActiveRecord::RecordNotUnique error.
But all solutions proposed here (all I found) propose some manual solutions - either do some operations in console, either run custom rake task.
However, I find this unsatisfying for production: each times it happens, users get 500, while someone administrating server should operatively save the day. (And according to test data for some reason it possible will occur frequently in my case).
So I would like rather to patch ActiveRecord Base class to catch this specific error and rescue it.
I use this logic sometimes in controller:
class ApplicationController < ActionController::Base
rescue_from ActionController::ParameterMissing, ActiveRecord::RecordNotFound do |e|
# some logic here
end
end
However, here I don't need retry. Also, I would like to not to go deep in monkey patching, for example, without overriding Base create method.
So I was thinking of something like this:
module ActiveRecord
class Base
rescue ActiveRecord::RecordNotUnique => e
if e.message.include? '_pkey'
table =e.message.match(//) #regex to define table
ActiveRecord::Base.connection.reset_pk_sequence!(table)
retry
else
raise
end
end
end
But it most likely doesn't work, as I'm not sure if Rails/Ruby will understand what exactly it asked to retry.
Is there any solution?
P.S. Not related solution for overall problem of sequence which will work without manual command line commands and having unserved users are also appreciated.
To answer the question you're asking, no. rescue can only be used from within a begin..end block or method body.
begin
bad_method
rescue SomeException
retry
end
def some_method
bad_method
rescue SomeException
retry
end
rescue_from is just a framework helper method created because of how indirect the execution is in a controller.
To answer the question you're really asking, sure. You can override create_or_update with a rescue/retry.
module NonUniquePkeyRecovery
def create_or_update(*)
super
rescue ActiveRecord::RecordNotUnique => e
raise unless e.message.include? '_pkey'
self.class.connection.reset_pk_sequence!(self.class.table_name)
retry
end
end
ActiveSupport.on_load(:active_record) do
include NonUniquePkeyRecovery
end

Are default error pages available as JSON in Rails?

When you create a new Rails application, it comes with built-in error pages for certain error codes, 404, 422, 500 etc. Is there any built-in mechanism I can use to provide these errors (and others) as JSON or do I need to manually define each individual error in JSON?
for the Rails built-in pages you cannot do it because they are static inside public folder, but you can do something like:
# for 404
def not_found
# render what ever you like 404
end
# for 500
rescue_from StandardError do |exception|
# Your exception handling code here 500
end
inside you application controller

Rails not catching exception in rescue block

User model has defined indexes to be searched using ThinkingSphinx. However when I stop my searchd deamon, I would like my method to fail gracefully and not throw an error. Normally I do this by using a rescue block for catching exceptions. But in this case, it still throws the error and the puts statement is never executed.
def search_users(key)
begin
search_results = User.search(key,options)
rescue Exception
puts "Hello World!!!"
search_results = []
end
return search_results
end
Following is the error i get:
Riddle::ConnectionError (Connection to 127.0.0.1 on 3201 failed. Connection refused - connect(2)):
Is there any way out?
Solved it.
Add the :populate => true option to your search calls.
Normally, Thinking Sphinx lazily loads search results (allowing for
sphinx scopes and such) - but if you want the rescue to take effect,
then you'll need to force the results to load immediately - hence the
:populate option.
Refer the link posted above for further reading.
Given ruby return semantics, you can compress your code:
def search_users(key)
begin
User.search(key,options)
rescue
puts "Hello World!!!"
[]
end
end
It is evil to rescue Exception. Just use rescue, which rescues StandardError, which captures most of the stuff you want it to. Otherwise you also capture SyntaxError, LoadError, SystemExit and other stuff you don't intend. In this case, rescue Riddle::ConnectionError is appropriate, but not necessary.

Ruby equivalent to PHPs set_error_handler

I have just barely gotten into Ruby / ROR but need to quickly write a class for handling errors and doing something with them. I've been able to find the important examples/tutorials for the rest of what I need but I'm having trouble finding what the best alternative to PHP's "set_error_handler" is.
My goals are:
I'd like to write a class that will capture any ruby-level errors automatically.
I'd like for the class to also be called by the user when there are custom errors/exceptions to report.
I'd like this work for any ruby app, but my main focus is for ruby-on-rails applications as well. Thanks for your advice.
I think the closest equivalent in Rails is rescue_from - it allows you to specify code will catch any given exception (except some template errors - though there are ways round that). If you want, you could then hand it off to some other class. So I guess what you'd do in your case would be:
in app/controllers/application_controller.rb:
class ApplicationController < ActionController::Base
rescue_from Exception do |e|
MyExceptionHandler.handle_exception(e)
end
end
in lib/my_exception_handler.rb:
class MyExceptionHandler
def self.handle_exception exception
# your code goes here
end
end
If that helps, let me know and I'll dig out the link to how you catch template errors.
begin
#require all_kinds_of_things
"abc".size(1,2)
123.reverse
# rest of brilliant app
rescue Exception => e #Custom, catch-all exeption handler
puts "Doh...#{e}"
print "Do you want the backtrace? (Y) :"
puts e.backtrace if gets.chomp == "Y"
end
Define ApplicationController#rescue_in_public(exception) and put your custom handling code there.
This augments Rails' default exception handling at the top level - right before the HTTP response is generated. As your Rails apps grow in complexity and use external resources, there will be more exceptions that you'll want to handle much closer to where the exceptions are thrown, but this can get you started.
This method will only work on HTTP requests and will not catch exceptions in any custom rake tasks you create or code executed via rails runner.
Here's an example from one of my applications:
class ApplicationController < ActionController::Base
...
protected
def rescue_action_in_public (exception)
case exception
when ActionController::InvalidAuthenticityToken
if request.xhr?
render :update do |page|
page.redirect_to '/sessions/new/'
end
else
redirect_to '/sessions/new/'
end
when ActionController::NotImplemented
RAILS_DEFAULT_LOGGER.info("ActionController::NotImplemented\n#{request.inspect}")
render :nothing => true, :status => '500 Error'
else
super
end
end
end

Resources