views:

376

answers:

3

In my Rails app, I have a script that updates some records in the database. When I send a SIGTERM to kill the script, it occasionally receives that signal while ActiveRecord is executing a query. This leads to an ActiveRecord::StatementInvalid exception being raised.

I'd like to catch StatementInvalid exceptions that occur when they're they're the result of a SIGTERM and exit the script. How can I tell that a StatementInvalid is occurring because of a signal and not for some other reason?

A: 

It sounds like this "script" is external to the Rails app (script/runner or similar?), so perhaps you can decouple the "signal handler" and "worker"? Eg can you fork a child process/thread/fiber/... to do the database update, and signal the parent to indicate "stop now"? Of course the parent will then have to "signal" the child to stop using some appropriate mechanism (not SIGTERM ;-)).

Martin Carpenter
+1  A: 

If you trap the TERM signal, I believe you will avoid the exception. You can do this at the beginning of your script (or really anywhere for that matter, but you only need to do it once).

 Signal.trap("TERM") do
   Kernel.exit!
 end

The reason you get the StatementInvalid error is Ruby handles the signal by raising a SIGTERM exception at the place of current execution. ActiveRecord is catching the exception and rethrowing it as StatementInvalid. By setting a Signal handler, Ruby will execute your handler instead of raising an exception.

See the Ruby Signal documentation for more information.

wuputah
Fixed, must use Kernel.exit! for this to work. Proof by example: http://gist.github.com/66735
wuputah
A: 

This is not an exact answer to the OP, however, you can control the exit point - the program will exit only after reaching the exit point defined by you.

time_to_die=false

# Prevent abrupt stopping of the daemon.
Signal.trap("TERM") { time_to_die=true; "SIG_IGN" }   

loop {
  .
  .
  exit_gracefully if time_to_die
  .
  .
}

def exit_gracefully
   #Cleaning up..
   $log.log "#{Time.now} TERM signal received. Exiting.."
   $db.close
   exit
 end
Jasim