Class: Postburner::OrphanedJob

Inherits:
Job show all
Defined in:
app/models/postburner/orphaned_job.rb

Overview

Inert STI placeholder loaded when a Postburner::Job row’s ‘type` column references a class that no longer exists in the application.

## Why readonly?

Rails’ ‘ensure_proper_type` (called via `save`/`update`) would overwrite the original `type` value with `’Postburner::OrphanedJob’‘, permanently destroying the record of which class was missing. `readonly?` returning true prevents all `save`/`update` paths and therefore blocks `ensure_proper_type` from running.

Rails’ ‘instantiate` reads the `type` column verbatim when loading records from the database and does NOT call `ensure_proper_type`, so the original class name is preserved on load. This class is only ever instantiated via the `find_sti_class` fallback in Job, never created directly.

## Why remove! is overridden here

In Rails 8.1+, ‘update_column` respects `readonly?` and raises `ActiveRecord::ReadOnlyRecord`. The base `Commands#remove!` calls `update_column(:removed_at, …)` — so it would be blocked too. This subclass overrides `remove!` to use `self.class.where(id: self.id).update_all(…)` which operates at the relation level (bypasses both `readonly?` and all instance callbacks) while still soft-deleting the row. Admins can therefore safely soft-remove orphaned rows without restoring the missing class.

## Usage

This class is loaded automatically by Job.find_sti_class when a row’s type is unresolvable. You should never instantiate it directly in application code.

Examples:

Typical lifecycle

# A DB row exists with type: 'Flex::TriggerNoShowDeliveriesJob' (class deleted)
job = Postburner::Job.find(42)
job.class         # => Postburner::OrphanedJob
job.type          # => 'Flex::TriggerNoShowDeliveriesJob'
job.orphaned?     # => true
job.missing_class_name  # => 'Flex::TriggerNoShowDeliveriesJob'
job.perform             # => raises Postburner::Job::OrphanedJobError
job.remove!             # soft-deletes fine despite readonly?

Instance Attribute Summary

Attributes inherited from Job

#args, #attempt_count, #attempting_at, #attempts, #bkid, #duration, #errata, #error_count, #id, #lag, #log_count, #logs, #processed_at, #processing_at, #queued_at, #removed_at, #run_at, #sid, #type

Instance Method Summary collapse

Methods inherited from Job

#bk, #bk!, #bk=, #destroy, #destroy!, find_sti_class

Methods included from Statistics

#elapsed_ms, #intended_at, #stats

Methods included from Execution

#perform!

Methods included from Insertion

#will_insert?

Methods included from Commands

#delete!, #extend!, #kick!

Methods included from Logging

#log, #log!, #log_exception, #log_exception!

Methods included from Properties

#priority, #queue_name, #retry_delay_for_attempt, #should_retry?, #ttr, #tube_name

Instance Method Details

#missing_class_nameString

Returns the original class name stored in the ‘type` column — the deleted or renamed class that this placeholder stands in for.

Returns:

  • (String)


51
52
53
# File 'app/models/postburner/orphaned_job.rb', line 51

def missing_class_name
  self.type.to_s
end

#orphaned?Boolean

Always true; identifies this instance as an orphaned placeholder.

Returns:

  • (Boolean)


59
# File 'app/models/postburner/orphaned_job.rb', line 59

def orphaned? = true

#performObject

Raises Job::OrphanedJobError because the original job class no longer exists and there is no implementation to run.

Raises:



66
67
68
69
# File 'app/models/postburner/orphaned_job.rb', line 66

def perform(*)
  raise Postburner::Job::OrphanedJobError,
    "Cannot perform orphaned job ##{self.id}: class '#{self.missing_class_name}' no longer exists"
end

#queue!Object Also known as: requeue!

Raises Job::OrphanedJobError because re-enqueuing a job whose class is gone would only produce another unperformable entry.

Raises:



76
77
78
79
# File 'app/models/postburner/orphaned_job.rb', line 76

def queue!(*)
  raise Postburner::Job::OrphanedJobError,
    "Cannot enqueue orphaned job ##{self.id}: class '#{self.missing_class_name}' no longer exists"
end

#readonly?Boolean

Prevents saves that would allow Rails’ ensure_proper_type to overwrite the original ‘type` value with ’Postburner::OrphanedJob’.

Returns:

  • (Boolean)

    always true



105
# File 'app/models/postburner/orphaned_job.rb', line 105

def readonly? = true

#remove!void

This method returns an undefined value.

Soft-deletes this orphaned row by setting ‘removed_at`, bypassing the `readonly?` guard via a relation-level `update_all` call (which does not go through instance save/update and therefore does not trigger `ensure_proper_type` either).

Idempotent: does nothing if already removed.



91
92
93
94
95
96
97
98
# File 'app/models/postburner/orphaned_job.rb', line 91

def remove!
  return if self.removed_at

  self.delete!
  now = Time.current
  self.class.where(id: self.id).update_all(removed_at: now)
  self.removed_at = now
end