Search code examples
rubyrakecatch-blockbunny

Graceful shutdown of an amqp (and/or) http daemon, (and about global variables)


I wrote an API daemon. It hardly processes requests in the loop (inside processing it can call other services by http and amqp, overload sql database etc). I want to find the best way to make a graceful shutdown for it. So when it receives sigterm or interrupt, it must exit only after finishing current request processing. I don't use threads, but, I'm confused, that some libs can. Like Bunny.

So this is not a deal:

begin
 processing @request
rescue Interrupt => _
 db.close
end

I thinking about like that:

$gameover = false
trap('INT'){ $gameover = true }
trap('TERM'){ $gameover = true }

class HardApi < Sinatra::Base
  before{ lots of code }
  after do
    if $gameover
      db.close
      mq.disconnect
      log.info{"The end."}
      exit 0 
    end
  end

  post('/chat/:id') do |chat_id|
    BabblerCtrl.new(@request).upload_file(chat_id)
    MQService.publish_to_user(@client_id, chat_id, type: :service, :i_file_uploaded)
  end
  # etc
end

Also Ruby interpreter say that it can't use Mutex from inside trap.

So my question is the global variable the best solution for such flag or there is something like semaphore, that I can setup from trap?

The simple script, that I wrote to test was working, but there's alots of debug of my real program and I'm still not sure that that will work in prod.


Solution

  • A global variable is a fine way to do this, but you can make things more organized:

    class HardApi < Sinatra::Base
      @@should_exit = false
      def self.soft_exit
        @@should_exit = true
      end
    
      before{ lots of code }
      after do
        if @@should_exit
          # ...
        end
      end
    end
    
    trap('INT'){ HardApi.soft_exit }
    trap('TERM'){ HardApi.soft_exit }