Skip to content

Commit 0dd3a61

Browse files
authored
Force delete very old delayed jobs (#4395)
We found some old jobs in the delayed_jobs table, which are beyond the cutoff_age_in_days interval. They had either no failed_at set or were still locked. With this change the FailedJobsCleanup job will force delete all jobs, which are older than the cutoff_age_in_days + 1 day.
1 parent f8ba687 commit 0dd3a61

File tree

2 files changed

+44
-1
lines changed

2 files changed

+44
-1
lines changed

app/jobs/runtime/failed_jobs_cleanup.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,18 @@ def perform
2020

2121
old_delayed_jobs.delete
2222

23+
# There were some very old jobs in the table which did not get cleaned up
24+
# This deletes those orphaned jobs, which were scheduled to run before the cutoff age + 1 day
25+
force_delete_after = cutoff_age_in_days.to_i + 1
26+
orphaned_delayed_jobs = Delayed::Job.
27+
where(Sequel.lit("run_at < CURRENT_TIMESTAMP - INTERVAL '?' DAY", force_delete_after))
28+
29+
unless orphaned_delayed_jobs.count.zero?
30+
logger.info("Deleting #{orphaned_delayed_jobs.count} orphaned Delayed Jobs older than #{force_delete_after} days")
31+
32+
orphaned_delayed_jobs.delete
33+
end
34+
2335
return if max_number_of_failed_delayed_jobs.nil?
2436

2537
ids_exceeding_limit = Delayed::Job.

spec/unit/jobs/runtime/failed_jobs_cleanup_spec.rb

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def max_attempts
6464
end
6565

6666
context 'when older than specified cut-off' do
67-
let(:run_at) { Time.now.utc - 3.days }
67+
let(:run_at) { Time.now.utc - 50.hours }
6868

6969
it 'removes the job' do
7070
expect do
@@ -73,6 +73,37 @@ def max_attempts
7373
Delayed::Job.find(id: @delayed_job.id)
7474
}.from(@delayed_job).to(nil)
7575
end
76+
77+
context 'when job is orphaned' do
78+
it 'does not remove the job if it is not older than cut-off + 1 day' do
79+
Sequel::Model.db[:delayed_jobs].where(id: @delayed_job.id).update(failed_at: nil, locked_by: nil)
80+
expect do
81+
cleanup_job.perform
82+
end.not_to(change { Delayed::Job.find(id: @delayed_job.id) })
83+
end
84+
end
85+
end
86+
87+
context 'when a job is orphaned and older than the cut-off + 1 day' do
88+
let(:run_at) { Time.now.utc - 73.hours }
89+
90+
it 'removes the job even if it is not failed and regardless of locked_by' do
91+
Sequel::Model.db[:delayed_jobs].where(id: @delayed_job.id).update(failed_at: nil, locked_by: 'some-worker', locked_at: Time.now.utc - 2.days)
92+
expect do
93+
cleanup_job.perform
94+
end.to change {
95+
Delayed::Job.find(id: @delayed_job.id)
96+
}.from(@delayed_job).to(nil)
97+
end
98+
99+
it 'removes the job even if it is not failed and locked_by is nil' do
100+
Sequel::Model.db[:delayed_jobs].where(id: @delayed_job.id).update(failed_at: nil, locked_by: nil)
101+
expect do
102+
cleanup_job.perform
103+
end.to change {
104+
Delayed::Job.find(id: @delayed_job.id)
105+
}.from(@delayed_job).to(nil)
106+
end
76107
end
77108

78109
context 'when the number of delayed jobs exceeds max_number_of_failed_delayed_jobs' do

0 commit comments

Comments
 (0)