Skip to content

Commit

Permalink
Defer async initialization until Rails fully initialized
Browse files Browse the repository at this point in the history
  • Loading branch information
bensheldon committed Nov 16, 2021
1 parent 3ae731b commit 20a1889
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 31 deletions.
45 changes: 34 additions & 11 deletions lib/good_job/adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ module GoodJob
# ActiveJob Adapter.
#
class Adapter
# @!attribute [r] instances
# @!scope class
# List of all instantiated Adapters in the current process.
# @return [Array<GoodJob::Adapter>, nil]
cattr_reader :instances, default: [], instance_reader: false

# @param execution_mode [Symbol, nil] specifies how and where jobs should be executed. You can also set this with the environment variable +GOOD_JOB_EXECUTION_MODE+.
#
# - +:inline+ executes jobs immediately in whatever process queued them (usually the web server process). This should only be used in test and development environments.
Expand All @@ -20,7 +26,8 @@ class Adapter
# @param max_threads [Integer, nil] sets the number of threads per scheduler to use when +execution_mode+ is set to +:async+. The +queues+ parameter can specify a number of threads for each group of queues which will override this value. You can also set this with the environment variable +GOOD_JOB_MAX_THREADS+. Defaults to +5+.
# @param queues [String, nil] determines which queues to execute jobs from when +execution_mode+ is set to +:async+. See {file:README.md#optimize-queues-threads-and-processes} for more details on the format of this string. You can also set this with the environment variable +GOOD_JOB_QUEUES+. Defaults to +"*"+.
# @param poll_interval [Integer, nil] sets the number of seconds between polls for jobs when +execution_mode+ is set to +:async+. You can also set this with the environment variable +GOOD_JOB_POLL_INTERVAL+. Defaults to +1+.
def initialize(execution_mode: nil, queues: nil, max_threads: nil, poll_interval: nil)
# @param start_async_on_initialize [Boolean] whether to start the async scheduler when the adapter is initialized.
def initialize(execution_mode: nil, queues: nil, max_threads: nil, poll_interval: nil, start_async_on_initialize: Rails.application.initialized?)
@configuration = GoodJob::Configuration.new(
{
execution_mode: execution_mode,
Expand All @@ -30,16 +37,9 @@ def initialize(execution_mode: nil, queues: nil, max_threads: nil, poll_interval
}
)
@configuration.validate!
self.class.instances << self

if execute_async? # rubocop:disable Style/GuardClause
@notifier = GoodJob::Notifier.new
@poller = GoodJob::Poller.new(poll_interval: @configuration.poll_interval)
@scheduler = GoodJob::Scheduler.from_configuration(@configuration, warm_cache_on_initialize: Rails.application.initialized?)
@notifier.recipients << [@scheduler, :create_thread]
@poller.recipients << [@scheduler, :create_thread]

@cron_manager = GoodJob::CronManager.new(@configuration.cron_entries, start_on_initialize: Rails.application.initialized?) if @configuration.enable_cron?
end
start_async if start_async_on_initialize
end

# Enqueues the ActiveJob job to be performed.
Expand Down Expand Up @@ -74,7 +74,7 @@ def enqueue_at(active_job, timestamp)
job_state = { queue_name: execution.queue_name }
job_state[:scheduled_at] = execution.scheduled_at if execution.scheduled_at

executed_locally = execute_async? && @scheduler.create_thread(job_state)
executed_locally = execute_async? && @scheduler&.create_thread(job_state)
Notifier.notify(job_state) unless executed_locally
end

Expand All @@ -97,6 +97,7 @@ def shutdown(timeout: :default)

executables = [@notifier, @poller, @scheduler].compact
GoodJob._shutdown_all(executables, timeout: timeout)
@_async_started = false
end

# Whether in +:async+ execution mode.
Expand All @@ -119,6 +120,28 @@ def execute_inline?
@configuration.execution_mode == :inline
end

# Start async executors
# @return void
def start_async
return unless execute_async?

@notifier = GoodJob::Notifier.new
@poller = GoodJob::Poller.new(poll_interval: @configuration.poll_interval)
@scheduler = GoodJob::Scheduler.from_configuration(@configuration, warm_cache_on_initialize: true)
@notifier.recipients << [@scheduler, :create_thread]
@poller.recipients << [@scheduler, :create_thread]

@cron_manager = GoodJob::CronManager.new(@configuration.cron_entries, start_on_initialize: true) if @configuration.enable_cron?

@_async_started = true
end

# Whether the async executors are running
# @return [Boolean]
def async_started?
@_async_started
end

private

# Whether running in a web server process.
Expand Down
1 change: 1 addition & 0 deletions lib/good_job/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ def start
poller.recipients << [scheduler, :create_thread]

cron_manager = GoodJob::CronManager.new(configuration.cron_entries, start_on_initialize: true) if configuration.enable_cron?

if configuration.probe_port
probe_server = GoodJob::ProbeServer.new(port: configuration.probe_port)
probe_server.start
Expand Down
34 changes: 16 additions & 18 deletions lib/good_job/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,24 +50,22 @@ def validate!
# for more details on possible values.
# @return [Symbol]
def execution_mode
@_execution_mode ||= begin
mode = if GoodJob::CLI.within_exe?
:external
else
options[:execution_mode] ||
rails_config[:execution_mode] ||
env['GOOD_JOB_EXECUTION_MODE']
end

if mode
mode.to_sym
elsif Rails.env.development?
:async
elsif Rails.env.test?
:inline
else
:external
end
mode = if GoodJob::CLI.within_exe?
:external
else
options[:execution_mode] ||
rails_config[:execution_mode] ||
env['GOOD_JOB_EXECUTION_MODE']
end

if mode
mode.to_sym
elsif Rails.env.development?
:async
elsif Rails.env.test?
:inline
else
:external
end
end

Expand Down
3 changes: 1 addition & 2 deletions lib/good_job/railtie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ class Railtie < ::Rails::Railtie
end

config.after_initialize do
GoodJob::Scheduler.instances.each(&:warm_cache)
GoodJob::CronManager.instances.each(&:start)
GoodJob::Adapter.instances.each(&:start_async)
end
end
end

0 comments on commit 20a1889

Please sign in to comment.