zammad/zammad

View on GitHub
lib/monitoring_helper/health_checker/delayed_job.rb

Summary

Maintainability
A
0 mins
Test Coverage
# 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