Callbacks for app lifecycle?
mperham opened this issue · comments
Hi, I'm wondering if puma has an API for applications to start and stop services that run within a puma process on start and stop. For example, Sidekiq has its on(event)
API so that services can run inside each Sidekiq process:
Sidekiq.configure_server do |config|
config.on(:startup) do
t = Thread.new do
loop do
puts Time.now
sleep 1
end
end
config.on(:shutdown) do
t.raise Interrupt
end
end
end
I'm planning on adding support for Sidekiq running natively within other processes but the other process needs to offer something like this API so Sidekiq can be started and stopped "cleanly". What would you recommend here? The only clean shutdown option I can find today is at_exit
which is less than ideal.
If one is using Puma in clustered mode, there are on_worker_boot
, on_worker_fork
, & on_worker_shutdown
.
See https://msp-greg.github.io/puma/Puma/DSL.html#on_worker_boot-instance_method
Nate may have some suggestions/thoughts...
What Greg said - we don't yet have equivalents yet for single mode: #2709
Ok, worker_boot and worker_shutdown will work with one caveat, there's no way for the two callbacks to share data except thru global variables. How do I start something in boot
and get a handle to it in shutdown
without creating this ugly global variable?
on_worker_boot do
$sidekiq = launcher = Sidekiq.configure_embed do |config|
config.queues = %w[critical default low]
end
launcher.run
end
on_worker_shutdown do
$sidekiq&.stop
end
Could puma pass an argument into those blocks which we can use to register components?
So, lets say one has four workers. Is it okay with you that the shared data is unique to each worker?
Yes, that's the intent. Since Sidekiq is threaded, we want to launch threads in each worker in order to get good utilization across cores.
I think I've got a 'non-breaking change' idea. Maybe tomorrow or Sunday.
Puma will hold a Hash reference and pass it into the blocks?
How do I start something in
boot
and get a handle to it inshutdown
without creating this ugly global variable?
Simply declare a local variable outside the block:
sidekiq = nil
on_worker_boot do
sidekiq = Sidekiq.configure_embed do |config|
config.queues = %w[critical default low]
end
sidekiq.run
end
on_worker_shutdown do
sidekiq&.stop
end
@schuetzm I think that will be shared by workers? I've got a patch for it, soon to create a test PR...