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

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.



83
84
85
86
87
88
# File 'lib/good_job/configuration.rb', line 83

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)


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

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)


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

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)


57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/good_job/configuration.rb', line 57

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)


39
40
41
# File 'lib/good_job/configuration.rb', line 39

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)


396
397
398
399
400
401
402
# File 'lib/good_job/configuration.rb', line 396

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)


253
254
255
256
257
258
# File 'lib/good_job/configuration.rb', line 253

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)


274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
# File 'lib/good_job/configuration.rb', line 274

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)


293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
# File 'lib/good_job/configuration.rb', line 293

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)


262
263
264
265
266
267
268
269
# File 'lib/good_job/configuration.rb', line 262

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



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

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



218
219
220
# File 'lib/good_job/configuration.rb', line 218

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

#cron_graceful_restart_periodObject



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

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)


311
312
313
# File 'lib/good_job/configuration.rb', line 311

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

#dashboard_default_localeObject



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

def dashboard_default_locale
  rails_config[:dashboard_default_locale] || DEFAULT_DASHBOARD_DEFAULT_LOCALE
end

#dashboard_live_poll_enabledObject



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

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)


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

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



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

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

#enqueue_after_transaction_commitBoolean

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

Returns:

  • (Boolean)


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

def enqueue_after_transaction_commit
  return options[:enqueue_after_transaction_commit] unless options[: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)


97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/good_job/configuration.rb', line 97

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.



243
244
245
246
247
248
249
# File 'lib/good_job/configuration.rb', line 243

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)


374
375
376
377
378
379
380
381
382
383
384
# File 'lib/good_job/configuration.rb', line 374

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)


165
166
167
# File 'lib/good_job/configuration.rb', line 165

def inline_execution_respects_schedule?
  !!rails_config[:inline_execution_respects_schedule]
end

#lower_thread_priorityObject



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

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)


173
174
175
176
177
178
179
180
# File 'lib/good_job/configuration.rb', line 173

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)


124
125
126
127
128
129
130
131
132
# File 'lib/good_job/configuration.rb', line 124

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)


317
318
319
320
321
# File 'lib/good_job/configuration.rb', line 317

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)


150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/good_job/configuration.rb', line 150

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)


342
343
344
# File 'lib/good_job/configuration.rb', line 342

def probe_app
  rails_config[:probe_app]
end

#probe_handlernil, Symbol

Probe server handler

Returns:

  • (nil, Symbol)


333
334
335
336
337
338
# File 'lib/good_job/configuration.rb', line 333

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)


325
326
327
328
329
# File 'lib/good_job/configuration.rb', line 325

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)


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

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)


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

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)


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

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

#validate!Object



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

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