Module: GoodJob

Includes:
ActiveSupport::Deprecation::DeprecatedConstantAccessor, Dependencies, ThreadStatus
Defined in:
lib/good_job.rb,
lib/good_job/cli.rb,
lib/good_job/bulk.rb,
lib/good_job/daemon.rb,
lib/good_job/engine.rb,
lib/good_job/poller.rb,
lib/good_job/adapter.rb,
lib/good_job/capsule.rb,
lib/good_job/version.rb,
lib/good_job/callable.rb,
lib/good_job/notifier.rb,
lib/good_job/scheduler.rb,
lib/good_job/sd_notify.rb,
app/models/good_job/job.rb,
app/models/good_job/batch.rb,
lib/good_job/cron_manager.rb,
lib/good_job/dependencies.rb,
lib/good_job/probe_server.rb,
lib/good_job/configuration.rb,
lib/good_job/job_performer.rb,
lib/good_job/thread_status.rb,
app/models/good_job/process.rb,
app/models/good_job/setting.rb,
lib/good_job/current_thread.rb,
lib/good_job/log_subscriber.rb,
lib/good_job/capsule_tracker.rb,
lib/good_job/cleanup_tracker.rb,
lib/good_job/interrupt_error.rb,
lib/good_job/multi_scheduler.rb,
lib/good_job/shared_executor.rb,
lib/good_job/systemd_service.rb,
app/models/good_job/execution.rb,
app/charts/good_job/base_chart.rb,
app/models/good_job/cron_entry.rb,
lib/good_job/interrupted_error.rb,
app/models/good_job/base_record.rb,
app/models/good_job/i18n_config.rb,
app/filters/good_job/base_filter.rb,
app/filters/good_job/jobs_filter.rb,
app/models/good_job/batch_record.rb,
app/models/good_job/job/lockable.rb,
app/helpers/good_job/icons_helper.rb,
lib/good_job/adapter/inline_buffer.rb,
lib/good_job/job_performer/metrics.rb,
app/filters/good_job/batches_filter.rb,
lib/good_job/overridable_connection.rb,
app/models/good_job/execution_result.rb,
app/models/good_job/discrete_execution.rb,
app/helpers/good_job/application_helper.rb,
app/models/concerns/good_job/filterable.rb,
app/models/concerns/good_job/reportable.rb,
lib/good_job/notifier/process_heartbeat.rb,
lib/good_job/probe_server/not_found_app.rb,
app/controllers/good_job/jobs_controller.rb,
lib/generators/good_job/update_generator.rb,
lib/good_job/probe_server/simple_handler.rb,
app/models/concerns/good_job/error_events.rb,
lib/generators/good_job/install_generator.rb,
lib/good_job/active_job_extensions/labels.rb,
lib/good_job/probe_server/webrick_handler.rb,
app/charts/good_job/performance_show_chart.rb,
app/controllers/good_job/pauses_controller.rb,
lib/good_job/active_job_extensions/batches.rb,
app/charts/good_job/performance_index_chart.rb,
app/controllers/good_job/batches_controller.rb,
app/controllers/good_job/cleaner_controller.rb,
app/controllers/good_job/metrics_controller.rb,
app/charts/good_job/scheduled_by_queue_chart.rb,
app/controllers/good_job/frontends_controller.rb,
app/controllers/good_job/processes_controller.rb,
app/models/concerns/good_job/advisory_lockable.rb,
app/models/good_job/active_record_parent_class.rb,
lib/good_job/active_job_extensions/concurrency.rb,
app/controllers/good_job/application_controller.rb,
app/controllers/good_job/performance_controller.rb,
app/controllers/good_job/cron_entries_controller.rb,
lib/good_job/probe_server/healthcheck_middleware.rb,
lib/good_job/active_job_extensions/notify_options.rb,
lib/good_job/active_job_extensions/interrupt_errors.rb

Overview

:nodoc:

Defined Under Namespace

Modules: ActiveJobExtensions, AdvisoryLockable, ApplicationHelper, Bulk, Callable, CurrentThread, Dependencies, ErrorEvents, Filterable, IconsHelper, OverridableConnection, Reportable, SdNotify, ThreadStatus Classes: Adapter, ApplicationController, BaseChart, BaseFilter, BaseRecord, Batch, BatchRecord, BatchesController, BatchesFilter, CLI, Capsule, CapsuleTracker, CleanerController, CleanupTracker, Configuration, CronEntriesController, CronEntry, CronManager, Daemon, DiscreteExecution, Engine, Execution, ExecutionResult, FrontendsController, I18nConfig, InstallGenerator, InterruptError, InterruptedError, Job, JobPerformer, JobsController, JobsFilter, LogSubscriber, MetricsController, MultiScheduler, Notifier, PausesController, PerformanceController, PerformanceIndexChart, PerformanceShowChart, Poller, ProbeServer, Process, ProcessesController, ScheduledByQueueChart, Scheduler, Setting, SharedExecutor, SystemdService, UpdateGenerator

Constant Summary collapse

NONE =

Default, null, blank value placeholder.

Module.new.freeze
DEFAULT_LOGGER =

Default logger for GoodJob; overridden by Rails.logger in Railtie.

ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new($stdout))
VERSION =

GoodJob gem version.

'4.18.0'
GEM_VERSION =

GoodJob version as Gem::Version object

Gem::Version.new(VERSION)
ActiveRecordParentClass =
if GoodJob.active_record_parent_class
  Object.const_get(GoodJob.active_record_parent_class)
else
  ActiveRecord::Base
end

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.active_record_parent_classActiveRecord::Base

The ActiveRecord parent class inherited by GoodJob::Job (default: ActiveRecord::Base). Use this when using multiple databases or other custom ActiveRecord configuration.

Examples:

Change the base class:

GoodJob.active_record_parent_class = "CustomApplicationRecord"

Returns:

  • (ActiveRecord::Base)


68
# File 'lib/good_job.rb', line 68

mattr_accessor :active_record_parent_class, default: nil

.capsuleGoodJob::Capsule?

Global/default execution capsule for GoodJob.

Returns:



137
# File 'lib/good_job.rb', line 137

mattr_accessor :capsule, default: GoodJob::Capsule.new(configuration: configuration)

.configurationGoodJob::Configuration?

Global configuration object for GoodJob.

Returns:



131
# File 'lib/good_job.rb', line 131

mattr_accessor :configuration, default: GoodJob::Configuration.new({})

.handled_exceptionsArray<Class, String>

Exception classes that GoodJob will rescue during job execution (default: [StandardError]). Accepts class objects or strings that will be constantized at runtime. Exceptions not in this list will propagate and crash the executing thread.

Examples:

Also rescue ScriptError

GoodJob.handled_exceptions = [StandardError, NotImplementedError, ScriptError]

Returns:

  • (Array<Class, String>)


109
# File 'lib/good_job.rb', line 109

mattr_accessor :handled_exceptions, default: [StandardError, NotImplementedError]

.loggerLogger?

The logger used by GoodJob (default: Rails.logger). Use this to redirect logs to a special location or file.

Examples:

Output GoodJob logs to a file:

GoodJob.logger = ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new("log/my_logs.log"))

Returns:

  • (Logger, nil)


77
# File 'lib/good_job.rb', line 77

mattr_accessor :logger, default: DEFAULT_LOGGER

.on_thread_errorProc?

This callable will be called when an exception reaches GoodJob (default: nil). It can be useful for logging errors to bug tracking services, like Sentry or Airbrake.

Examples:

Send errors to Sentry

# config/initializers/good_job.rb
GoodJob.on_thread_error = -> (exception) { Raven.capture_exception(exception) }

Returns:

  • (Proc, nil)


125
# File 'lib/good_job.rb', line 125

mattr_accessor :on_thread_error, default: nil

.preserve_job_recordsBoolean, ...

Whether to preserve job records in the database after they have finished (default: true). If you want to preserve jobs for latter inspection, set this to true. If you want to preserve only jobs that finished with error for latter inspection, set this to :on_unhandled_error. If you want to preserve jobs based on the error event, set this to a lambda that takes the error_event argument. If you do not want to preserve jobs, set this to false. When using GoodJob’s cron functionality, job records will be preserved for a brief time to prevent duplicate jobs.

Examples:

Preserve only jobs that were discarded

GoodJob.preserve_job_records = ->(active_job, exception, error_event) { error_event == :discarded }

Returns:

  • (Boolean, Symbol, Proc, nil)


90
# File 'lib/good_job.rb', line 90

mattr_accessor :preserve_job_records, default: true

.retry_on_unhandled_errorBoolean?

Whether to re-perform a job when a handled (rescued) error is raised to GoodJob (default: false). If true, causes jobs to be re-queued and retried if they raise an instance of a handled exception. If false, jobs will be discarded or marked as finished if they raise an instance of a handled exception.

Returns:

  • (Boolean, nil)

See Also:



99
# File 'lib/good_job.rb', line 99

mattr_accessor :retry_on_unhandled_error, default: false

Class Method Details

._on_thread_error(exception) ⇒ void

This method returns an undefined value.

Called with exception when a GoodJob thread raises an exception

Parameters:

  • exception (Exception)

    Exception that was raised



142
143
144
# File 'lib/good_job.rb', line 142

def self._on_thread_error(exception)
  on_thread_error.call(exception) if on_thread_error.respond_to?(:call)
end

._shutdown_all(executables, method_name = :shutdown, timeout: -1,, after: []) ⇒ void

This method returns an undefined value.

Sends #shutdown or #restart to executable objects (Notifier, Poller, Scheduler, MultiScheduler, CronManager, SharedExecutor)

Parameters:



200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/good_job.rb', line 200

def self._shutdown_all(executables, method_name = :shutdown, timeout: -1, after: [])
  if timeout.is_a?(Numeric) && timeout.positive?
    executables.each { |executable| executable.send(method_name, timeout: nil) }

    stop_at = Time.current + timeout
    executables.each { |executable| executable.send(method_name, timeout: [stop_at - Time.current, 0].max) }
  else
    executables.each { |executable| executable.send(method_name, timeout: timeout) }
  end
  return unless after.any? && !timeout.nil?

  if stop_at
    after.each { |executable| executable.shutdown(timeout: [stop_at - Time.current, 0].max) }
  else
    after.each { |executable| executable.shutdown(timeout: timeout) }
  end
end

.cleanup_preserved_jobs(older_than: nil, in_batches_of: 1_000, include_discarded: GoodJob.configuration.cleanup_discarded_jobs?) ⇒ Integer

Destroys preserved job and batch records. By default, GoodJob destroys job records when the job is performed and this method is not necessary. However, when ‘GoodJob.preserve_job_records = true`, the jobs will be preserved in the database. This is useful when wanting to analyze or inspect job performance. If you are preserving job records this way, use this method regularly to destroy old records and preserve space in your database.

Parameters:

  • older_than (nil, Numeric, ActiveSupport::Duration) (defaults to: nil)

    Jobs older than this will be destroyed (default: 86400).

  • include_discarded (Boolean) (defaults to: GoodJob.configuration.cleanup_discarded_jobs?)

    Whether or not to destroy discarded jobs (default: per cleanup_discarded_jobs config option)

Returns:

  • (Integer)

    Number of job execution records and batches that were destroyed.



228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/good_job.rb', line 228

def self.cleanup_preserved_jobs(older_than: nil, in_batches_of: 1_000, include_discarded: GoodJob.configuration.cleanup_discarded_jobs?)
  older_than ||= GoodJob.configuration.cleanup_preserved_jobs_before_seconds_ago
  timestamp = Time.current - older_than

  ActiveSupport::Notifications.instrument("cleanup_preserved_jobs.good_job", { older_than: older_than, timestamp: timestamp }) do |payload|
    deleted_jobs_count = 0
    deleted_batches_count = 0
    deleted_executions_count = 0

    jobs_query = GoodJob::Job.finished_before(timestamp).order(finished_at: :asc).limit(in_batches_of)
    jobs_query = jobs_query.succeeded unless include_discarded
    loop do
      break if GoodJob.current_thread_shutting_down?

      active_job_ids = jobs_query.pluck(:active_job_id)
      break if active_job_ids.empty?

      deleted_executions = GoodJob::Execution.where(active_job_id: active_job_ids).delete_all
      deleted_executions_count += deleted_executions

      deleted_jobs = GoodJob::Job.where(active_job_id: active_job_ids).delete_all
      deleted_jobs_count += deleted_jobs
    end

    batches_query = GoodJob::BatchRecord.finished_before(timestamp).limit(in_batches_of)
    batches_query = batches_query.succeeded unless include_discarded
    loop do
      break if GoodJob.current_thread_shutting_down?

      deleted = batches_query.delete_all
      break if deleted.zero?

      deleted_batches_count += deleted
    end

    payload[:destroyed_batches_count] = deleted_batches_count
    payload[:destroyed_executions_count] = deleted_executions_count
    payload[:destroyed_jobs_count] = deleted_jobs_count

    destroyed_records_count = deleted_batches_count + deleted_executions_count + deleted_jobs_count
    payload[:destroyed_records_count] = destroyed_records_count

    destroyed_records_count
  end
end

.cli?Boolean

Whether this process was initialized via the GoodJob executable (‘$ good_job`)

Returns:

  • (Boolean)


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

def self.cli?
  GoodJob::CLI.within_exe?
end

.configure_active_record(&block) ⇒ void

This method returns an undefined value.

Custom Active Record configuration that is class_eval’ed into GoodJob::BaseRecord

Examples:

GoodJob.configure_active_record do
  connects_to database: :special_database
end

Parameters:

  • block

    Custom Active Record configuration



154
155
156
# File 'lib/good_job.rb', line 154

def self.configure_active_record(&block)
  self._active_record_configuration = block
end

.deprecatorActiveSupport::Deprecation

Deprecator for providing deprecation warnings.

Returns:

  • (ActiveSupport::Deprecation)


306
307
308
309
310
311
# File 'lib/good_job.rb', line 306

def self.deprecator
  @_deprecator ||= begin
    next_major_version = GEM_VERSION.segments[0] + 1
    ActiveSupport::Deprecation.new("#{next_major_version}.0", "GoodJob")
  end
end

.handled_exception_classesArray<Class>

Resolved exception classes from handled_exceptions.

Returns:

  • (Array<Class>)


113
114
115
# File 'lib/good_job.rb', line 113

def self.handled_exception_classes
  handled_exceptions.map { |e| e.is_a?(String) ? e.constantize : e }
end

.migrated?Boolean

Whether all GoodJob migrations have been applied. For use in tests/CI to validate GoodJob is up-to-date.

Returns:

  • (Boolean)


316
317
318
319
# File 'lib/good_job.rb', line 316

def self.migrated?
  GoodJob::Job.lock_type_migrated? &&
    GoodJob::Job.connection.index_name_exists?(:good_jobs, "index_good_jobs_on_queue_name_priority_scheduled_at_unfinished")
end

.pause(queue: nil, job_class: nil) ⇒ void

This method returns an undefined value.

Pause job execution for a given queue or job class.

Parameters:

  • queue (String, nil) (defaults to: nil)

    Queue name to pause

  • job_class (String, nil) (defaults to: nil)

    Job class name to pause



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

def self.pause(queue: nil, job_class: nil)
  GoodJob::Setting.pause(queue: queue, job_class: job_class)
end

.paused(type = nil) ⇒ Hash

Get a list of all paused queues and job classes

Returns:

  • (Hash)

    Hash with :queues, :job_classes, :labels arrays of paused items



349
350
351
# File 'lib/good_job.rb', line 349

def self.paused(type = nil)
  GoodJob::Setting.paused(type)
end

.paused?(queue: nil, job_class: nil, label: nil) ⇒ Boolean

Check if job execution is paused for a given queue or job class.

Parameters:

  • queue (String, nil) (defaults to: nil)

    Queue name to check

  • job_class (String, nil) (defaults to: nil)

    Job class name to check

  • label (String, nil) (defaults to: nil)

    Label to check

Returns:

  • (Boolean)


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

def self.paused?(queue: nil, job_class: nil, label: nil)
  GoodJob::Setting.paused?(queue: queue, job_class: job_class, label: label)
end

.perform_inline(queue_string = "*", limit: nil) ⇒ void

This method returns an undefined value.

Perform all queued jobs in the current thread. This is primarily intended for usage in a test environment. Unhandled job errors will be raised.

Parameters:

  • queue_string (String) (defaults to: "*")

    Queues to execute jobs from

  • limit (Integer, nil) (defaults to: nil)

    Maximum number of iterations for the loop



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

def self.perform_inline(queue_string = "*", limit: nil)
  job_performer = JobPerformer.new(queue_string)
  iteration = 0
  loop do
    break if limit && iteration >= limit

    result = Rails.application.executor.wrap { job_performer.next }
    break unless result
    raise result.unhandled_error if result.unhandled_error

    iteration += 1
  end
end

.restart(timeout: -1)) ⇒ void

This method returns an undefined value.

Stops and restarts executing jobs. GoodJob does its work in pools of background threads. When forking processes you should shut down these background threads before forking, and restart them after forking. For example, you should use shutdown and restart when using async execution mode with Puma. See the README for more explanation and examples.

Parameters:

  • timeout (Numeric) (defaults to: -1))

    Seconds to wait for active threads to finish.



188
189
190
191
192
# File 'lib/good_job.rb', line 188

def self.restart(timeout: -1)
  return if configuration.execution_mode != :async && configuration.in_webserver?

  _shutdown_all(Capsule.instances, :restart, timeout: timeout)
end

.shutdown(timeout: -1)) ⇒ void

This method returns an undefined value.

Stop executing jobs. GoodJob does its work in pools of background threads. When forking processes you should shut down these background threads before forking, and restart them after forking. For example, you should use shutdown and restart when using async execution mode with Puma. See the README for more explanation and examples.

Parameters:

  • timeout (nil, Numeric) (defaults to: -1))

    Seconds to wait for actively executing jobs to finish

    • nil, the scheduler will trigger a shutdown but not wait for it to complete.

    • -1, the scheduler will wait until the shutdown is complete.

    • 0, the scheduler will immediately shutdown and stop any active tasks.

    • 1.., the scheduler will wait that many seconds before stopping any remaining active tasks.



171
172
173
# File 'lib/good_job.rb', line 171

def self.shutdown(timeout: -1)
  _shutdown_all(Capsule.instances, timeout: timeout)
end

.shutdown?Boolean

Tests whether jobs have stopped executing.

Returns:

  • (Boolean)

    whether background threads are shut down



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

def self.shutdown?
  Capsule.instances.all?(&:shutdown?)
end

.unpause(queue: nil, job_class: nil, label: nil) ⇒ void

This method returns an undefined value.

Unpause job execution for a given queue or job class.

Parameters:

  • queue (String, nil) (defaults to: nil)

    Queue name to unpause

  • job_class (String, nil) (defaults to: nil)

    Job class name to unpause

  • label (String, nil) (defaults to: nil)

    Label to unpause



334
335
336
# File 'lib/good_job.rb', line 334

def self.unpause(queue: nil, job_class: nil, label: nil)
  GoodJob::Setting.unpause(queue: queue, job_class: job_class, label: label)
end

.v4_ready?Boolean

Tests whether GoodJob can be safely upgraded to v4

Returns:

  • (Boolean)


296
297
298
299
300
301
302
# File 'lib/good_job.rb', line 296

def self.v4_ready?
  GoodJob.deprecator.warn(<<~MSG)
    Calling `GoodJob.v4_ready?` is deprecated and will be removed in GoodJob v5.
    If you are reading this deprecation you are already on v4.
  MSG
  true
end