lib/monitoring_helper/health_checker/delayed_job.rb
# Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
module MonitoringHelper
class HealthChecker
class DelayedJob < Backend
FAILED_JOBS_THRESHOLD = 10
TOTAL_JOBS_THRESHOLD = 8_000
TOTAL_JOBS_TIMEOUT = 15.minutes
def run_health_check
failed_jobs
failed_with_attempts
total_jobs
end
private
def scope
Delayed::Job.where('attempts > 0')
end
def failed_jobs
count_failed_jobs = scope.count
return if count_failed_jobs <= FAILED_JOBS_THRESHOLD
response.issues.push "#{count_failed_jobs} failing background jobs"
end
def failed_with_attempts
scope
.reorder(:created_at)
.limit(10)
.each_with_object({}) { |elem, memo| map_single_failed_job(elem, memo) }
.sort
.each_with_index do |(job_name, job_data), index|
response.issues.push "Failed to run background job ##{index + 1} '#{job_name}' #{job_data[:count]} time(s) with #{job_data[:attempts]} attempt(s)."
end
end
def map_single_failed_job(job, hash)
job_name = job_name(job)
hash[job_name] ||= {
count: 0,
attempts: 0,
}
hash[job_name][:count] += 1
hash[job_name][:attempts] += job.attempts
end
def job_name(job)
if job.instance_of?(Delayed::Backend::ActiveRecord::Job) && job.payload_object.respond_to?(:job_data)
return job.payload_object.job_data['job_class']
end
job.name
end
def total_jobs
total_jobs = Delayed::Job.where(created_at: ...TOTAL_JOBS_TIMEOUT.ago).count
return if total_jobs <= TOTAL_JOBS_THRESHOLD
response.issues.push "#{total_jobs} background jobs in queue"
end
end
end
end