Automatically handle missing database connection in ActiveRecord? - ruby-on-rails

With the launch of Amazon's Relational Database Service today and their 'enforced' maintenance windows I wondered if anyone has any solutions for handling a missing database connection in Rails.
Ideally I'd like to be able to automatically present a maintenance page to visitors if the database connection disappears (i.e. Amazon are doing their maintenance) - has anyone ever done anything like this?
Cheers
Arfon

You can do this with a Rack Middleware:
class RescueFromNoDB < Struct.new(:app)
def call(env)
app.call(env)
rescue Mysql::Error => e
if e.message =~ /Can't connect to/
[500, {"Content-Type" => "text/plain"}, ["Can't get to the DB server right now."]]
else
raise
end
end
end
Obviously you can customize the error message, and the e.message =~ /Can't connect to/ bit may just be paranoia, almost all other SQL errors should be caught inside ActionController::Dispatcher.

Related

How to rescue Socket Error in Ruby with RestClient?

I am using RestClient to make a network call in the ruby class. I am getting a SocketError whenever I am not connected to the internet. I have added a rescue block to catch the exception still I am not able to do so.
the error message is:
SocketError (Failed to open TCP connection to api.something.com:443 (getaddrinfo: Name or service not known))
module MyProject
class Client
def get_object(url, params={})
response = RestClient.get(url, {params: params})
rescue SocketError => e
puts "In Socket errror"
rescue => e
puts (e.class.inspect)
end
end
end
The broad rescue gets called and print SocketError, but why the previous rescue SocketError is not triggered!
Do you see something that I am missing?
There are a couple of exceptions you'll need to rescue in case you want to fail gracefully.
require 'rest-client'
def get_object(url, params={})
response = RestClient.get(url, {params: params})
rescue RestClient::ResourceNotFound => e
p e.class
rescue SocketError => e
p e.class
rescue Errno::ECONNREFUSED => e
p e.class
end
get_object('https://www.google.com/missing')
get_object('https://dasdkajsdlaadsasd.com')
get_object('dasdkajsdlaadsasd.com')
get_object('invalid')
get_object('127.0.0.1')
Because you can have problems regarding the uri being a 404 destination, or be a domain that doesn't existing, an IP, and so on.
As you can see above, you can have different scenarios that you may encounter when dealing with connecting to a remote uri. The code above rescue from the most common scenarios.
The first one the URL is missing, which is handled by RestClient itself.
The the following three below are invalid domains, which will fail with SocketError (basically a DNS error in this case).
Finally in the last call we try to connect to an IP that has no server running on it - therefore it throws an ERRNO::ECONNREFUSED

Avoid server stuck on SSE (Server Sent Event)

My server will get stuck, when users open SSE many times,
Because it seems Redis has some bugs with SSE.
The stream won't be closed even clients close browser or go to another page.
By the way I don't know when where the
logger.info "Stream closed"
logger.info "Client disconnected"
will be invoked ? (it doesn't be invoked when I close the browser)
Is it some workaround to avoid this issue ?
def new_prizes_stream
# http://ngauthier.com/2013/02/rails-4-sse-notify-listen.html
begin
response.headers.delete('Content-Length')
response.headers['Cache-Control'] = 'no-cache'
response.headers['Content-Type'] = 'text/event-stream'
logger.info "New stream starting, connecting to redis"
redis = Redis.new
redis.subscribe('messages.create', 'heartbeat') do |on|
on.message do |event, data|
if event == 'messages.create'
response.stream.write "event: #{event}\n"
response.stream.write "data: #{data}\n\n"
elsif event == 'heartbeat'
response.stream.write("event: heartbeat\ndata: heartbeat\n\n")
end
end
end
rescue IOError
logger.info "Stream closed"
rescue ActionController::Live::ClientDisconnected
logger.info "Client disconnected"
ensure
ap "close a live stream"
redis.quit
response.stream.close
end
end
My recommendation is that you do not create a connection on each request/SSE, and benchmark the results. Everytime you execute:
redis = Redis.new
If you can create the connection (singleton or factory patterns), instead of running this you would instead do something like:
redis = myPoolObj.getRedisConnection()
You then decide what you want to do on that Pool and how many connections you want to use. I checked the docs at redis-db but I did not see a built-in API for this like I saw when inspecting the python one.
You can open an issue on the repo asking if there's a built-in way to execute this without managing your own pool.
Did this help?

EventMachine::WebSocketClient.connect causes onclose to trigger on server side

There are three parts in my Rails application:
A part which listens for HTML5 web sockets (em-websocket)
a piece of JavaScript which connects to them
and a part which task is to connect to these sockets from inside of the same web application (em-websocket-client) (Yes, I am trying to do some IPC in Phusion Passenger environment)
JavaScript code connects fine and Web sockets server is happy with such a client, but when I connect from em-websocket-client a strange thing is happening: onclose handler is being called without calling onopen, and moreover - it is called for a socket which had been opened by web browser, not em-websocket-client.
The same em-websocket-client code, when executed in separate Ruby script through command line, works as planned. Here is the sample of em-websocket-client code:
require 'em-websocket-client'
class WebSocketsClient
def initialize
Thread.new do
log 'In a thread'
EventMachine.run do
log 'EM run'
#conn = EventMachine::WebSocketClient.connect("ws://localhost:5050?user_id=1&page_token=JYUTbfYDTTliglififi")
#conn.callback do
log 'Callback'
#conn.send_msg({ message_type: 'phone_call', user_id: 1, order_id: 1}.to_json)
#conn.close_connection
end
#conn.errback do |e|
log 'Errback'
puts "Got error: #{e}"
end
#conn.stream do |msg|
#log 'Stream'
#puts "<#{msg}>"
#if msg.data == 'done'
# #conn.close_connection
#end
end
#conn.disconnect do
puts 'gone'
EventMachine::stop_event_loop
end
end
end
end
def send_phone_call(order_id, user_id)
#conn.send_msg({ message_type: 'phone_call', user_id: user_id, order_id: order_id}.to_json)
end
def log(text)
puts "WebSocketsClient: #{text}\n"
end
end
WebSocketsClient.new
onclose on server side is being callsd as soon as EventMachine::WebSocketClient.connect is executed on client side. It doesn't even come to #conn.disconnect call.
One more thing I can conjecture is that this behaviour is due to using same EventMachine mechanism by server and client inside of same Rails application.

Unable to catch Mysql2::Error in Rails

Currently testing the following code:
def db_check
begin
schema_call = ActiveRecord::Base.establish_connection(
:adapter => 'mysql2',
:host => 'localhost',
:database => 'dev_db',
:username => 'dev_user',
:password => 'dev_pw').connection.execute("SELECT * FROM schema_migrations LIMIT 1")
if schema_call
render :status => 200, :file => "public/success.html"
else
render :status => 500, :file => "public/query_fail.html"
end
rescue Exception => e
puts "#{e.class} ;; #{e.message}"
logger.debug "#{e.class}"
render :status => 500, :file => "public/500.html"
end
end
The eventual goal is to have a call to a MySQL server to see if 1) the server is still up and 2) if the database is available. If the connection doesn't work, an Error is thrown, so I put the code in a rescue block. Unfortunately, even when I use rescue Exception, which I understand to be advised against, I still get a Mysql2::Error message in the browser (I also tried rescue Mysql2:Error, which had no effect).
The duplication of error logging in the rescue is extra attempts to get additional information to work with, but nothing has worked so far. Anyone know how to catch this error?
UPDATE: also, for additional context, testing the code with MySQL not running currently (condition if the DB server is down), get back the following:
Mysql2::Error
Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)
which makes partial sense, given the server is off, but I still don't understand why it isn't rescuing the error.
The reason the rescue isn't catching the Mysql2::Error is that the error isn't coming from the ActiveRecord::Base code, but rather is Rails throwing an error because it can't connect to the MySQL server that it expects (which I had turned off to test the above code).

How to check for specific rescue clause for error handling in Rails 3.x?

I have the following code:
begin
site = RedirectFollower.new(url).resolve
rescue => e
puts e.to_s
return false
end
Which throws errors like:
the scheme http does not accept registry part: www.officedepot.com;
the scheme http does not accept registry part: ww2.google.com/something;
Operation timed out - connect(2)
How can I add in another rescue for all errors that are like the scheme http does not accept registry part?
Since I want to do something other than just printing the error and returning false in that case.
That depends.
I see the three exception descriptions are different. Are the Exception types different as well?
If So you could write your code like this:
begin
site = RedirectFollower.new(url).resolve
rescue ExceptionType1 => e
#do something with exception that throws 'scheme http does not...'
else
#do something with other exceptions
end
If the exception types are the same then you'll still have a single rescue block but will decide what to do based on a regular expression. Perhaps something like:
begin
site = RedirectFollower.new(url).resolve
rescue Exception => e
if e.message =~ /the scheme http does not accept registry part/
#do something with it
end
end
Does this help?
Check what is exception class in case of 'the scheme http does not accept registry part' ( you can do this by puts e.class). I assume that this will be other than 'Operation timed out - connect(2)'
then:
begin
.
rescue YourExceptionClass => e
.
rescue => e
.
end
Important Note: Rescue with wildcard, defaults to StandardError. It will not rescue every error.
For example, SignalException: SIGTERMwill not be rescued with rescue => error. You will have to specifically use rescue SignalException => e.

Resources