Class: Postburner::OrphanedJob
- Inherits:
-
Job
- Object
- ActiveRecord::Base
- ApplicationRecord
- Job
- Postburner::OrphanedJob
- 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.
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
-
#destroy ⇒ self
Hard-deletes this orphaned row, removing BOTH its Beanstalkd job and its AR row, while keeping
readonly?true for saves/updates. -
#missing_class_name ⇒ String
Returns the original class name stored in the
typecolumn — the deleted or renamed class that this placeholder stands in for. -
#orphaned? ⇒ Boolean
Always true; identifies this instance as an orphaned placeholder.
-
#perform ⇒ Object
Raises Job::OrphanedJobError because the original job class no longer exists and there is no implementation to run.
-
#queue! ⇒ Object
(also: #requeue!)
Raises Job::OrphanedJobError because re-enqueuing a job whose class is gone would only produce another unperformable entry.
-
#readonly? ⇒ Boolean
Prevents saves that would allow Rails' ensure_proper_type to overwrite the original
typevalue with 'Postburner::OrphanedJob'. -
#remove! ⇒ void
Soft-deletes this orphaned row by setting
removed_at, bypassing thereadonly?guard via a relation-levelupdate_allcall (which does not go through instance save/update and therefore does not triggerensure_proper_typeeither).
Methods inherited from Job
#bk, #bk!, #bk=, #destroy!, find_sti_class
Methods included from Statistics
#elapsed_ms, #intended_at, #stats
Methods included from Execution
Methods included from Insertion
Methods included from Commands
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
#destroy ⇒ self
Hard-deletes this orphaned row, removing BOTH its Beanstalkd job and its AR
row, while keeping readonly? true for saves/updates.
The base ActiveRecord#destroy raises ReadOnlyRecord on a readonly record,
which would make ScheduleExecution#teardown_job! (which calls job.destroy
uniformly across job shapes) fail for an execution whose job class was
deleted/renamed. This override mirrors #remove!: it removes the Beanstalkd
job via Commands#delete! and deletes the row at the relation level — using the STI
base class so the missing type value doesn't scope the row out — bypassing
both readonly? and instance callbacks. The row is genuinely removed (not a
soft-delete), matching destroy semantics.
118 119 120 121 122 123 |
# File 'app/models/postburner/orphaned_job.rb', line 118 def destroy self.delete! self.class.base_class.where(id: self.id).delete_all @destroyed = true freeze end |
#missing_class_name ⇒ String
Returns the original class name stored in the type column — the deleted
or renamed class that this placeholder stands in for.
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.
59 |
# File 'app/models/postburner/orphaned_job.rb', line 59 def orphaned? = true |
#perform ⇒ Object
Raises Job::OrphanedJobError because the original job class no longer exists and there is no implementation to run.
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.
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'.
130 |
# File 'app/models/postburner/orphaned_job.rb', line 130 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).
Uses the STI base class for the relation so the row's missing type value
(which differs from OrphanedJob's sti_name) does not scope it out — a
self.class.where would match zero rows and silently fail to persist.
Idempotent: does nothing if already removed.
95 96 97 98 99 100 101 102 |
# File 'app/models/postburner/orphaned_job.rb', line 95 def remove! return if self.removed_at self.delete! now = Time.current self.class.base_class.where(id: self.id).update_all(removed_at: now) self.removed_at = now end |