2

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.

Dimitri
  • 623
  • 7
  • 14
  • What web server library are you using to run the application? Most of those already have signal handling and soft-shutdown built in. – matthewd Aug 02 '18 at 07:33
  • Actually I wrote 7 microservices, two of them are using rake-sinatra, others - listening for tasks from RabbitMQ exchanges, so not using *web*server framework at all. And event those two first uses sending tasks *and receiving responces* over amqp. – Dimitri Aug 02 '18 at 09:24

2 Answers2

2

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 }
Max
  • 21,123
  • 5
  • 49
  • 71
1

So, when I have both single-processing http server and at the same time an amqp responder (or two), seems that a global variable and Mutex are working fine.

Here is an example of cancelling a bunny worker.

https://gist.github.com/deemytch/6d3f8fd35a796f6b1cdc1e10a4911cc8

Dimitri
  • 623
  • 7
  • 14