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

Default smaller_number_is_higher_priority

true

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.



85
86
87
88
89
90
# File 'lib/good_job/configuration.rb', line 85

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)


54
55
56
# File 'lib/good_job/configuration.rb', line 54

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)


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

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)


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

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)


41
42
43
# File 'lib/good_job/configuration.rb', line 41

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

#cleanup_discarded_jobs?Boolean

Whether to automatically destroy discarded jobs that have been preserved.

Returns:

  • (Boolean)


250
251
252
253
254
255
# File 'lib/good_job/configuration.rb', line 250

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)


271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
# File 'lib/good_job/configuration.rb', line 271

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)


290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/good_job/configuration.rb', line 290

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)


259
260
261
262
263
264
265
266
# File 'lib/good_job/configuration.rb', line 259

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



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

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



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

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

#daemonize?Boolean

Tests whether to daemonize the process.

Returns:

  • (Boolean)


308
309
310
# File 'lib/good_job/configuration.rb', line 308

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

#dashboard_default_localeObject



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

def dashboard_default_locale
  rails_config[:dashboard_default_locale] || DEFAULT_DASHBOARD_DEFAULT_LOCALE
end

#dashboard_live_poll_enabledObject



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

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)


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

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



343
344
345
346
347
348
349
# File 'lib/good_job/configuration.rb', line 343

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)


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

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)


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

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.



240
241
242
243
244
245
246
# File 'lib/good_job/configuration.rb', line 240

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)


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

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)


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

def inline_execution_respects_schedule?
  !!rails_config[:inline_execution_respects_schedule]
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)


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

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)


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

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)


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

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)


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

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)


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

def probe_app
  rails_config[:probe_app]
end

#probe_handlernil, Symbol

Probe server handler

Returns:

  • (nil, Symbol)


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

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)


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

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)


230
231
232
233
234
235
236
# File 'lib/good_job/configuration.rb', line 230

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)


141
142
143
144
145
146
# File 'lib/good_job/configuration.rb', line 141

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)


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

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

#smaller_number_is_higher_priorityObject



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

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

  DEFAULT_SMALLER_NUMBER_IS_HIGHER_PRIORITY
end

#validate!Object



92
93
94
# File 'lib/good_job/configuration.rb', line 92

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