Skip to content

Commit 256e517

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 2 times the cutoff_age_in_days.
1 parent b328c8f commit 256e517

File tree

2 files changed

+47
-0
lines changed

2 files changed

+47
-0
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 double the cutoff age
25+
double_cutoff_days = cutoff_age_in_days.to_i * 2
26+
orphaned_delayed_jobs = Delayed::Job.
27+
where(Sequel.lit("run_at < CURRENT_TIMESTAMP - INTERVAL '?' DAY", double_cutoff_days))
28+
29+
unless orphaned_delayed_jobs.count.zero?
30+
logger.info("Cleaning up #{orphaned_delayed_jobs.count} Delayed Jobs older than #{double_cutoff_days} 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: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,41 @@ 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 2 times the cut-off' do
79+
# Simulate a job that is orphaned but not older than 2 times the cut-off
80+
Sequel::Model.db[:delayed_jobs].where(id: @delayed_job.id).update(failed_at: nil, locked_by: nil)
81+
puts @delayed_job.inspect
82+
expect do
83+
cleanup_job.perform
84+
end.not_to(change { Delayed::Job.find(id: @delayed_job.id) })
85+
end
86+
end
87+
end
88+
89+
context 'when a job is orphaned and older than 2 times the cut-off' do
90+
let(:run_at) { Time.now.utc - 5.days }
91+
92+
it 'removes the job even if it is not failed and regardless of locked_by' do
93+
# Simulate a job that is not failed but still locked
94+
Sequel::Model.db[:delayed_jobs].where(id: @delayed_job.id).update(failed_at: nil, locked_by: 'some-worker', locked_at: Time.now.utc - 4.days)
95+
expect do
96+
cleanup_job.perform
97+
end.to change {
98+
Delayed::Job.find(id: @delayed_job.id)
99+
}.from(@delayed_job).to(nil)
100+
end
101+
102+
it 'removes the job even if it is not failed and locked_by is nil' do
103+
# Simulate a job that is not failed and not locked
104+
Sequel::Model.db[:delayed_jobs].where(id: @delayed_job.id).update(failed_at: nil, locked_by: nil)
105+
expect do
106+
cleanup_job.perform
107+
end.to change {
108+
Delayed::Job.find(id: @delayed_job.id)
109+
}.from(@delayed_job).to(nil)
110+
end
76111
end
77112

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

0 commit comments

Comments
 (0)