Class: GoodJob::Configuration

Inherits:
Object
  • Object
show all
Defined in:
lib/good_job/configuration.rb

Overview

GoodJob::Configuration provides normalized configuration information to the rest of GoodJob. It combines environment information with explicitly set options to get the final values for each option.

Constant Summary collapse

EXECUTION_MODES =

Valid execution modes.

[:async, :async_all, :async_server, :external, :inline].freeze
DEFAULT_MAX_THREADS =

Default number of threads to use per Scheduler

5
DEFAULT_POLL_INTERVAL =

Default number of seconds between polls for jobs

10
DEFAULT_DEVELOPMENT_ASYNC_POLL_INTERVAL =

Default poll interval for async in development environment

-1
# Default number of threads to use per {Scheduler}
DEFAULT_MAX_CACHE =

Default number of threads to use per Scheduler

10_000
DEFAULT_CLEANUP_PRESERVED_JOBS_BEFORE_SECONDS_AGO =

Default number of seconds to preserve jobs for GoodJob::CLI#cleanup_preserved_jobs and GoodJob.cleanup_preserved_jobs

14.days.to_i
DEFAULT_CLEANUP_INTERVAL_JOBS =

Default number of jobs to execute between preserved job cleanup runs

1_000
DEFAULT_CLEANUP_INTERVAL_SECONDS =

Default number of seconds to wait between preserved job cleanup runs

10.minutes.to_i
DEFAULT_SHUTDOWN_TIMEOUT =

Default to always wait for jobs to finish for Adapter#shutdown

-1
# Default to not running cron
DEFAULT_ENABLE_CRON =

Default to not running cron

false
DEFAULT_ENABLE_LISTEN_NOTIFY =

Default to enabling LISTEN/NOTIFY

true
DEFAULT_DASHBOARD_DEFAULT_LOCALE =

Default Dashboard I18n locale

:en
DEFAULT_DASHBOARD_LIVE_POLL_ENABLED =

Default Dashboard Live Poll button enabled

true
DEFAULT_ENQUEUE_AFTER_TRANSACTION_COMMIT =

Default enqueue_after_transaction_commit

false
DEFAULT_ENABLE_PAUSES =

Default enable_pauses setting

false

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options, env: ENV) ⇒ Configuration

Returns a new instance of Configuration.

Parameters:

  • options (Hash)

    Any explicitly specified configuration options to use. Keys are symbols that match the various methods on this class.

  • env (Hash) (defaults to: ENV)

    A Hash from which to read environment variables that might specify additional configuration values.



87
88
89
90
91
92
# File 'lib/good_job/configuration.rb', line 87

def initialize(options, env: ENV)
  @options = options
  @env = env

  @_in_webserver = nil
end

Instance Attribute Details

#envHash (readonly)

The environment from which to read GoodJob’s environment variables. By default, this is the current process’s environment, but it can be set to something else in #initialize.

Returns:

  • (Hash)


56
57
58
# File 'lib/good_job/configuration.rb', line 56

def env
  @env
end

#optionsHash (readonly)

The options that were explicitly set when initializing Configuration. It is safe to modify this hash in place; be sure to symbolize keys.

Returns:

  • (Hash)


50
51
52
# File 'lib/good_job/configuration.rb', line 50

def options
  @options
end

Class Method Details

.total_estimated_threads(warn: false) ⇒ Integer

Returns the maximum number of threads GoodJob might consume

Parameters:

  • warn (Boolean) (defaults to: false)

    whether to print a warning when over the limit

Returns:

  • (Integer)


61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/good_job/configuration.rb', line 61

def self.total_estimated_threads(warn: false)
  utility_threads = GoodJob::SharedExecutor::MAX_THREADS
  scheduler_threads = GoodJob::Scheduler.instances.sum { |scheduler| scheduler.stats[:max_threads] }

  good_job_threads = utility_threads + scheduler_threads
  puma_threads = (Puma::Server.current&.max_threads if defined?(Puma::Server)) || 0

  total_threads = good_job_threads + puma_threads
  activerecord_pool_size = ActiveRecord::Base.connection_pool&.size

  if warn && activerecord_pool_size && total_threads > activerecord_pool_size
    message = "GoodJob is using #{good_job_threads} threads, " \
              "#{" and Puma is using #{puma_threads} threads, " if puma_threads.positive?}" \
              "which is #{total_threads - activerecord_pool_size} thread(s) more than ActiveRecord's database connection pool size of #{activerecord_pool_size}. " \
              "Consider increasing ActiveRecord's database connection pool size in config/database.yml."

    GoodJob.logger.warn message
  end

  good_job_threads
end

.validate_execution_mode(execution_mode) ⇒ Object

Raises:

  • (ArgumentError)


43
44
45
# File 'lib/good_job/configuration.rb', line 43

def self.validate_execution_mode(execution_mode)
  raise ArgumentError, "GoodJob execution mode must be one of #{EXECUTION_MODES.join(', ')}. It was '#{execution_mode}' which is not valid." unless execution_mode.in?(EXECUTION_MODES)
end

Instance Method Details

#advisory_lock_heartbeatBoolean

Whether to take an advisory lock on the process record in the notifier reactor.

Returns:

  • (Boolean)


411
412
413
414
415
416
417
# File 'lib/good_job/configuration.rb', line 411

def advisory_lock_heartbeat
  return options[:advisory_lock_heartbeat] unless options[:advisory_lock_heartbeat].nil?
  return rails_config[:advisory_lock_heartbeat] unless rails_config[:advisory_lock_heartbeat].nil?
  return ActiveModel::Type::Boolean.new.cast(env['GOOD_JOB_ADVISORY_LOCK_HEARTBEAT']) unless env['GOOD_JOB_ADVISORY_LOCK_HEARTBEAT'].nil?

  Rails.env.development?
end

#cleanup_discarded_jobs?Boolean

Whether to automatically destroy discarded jobs that have been preserved.

Returns:

  • (Boolean)


257
258
259
260
261
262
# File 'lib/good_job/configuration.rb', line 257

def cleanup_discarded_jobs?
  return rails_config[:cleanup_discarded_jobs] unless rails_config[:cleanup_discarded_jobs].nil?
  return ActiveModel::Type::Boolean.new.cast(env['GOOD_JOB_CLEANUP_DISCARDED_JOBS']) unless env['GOOD_JOB_CLEANUP_DISCARDED_JOBS'].nil?

  true
end

#cleanup_interval_jobsInteger, ...

Number of jobs a Scheduler will execute before automatically cleaning up preserved jobs. Positive values will clean up after that many jobs have run, false or 0 will disable, and -1 will clean up after every job.

Returns:

  • (Integer, Boolean, nil)


278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/good_job/configuration.rb', line 278

def cleanup_interval_jobs
  value = if rails_config.key?(:cleanup_interval_jobs)
            rails_config[:cleanup_interval_jobs]
          elsif env.key?('GOOD_JOB_CLEANUP_INTERVAL_JOBS')
            env['GOOD_JOB_CLEANUP_INTERVAL_JOBS']
          end

  if value.in? [nil, "", true]
    DEFAULT_CLEANUP_INTERVAL_JOBS
  elsif value.in? [0, "0", false, "false"]
    false
  else
    value ? value.to_i : false
  end
end

#cleanup_interval_secondsInteger, ...

Number of seconds a Scheduler will wait before automatically cleaning up preserved jobs. Positive values will clean up after that many jobs have run, false or 0 will disable, and -1 will clean up after every job.

Returns:

  • (Integer, Boolean, nil)


297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/good_job/configuration.rb', line 297

def cleanup_interval_seconds
  value = if rails_config.key?(:cleanup_interval_seconds)
            rails_config[:cleanup_interval_seconds]
          elsif env.key?('GOOD_JOB_CLEANUP_INTERVAL_SECONDS')
            env['GOOD_JOB_CLEANUP_INTERVAL_SECONDS']
          end

  if value.nil? || value == "" || value == true
    DEFAULT_CLEANUP_INTERVAL_SECONDS
  elsif value.in? [0, "0", false, "false"]
    false
  else
    value ? value.to_i : false
  end
end

#cleanup_preserved_jobs_before_seconds_agoInteger

Number of seconds to preserve jobs before automatic destruction.

Returns:

  • (Integer)


266
267
268
269
270
271
272
273
# File 'lib/good_job/configuration.rb', line 266

def cleanup_preserved_jobs_before_seconds_ago
  (
    options[:before_seconds_ago] ||
      rails_config[:cleanup_preserved_jobs_before_seconds_ago] ||
      env['GOOD_JOB_CLEANUP_PRESERVED_JOBS_BEFORE_SECONDS_AGO'] ||
      DEFAULT_CLEANUP_PRESERVED_JOBS_BEFORE_SECONDS_AGO
  ).to_i
end

#cronObject



212
213
214
215
216
217
218
219
220
# File 'lib/good_job/configuration.rb', line 212

def cron
  env_cron = JSON.parse(ENV.fetch('GOOD_JOB_CRON'), symbolize_names: true) if ENV['GOOD_JOB_CRON'].present?
  rails_config_cron = rails_config[:cron].presence

  options[:cron] ||
    rails_config_cron ||
    env_cron ||
    {}
end

#cron_entriesObject



222
223
224
# File 'lib/good_job/configuration.rb', line 222

def cron_entries
  cron.map { |cron_key, params| GoodJob::CronEntry.new(params.merge(key: cron_key)) }
end

#cron_graceful_restart_periodObject



226
227
228
229
230
# File 'lib/good_job/configuration.rb', line 226

def cron_graceful_restart_period
  options[:cron_graceful_restart_period] ||
    rails_config[:cron_graceful_restart_period] ||
    env['GOOD_JOB_CRON_GRACEFUL_RESTART_PERIOD']
end

#daemonize?Boolean

Tests whether to daemonize the process.

Returns:

  • (Boolean)


315
316
317
# File 'lib/good_job/configuration.rb', line 315

def daemonize?
  options[:daemonize] || false
end

#dashboard_default_localeObject



358
359
360
# File 'lib/good_job/configuration.rb', line 358

def dashboard_default_locale
  rails_config[:dashboard_default_locale] || DEFAULT_DASHBOARD_DEFAULT_LOCALE
end

#dashboard_live_poll_enabledObject



362
363
364
365
366
# File 'lib/good_job/configuration.rb', line 362

def dashboard_live_poll_enabled
  return rails_config[:dashboard_live_poll_enabled] unless rails_config[:dashboard_live_poll_enabled].nil?

  DEFAULT_DASHBOARD_LIVE_POLL_ENABLED
end

#enable_cronBoolean Also known as: enable_cron?

Whether to run cron

Returns:

  • (Boolean)


200
201
202
203
204
205
206
207
208
# File 'lib/good_job/configuration.rb', line 200

def enable_cron
  value = ActiveModel::Type::Boolean.new.cast(
    options[:enable_cron] ||
      rails_config[:enable_cron] ||
      env['GOOD_JOB_ENABLE_CRON'] ||
      false
  )
  value && cron.size.positive?
end

#enable_listen_notifyObject



350
351
352
353
354
355
356
# File 'lib/good_job/configuration.rb', line 350

def enable_listen_notify
  return options[:enable_listen_notify] unless options[:enable_listen_notify].nil?
  return rails_config[:enable_listen_notify] unless rails_config[:enable_listen_notify].nil?
  return ActiveModel::Type::Boolean.new.cast(env['GOOD_JOB_ENABLE_LISTEN_NOTIFY']) unless env['GOOD_JOB_ENABLE_LISTEN_NOTIFY'].nil?

  DEFAULT_ENABLE_LISTEN_NOTIFY
end

#enable_pausesBoolean

Whether the job processing can be paused.

Returns:

  • (Boolean)


379
380
381
382
383
384
385
# File 'lib/good_job/configuration.rb', line 379

def enable_pauses
  return options[:enable_pauses] unless options[:enable_pauses].nil?
  return rails_config[:enable_pauses] unless rails_config[:enable_pauses].nil?
  return ActiveModel::Type::Boolean.new.cast(env['GOOD_JOB_ENABLE_PAUSES']) unless env['GOOD_JOB_ENABLE_PAUSE'].nil?

  DEFAULT_ENABLE_PAUSES
end

#enqueue_after_transaction_commitBoolean

Whether the Adapter should have Active Job enqueue jobs after the transaction has committed.

Returns:

  • (Boolean)


370
371
372
373
374
375
# File 'lib/good_job/configuration.rb', line 370

def enqueue_after_transaction_commit
  return options[:enqueue_after_transaction_commit] unless options[:enqueue_after_transaction_commit].nil?
  return rails_config[:enqueue_after_transaction_commit] unless rails_config[:enqueue_after_transaction_commit].nil?

  DEFAULT_ENQUEUE_AFTER_TRANSACTION_COMMIT
end

#execution_modeSymbol

Specifies how and where jobs should be executed. See Adapter#initialize for more details on possible values.

Returns:

  • (Symbol)


101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/good_job/configuration.rb', line 101

def execution_mode
  mode = options[:execution_mode] ||
         rails_config[:execution_mode] ||
         env['GOOD_JOB_EXECUTION_MODE']
  mode = mode.to_sym if mode

  if mode
    if GoodJob::CLI.within_exe? && [:async, :async_server].include?(mode)
      :external
    else
      mode
    end
  elsif GoodJob::CLI.within_exe?
    :external
  elsif Rails.env.development?
    :async
  elsif Rails.env.test?
    :inline
  else # rubocop:disable Lint/DuplicateBranch
    :external
  end
end

#idle_timeoutInteger?

The number of seconds that a good_job process will idle with out running a job before exiting

Returns:

  • (Integer, nil)

    Number of seconds or nil means do not idle out.



247
248
249
250
251
252
253
# File 'lib/good_job/configuration.rb', line 247

def idle_timeout
  (
    options[:idle_timeout] ||
      rails_config[:idle_timeout] ||
      env['GOOD_JOB_IDLE_TIMEOUT']
  )&.to_i || nil
end

#in_webserver?Boolean?

Whether running in a web server process.

Returns:

  • (Boolean, nil)


389
390
391
392
393
394
395
396
397
398
399
# File 'lib/good_job/configuration.rb', line 389

def in_webserver?
  return @_in_webserver unless @_in_webserver.nil?

  @_in_webserver = Rails.const_defined?(:Server) || begin
    self_caller = caller
    self_caller.grep(%r{config.ru}).any? || # EXAMPLE: config.ru:3:in `block in <main>' OR config.ru:3:in `new_from_string'
      self_caller.grep(%r{puma/request}).any? || # EXAMPLE: puma-5.6.4/lib/puma/request.rb:76:in `handle_request'
      self_caller.grep(%{/rack/handler/}).any? || # EXAMPLE: iodine-0.7.44/lib/rack/handler/iodine.rb:13:in `start'
      (Concurrent.on_jruby? && self_caller.grep(%r{jruby/rack/rails_booter}).any?) # EXAMPLE: uri:classloader:/jruby/rack/rails_booter.rb:83:in `load_environment'
  end || false
end

#inline_execution_respects_schedule?Boolean

Returns:

  • (Boolean)


169
170
171
# File 'lib/good_job/configuration.rb', line 169

def inline_execution_respects_schedule?
  !!rails_config[:inline_execution_respects_schedule]
end

#lower_thread_priorityObject



401
402
403
404
405
406
407
# File 'lib/good_job/configuration.rb', line 401

def lower_thread_priority
  return options[:lower_thread_priority] unless options[:lower_thread_priority].nil?
  return rails_config[:lower_thread_priority] unless rails_config[:lower_thread_priority].nil?
  return ActiveModel::Type::Boolean.new.cast(env['GOOD_JOB_LOWER_THREAD_PRIORITY']) unless env['GOOD_JOB_LOWER_THREAD_PRIORITY'].nil?

  nil
end

#max_cacheInteger

The maximum number of future-scheduled jobs to store in memory. Storing future-scheduled jobs in memory reduces execution latency at the cost of increased memory usage. 10,000 stored jobs = ~20MB.

Returns:

  • (Integer)


177
178
179
180
181
182
183
184
# File 'lib/good_job/configuration.rb', line 177

def max_cache
  (
    options[:max_cache] ||
      rails_config[:max_cache] ||
      env['GOOD_JOB_MAX_CACHE'] ||
      DEFAULT_MAX_CACHE
  ).to_i
end

#max_threadsInteger

Indicates the number of threads to use per Scheduler. Note that #queue_string may provide more specific thread counts to use with individual schedulers.

Returns:

  • (Integer)


128
129
130
131
132
133
134
135
136
# File 'lib/good_job/configuration.rb', line 128

def max_threads
  (
    options[:max_threads] ||
      rails_config[:max_threads] ||
      env['GOOD_JOB_MAX_THREADS'] ||
      env['RAILS_MAX_THREADS'] ||
      DEFAULT_MAX_THREADS
  ).to_i
end

#pidfilePathname, String

Path of the pidfile to create when running as a daemon.

Returns:

  • (Pathname, String)


321
322
323
324
325
# File 'lib/good_job/configuration.rb', line 321

def pidfile
  options[:pidfile] ||
    env['GOOD_JOB_PIDFILE'] ||
    Rails.application.root.join('tmp', 'pids', 'good_job.pid')
end

#poll_intervalInteger

The number of seconds between polls for jobs. GoodJob will execute jobs on queues continuously until a queue is empty, at which point it will poll (using this interval) for new queued jobs to execute.

Returns:

  • (Integer)


154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/good_job/configuration.rb', line 154

def poll_interval
  interval =
    options[:poll_interval] ||
    rails_config[:poll_interval] ||
    env['GOOD_JOB_POLL_INTERVAL']

  if interval
    interval.to_i
  elsif Rails.env.development? && execution_mode.in?([:async, :async_all, :async_server])
    DEFAULT_DEVELOPMENT_ASYNC_POLL_INTERVAL
  else
    DEFAULT_POLL_INTERVAL
  end
end

#probe_appnil, Class

Rack compliant application to be run on the ProbeServer

Returns:

  • (nil, Class)


346
347
348
# File 'lib/good_job/configuration.rb', line 346

def probe_app
  rails_config[:probe_app]
end

#probe_handlernil, Symbol

Probe server handler

Returns:

  • (nil, Symbol)


337
338
339
340
341
342
# File 'lib/good_job/configuration.rb', line 337

def probe_handler
  (options[:probe_handler] ||
    rails_config[:probe_handler] ||
    env['GOOD_JOB_PROBE_HANDLER']
  )&.to_sym
end

#probe_portnil, Integer

Port of the probe server

Returns:

  • (nil, Integer)


329
330
331
332
333
# File 'lib/good_job/configuration.rb', line 329

def probe_port
  (options[:probe_port] ||
    env['GOOD_JOB_PROBE_PORT']
  )&.to_i
end

#queue_select_limitInteger?

The number of queued jobs to select when polling for a job to run. This limit is intended to avoid locking a large number of rows when selecting eligible jobs from the queue. This value should be higher than the total number of threads across all good_job processes to ensure a thread can retrieve an eligible and unlocked job.

Returns:

  • (Integer, nil)


237
238
239
240
241
242
243
# File 'lib/good_job/configuration.rb', line 237

def queue_select_limit
  (
    options[:queue_select_limit] ||
      rails_config[:queue_select_limit] ||
      env['GOOD_JOB_QUEUE_SELECT_LIMIT']
  )&.to_i
end

#queue_stringString

Describes which queues to execute jobs from and how those queues should be grouped into Scheduler instances. See README for more details on the format of this string.

Returns:

  • (String)


143
144
145
146
147
148
# File 'lib/good_job/configuration.rb', line 143

def queue_string
  options[:queues].presence ||
    rails_config[:queues].presence ||
    env['GOOD_JOB_QUEUES'].presence ||
    '*'
end

#shutdown_timeoutFloat

The number of seconds to wait for jobs to finish when shutting down before stopping the thread. -1 is forever.

Returns:

  • (Float)


189
190
191
192
193
194
195
196
# File 'lib/good_job/configuration.rb', line 189

def shutdown_timeout
  (
    options[:shutdown_timeout] ||
      rails_config[:shutdown_timeout] ||
      env['GOOD_JOB_SHUTDOWN_TIMEOUT'] ||
      DEFAULT_SHUTDOWN_TIMEOUT
  ).to_f
end

#validate!Object



94
95
96
# File 'lib/good_job/configuration.rb', line 94

def validate!
  self.class.validate_execution_mode(execution_mode)
end