How to write a spec for trap handling on rspec? - ruby-on-rails

say I have a class which traps SIGTERM and I want to write a spec to verify that that specific code is ran when SIGTERM is received. What is the proper way of doing it?
I've followed the answer for this topic: How to test signal handling in RSpec, particularly handling of SIGTERM?, but rspec is terminated on Process.kill happens.
I've also tried it like this:
raise SignalException.new('TERM')
But it doesn't seem to do anything (trap is not triggered). Finnaly, I've tried using 'allow' to substitute a method which is called during the spec to raise the signal or call Process.kill like this:
allow(<Class>).to receive(<method>).and_raise(SignalException.new('TERM'))
allow(<Class>).to receive(<method>).and_return(Process.kill 'TERM',0)
When raising the signal it also doesn't seem to do anything, and calling Process.kill simply ends rspec without a stack trace, just the word 'Terminated').
The trap code is like this:
trap('SIGTERM') {
Rails.logger.error('term')
#received_sigterm = true
}

Related

Custom busy handler for Rails SQLite3

I have a Rails/SQLite3 setup which is getting exceptions
ActiveRecord::StatementInvalid (SQLite3::BusyException: database is locked):
Now I understand that SQLite3 cannot do parallel writes, I'd be happy if they were blocked until the other write was finished, so done serially, rather than raising. This is something to do with default SQLite3 handler in C falling foul of Ruby's GIL, apparently. The fix, according to this post, is to install one's own "busy handler" for the SQLite3 connection, and I have this code in a Rails initializer
# config/initializers/sqlite3.rb
if ActiveRecord::Base.connection.adapter_name == 'SQLite' then
if raw_connection = ActiveRecord::Base.connection.raw_connection then
puts 'installing busy handler'
raw_connection.busy_handler do |count|
puts 'QUACK'
end
puts 'done'
else
raise RuntimeError 'no DB raw connection!'
end
end
On starting Rails (Puma in the console) I get the expected
installing busy handler
done
but on running parallel requests I get the exception with no QUACK, i.e., it seems that my handler has not been called. Is this a misunderstanding of the scope of initializers? Is there a "correct" way to get this handler installed?
(Obviously the real handler will not just say QUACK, but I find that my "real" handler has no effect so replace it by a debugging version.)

How can I ensure an operation runs before Rails exits, without using `at_exit`?

I have an operation that I need to execute in my rails application that before my Rails app dies. Is there a hook I can utilize in Rails for this? Something similar to at_exit I guess.
Ruby itself supports two hooks, BEGIN and END, which are run at the start of a script and as the interpreter stops running it.
See "What does Ruby's BEGIN do?" for more information.
The BEGIN documentation says:
Designates, via code block, code to be executed unconditionally before sequential execution of the program begins. Sometimes used to simulate forward references to methods.
puts times_3(gets.to_i)
BEGIN {
def times_3(n)
n * 3
end
}
The END documentations says:
Designates, via code block, code to be executed just prior to program termination.
END { puts "Bye!" }
Okay so I am making no guarantees as to impact because I have not tested this at all but you could define your own hook e.g.
ObjectSpace.define_finalizer(YOUR_RAILS_APP::Application, proc {puts "exiting now"})
Note this will execute after at_exit so the rails application server output will look like
Stopping ...
Exiting
exiting now
With Tin Man's solution included
ObjectSpace.define_finalizer(YOUR_RAILS_APP::Application, proc {puts "exiting now"})
END { puts "exiting again" }
Output is
Stopping ...
Exiting
exiting again
exiting now

Timing mistake in Elixir GenServer tutorial?

I'm going through the Elixir "Getting Started" tutorial, where the following code snippet is used:
test "removes buckets on exit", %{registry: registry} do
KV.Registry.create(registry, "shopping")
{:ok, bucket} = KV.Registry.lookup(registry, "shopping")
Agent.stop(bucket)
assert KV.Registry.lookup(registry, "shopping") == :error
end
Now, create/2 uses the cast operation whereas lookup uses call. So that means that an asynchronous call is executed and then immediately after that, a synchronous call which assumes the async action was performed successfully. Could timing issues result in the test failing when the code itself is correct, or is there some aspect of cast and call that I am missing?
Since GenServer's process all messages sequentially, the lookup call would block until the previous cast completed so there should be no timing issues.

how can we tell delayed_job when a delayed task fails so it will auto-retry?

Our app is hosted on heroku and we use delayed job when sending info to a remote system (via a GET to a url with some url params)
the remote system returns a success code usually, but it it;s real busy it returns a tryagain code.
suppose the our method is
def send_info
the_url = "http://mydomain.com/dosomething?arg=#{self.someval}"
the_result = open(the_url).read
successflag = get_success_flag_from(the_result)
end
and so somewhere in our code we do
#widget.delay.send_info
and that all works fine.
Except it does not automatically handle the case where the remote said to try back later.
Is there any way for the send_info method (which is what delayed job will execute) to "tell" delayed_job "retry me again"? Do we need to throw some custom exception or something?
Raising any kind of exception ought to cause delayed_job to requeue it (subject to only-trying-so-many-times); if you don't especially need a custom exception you can just raise a RuntimeError.

ActiveRecord::StatementInvalid when process receives SIGTERM?

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?
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.
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 ;-)).
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

Resources