Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Run GoodJob on puma boot #91

Closed
sj26 opened this issue Aug 24, 2020 · 5 comments
Closed

Run GoodJob on puma boot #91

sj26 opened this issue Aug 24, 2020 · 5 comments
Projects

Comments

@sj26
Copy link
Contributor

sj26 commented Aug 24, 2020

I'm running an app on Heroku and want to use a single dyno, and good_job is fitting the bill nicely. I want to run good_job in async mode on puma boot. But I don't want to run a new good_job async if I start a rails console, so I can't put it in my config/environments/production.rb. I also noticed that just setting queue_adapter to :good_job and then setting GOOD_JOB_EXECUTION_MODE=async wasn't enough to reliably start good job on puma boot until an activejob was touched (and autoloading configured [and started] the queue adapter). So I ended up using:

# config/application.rb

module MyGreatApp
  class Application < Rails::Application
    # ...

    config.active_job.queue_adapter = :good_job
  end
end
# config/puma.rb

# Make sure good_job runs in the puma process as soon as puma is started, and
# always -- even in production
# 
# Note if `workers` is set then this will need to be wrapped in `on_worker_boot`
Rails.application.config.active_job.queue_adapter = GoodJob::Adapter.new(execution_mode: :async)

This means the autoloading behaviour is all good, in development it defaults to running inline but also has an async processor running in puma which is processing immediately, and always starts an async processor in the puma process in production while using external from rails console for enqueuing jobs.

This seemed like a useful little journey worth sharing. :-)

@bensheldon bensheldon added this to Inbox in Backlog Aug 24, 2020
@bensheldon
Copy link
Owner

Thank you for documenting this 🙏 I think you're right that configuring the adapter inside of config/puma.rb works best right now.

I will dig into #89 too. I'm thinking that it may be safest to defer starting the background processing (e.g. the Notifier and Scheduler) until after the application has fully loaded via an initializer.

@sj26
Copy link
Contributor Author

sj26 commented Aug 24, 2020

Thanks! Yeah, I'm wondering if good_job needs a railtie. Perhaps it can use that to hook into the lifecycle of the rails server command, or something.

@sj26
Copy link
Contributor Author

sj26 commented Aug 25, 2020

Note that auto_terminate doesn't seem to have any effect in concurrent-ruby (ruby-concurrency/concurrent-ruby#841), so to get puma to reliably exit when running good_job async I had to change this to:

good_job = GoodJob::Adapter.new(execution_mode: :async)
Rails.application.config.active_job.queue_adapter = good_job
at_exit { good_job.shutdown }

@bensheldon
Copy link
Owner

The startup/initialization issues have been addressed in #199 and #205

This using if Rails.const_defined?("Server") ... is a pattern that seems reliable at least on Puma:

config.active_job.queue_adapter = :good_job
if ENV['GOOD_JOB_EXECUTION_MODE']
config.good_job.execution_mode = ENV['GOOD_JOB_EXECUTION_MODE'].to_sym
elsif Rails.const_defined?("Server")
config.good_job.execution_mode = :async
elsif Rails.const_defined?("Console")
config.good_job.execution_mode = :external
end

Backlog automation moved this from Inbox to Done Jan 31, 2021
@bensheldon
Copy link
Owner

Also, this is current Readme documentation for Puma:

good_job/README.md

Lines 508 to 531 in b1cd222

- When running Puma with workers (`WEB_CONCURRENCY > 0`) or another process-forking webserver, GoodJob's threadpool schedulers should be stopped before forking, restarted after fork, and cleanly shut down on exit. Stopping GoodJob's scheduler pre-fork is recommended to ensure that GoodJob does not continue executing jobs in the parent/controller process. For example, with Puma:
```ruby
# config/puma.rb
before_fork do
GoodJob.shutdown
end
on_worker_boot do
GoodJob.restart
end
on_worker_shutdown do
GoodJob.shutdown
end
MAIN_PID = Process.pid
at_exit do
GoodJob.shutdown if Process.pid == MAIN_PID
end
```
GoodJob is compatible with Puma's `preload_app!` method.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Backlog
  
Done
Development

No branches or pull requests

2 participants