@@ -9,10 +9,30 @@ def initialize(cutoff_age_in_days)
99 end
1010
1111 def perform
12- old_pollable_jobs = PollableJobModel . where ( Sequel . lit ( "created_at < CURRENT_TIMESTAMP - INTERVAL '?' DAY" , cutoff_age_in_days ) )
1312 logger = Steno . logger ( 'cc.background.pollable-job-cleanup' )
13+ cutoff_condition = Sequel . lit ( "created_at < CURRENT_TIMESTAMP - INTERVAL '?' DAY" , cutoff_age_in_days )
14+
15+ old_pollable_jobs = PollableJobModel . where ( cutoff_condition )
1416 logger . info ( "Cleaning up #{ old_pollable_jobs . count } Jobs rows" )
1517 old_pollable_jobs . delete
18+
19+ # Job warnings have to be deleted explicitly, because
20+ # - we don't have a foreign key constraint and thus cannot use DELETE CASCADE, and
21+ # - we don't delete/destroy pollable job model objects one by one and thus cannot use hooks defined by the
22+ # 'destroy' association dependency between PollableJobModel and JobWarningModel.
23+ # Instead, we delete all expired jobs with a single SQL statement.
24+ #
25+ # By using the same cutoff condition based on 'created_at' for pollable jobs and job warnings, we ensure that
26+ # only job warnings are deleted where it is guaranteed that also the associated pollable job already has been
27+ # removed. This is due to the fact that job warnings are created after their associated pollable job, i.e.
28+ # pollable_job.created_at <= job_warning.created_at.
29+ #
30+ # On the other hand, it is not guaranteed that all associated job warnings are deleted for all pollable jobs
31+ # that have been removed during an execution of this cleanup job. But these leftovers will simply be removed
32+ # during the next job execution.
33+ old_job_warnings = JobWarningModel . where ( cutoff_condition )
34+ logger . info ( "Cleaning up #{ old_job_warnings . count } Job Warnings rows" )
35+ old_job_warnings . delete
1636 end
1737
1838 def job_name_in_configuration
0 commit comments