Skip to content

Commit 9a9248f

Browse files
committed
Force delete very old delayed jobs
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 b328c8f commit 9a9248f

File tree

2 files changed

+45
-1
lines changed

2 files changed

+45
-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: 33 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,38 @@ 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+
puts @delayed_job.inspect
81+
expect do
82+
cleanup_job.perform
83+
end.not_to(change { Delayed::Job.find(id: @delayed_job.id) })
84+
end
85+
end
86+
end
87+
88+
context 'when a job is orphaned and older than the cut-off + 1 day' do
89+
let(:run_at) { Time.now.utc - 73.hours }
90+
91+
it 'removes the job even if it is not failed and regardless of locked_by' do
92+
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)
93+
expect do
94+
cleanup_job.perform
95+
end.to change {
96+
Delayed::Job.find(id: @delayed_job.id)
97+
}.from(@delayed_job).to(nil)
98+
end
99+
100+
it 'removes the job even if it is not failed and locked_by is nil' do
101+
Sequel::Model.db[:delayed_jobs].where(id: @delayed_job.id).update(failed_at: nil, locked_by: nil)
102+
expect do
103+
cleanup_job.perform
104+
end.to change {
105+
Delayed::Job.find(id: @delayed_job.id)
106+
}.from(@delayed_job).to(nil)
107+
end
76108
end
77109

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

0 commit comments

Comments
 (0)