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
DEQUEUE_QUERY_SORTS =

Valid dequeue query sorts

[:created_at, :scheduled_at].freeze
LOCK_STRATEGIES =

Valid lock strategies

[:advisory, :skiplocked, :hybrid].freeze

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.



95
96
97
98
99
100
# File 'lib/good_job/configuration.rb', line 95

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)


64
65
66
# File 'lib/good_job/configuration.rb', line 64

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)


58
59
60
# File 'lib/good_job/configuration.rb', line 58

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)


69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/good_job/configuration.rb', line 69

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_dequeue_query_sort(dequeue_query_sort) ⇒ Object

Raises:

  • (ArgumentError)


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

def self.validate_dequeue_query_sort(dequeue_query_sort)
  raise ArgumentError, "GoodJob dequeue query sortmust be one of #{DEQUEUE_QUERY_SORTS.join(', ')}. It was '#{dequeue_query_sort}' which is not valid." unless dequeue_query_sort.in?(DEQUEUE_QUERY_SORTS)
end

.validate_execution_mode(execution_mode) ⇒ Object

Raises:

  • (ArgumentError)


47
48
49
# File 'lib/good_job/configuration.rb', line 47

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)


431
432
433
434
435
436
437
# File 'lib/good_job/configuration.rb', line 431

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)


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

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)


287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
# File 'lib/good_job/configuration.rb', line 287

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)


306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# File 'lib/good_job/configuration.rb', line 306

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)


275
276
277
278
279
280
281
282
# File 'lib/good_job/configuration.rb', line 275

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



221
222
223
224
225
226
227
228
229
# File 'lib/good_job/configuration.rb', line 221

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



231
232
233
# File 'lib/good_job/configuration.rb', line 231

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

#cron_graceful_restart_periodObject



235
236
237
238
239
# File 'lib/good_job/configuration.rb', line 235

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)


324
325
326
# File 'lib/good_job/configuration.rb', line 324

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

#dashboard_default_localeObject



367
368
369
# File 'lib/good_job/configuration.rb', line 367

def dashboard_default_locale
  rails_config[:dashboard_default_locale] || DEFAULT_DASHBOARD_DEFAULT_LOCALE
end

#dashboard_live_poll_enabledObject



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

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

#dequeue_query_sortObject



439
440
441
# File 'lib/good_job/configuration.rb', line 439

def dequeue_query_sort
  (options[:dequeue_query_sort] || rails_config[:dequeue_query_sort] || :created_at).to_sym
end

#enable_cronBoolean Also known as: enable_cron?

Whether to run cron

Returns:

  • (Boolean)


209
210
211
212
213
214
215
216
217
# File 'lib/good_job/configuration.rb', line 209

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



359
360
361
362
363
364
365
# File 'lib/good_job/configuration.rb', line 359

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)


388
389
390
391
392
393
394
# File 'lib/good_job/configuration.rb', line 388

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)


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

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)


110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/good_job/configuration.rb', line 110

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.



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

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)


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

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)


178
179
180
# File 'lib/good_job/configuration.rb', line 178

def inline_execution_respects_schedule?
  !!rails_config[:inline_execution_respects_schedule]
end

#lock_strategySymbol

Strategy for locking jobs during dequeue.

Returns:

  • (Symbol)


398
399
400
401
402
403
404
405
# File 'lib/good_job/configuration.rb', line 398

def lock_strategy
  (
    options[:lock_strategy] ||
      rails_config[:lock_strategy] ||
      env['GOOD_JOB_LOCK_STRATEGY'] ||
      :advisory
  )&.to_sym
end

#lower_thread_priorityObject



421
422
423
424
425
426
427
# File 'lib/good_job/configuration.rb', line 421

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)


186
187
188
189
190
191
192
193
# File 'lib/good_job/configuration.rb', line 186

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)


137
138
139
140
141
142
143
144
145
# File 'lib/good_job/configuration.rb', line 137

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)


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

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)


163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/good_job/configuration.rb', line 163

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)


355
356
357
# File 'lib/good_job/configuration.rb', line 355

def probe_app
  rails_config[:probe_app]
end

#probe_handlernil, Symbol

Probe server handler

Returns:

  • (nil, Symbol)


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

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)


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

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)


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

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)


152
153
154
155
156
157
# File 'lib/good_job/configuration.rb', line 152

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)


198
199
200
201
202
203
204
205
# File 'lib/good_job/configuration.rb', line 198

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

#validate!Object



102
103
104
105
# File 'lib/good_job/configuration.rb', line 102

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