puma / puma

A Ruby/Rack web server built for parallelism

Home Page:https://puma.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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 in shutdown 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...

@mperham

Using PR #2917, I believe the following should work:

on_worker_boot(:sidekiq) do |idx, data|
    data[:any_key] = launcher = Sidekiq.configure_embed do |config|
    config.queues = %w[critical default low]
  end
  launcher.run
end

on_worker_shutdown(:sidekiq) do |idx, data|
  data[:any_key]&.stop
end