Class: MiniSchedulerLongRunningJobLogger

Inherits:
Object
  • Object
show all
Defined in:
lib/mini_scheduler_long_running_job_logger.rb

Constant Summary collapse

DEFAULT_POLL_INTERVAL_SECONDS =
6

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(poll_interval_seconds: nil) ⇒ MiniSchedulerLongRunningJobLogger

Returns a new instance of MiniSchedulerLongRunningJobLogger.



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# File 'lib/mini_scheduler_long_running_job_logger.rb', line 8

def initialize(poll_interval_seconds: nil)
  @mutex = Mutex.new
  @stop_requested = false

  @poll_interval_seconds =
    if poll_interval_seconds
      begin
        Integer(poll_interval_seconds)
      rescue ArgumentError
        DEFAULT_POLL_INTERVAL_SECONDS
      end
    else
      DEFAULT_POLL_INTERVAL_SECONDS
    end
end

Instance Attribute Details

#threadObject (readonly)

Returns the value of attribute thread.



6
7
8
# File 'lib/mini_scheduler_long_running_job_logger.rb', line 6

def thread
  @thread
end

Instance Method Details

#startObject



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/mini_scheduler_long_running_job_logger.rb', line 24

def start
  @thread ||=
    Thread.new do
      hostname = Discourse.os_hostname

      loop do
        break if self.stop_requested?

        current_long_running_jobs = Set.new

        begin
          MiniScheduler::Manager.discover_running_scheduled_jobs.each do |job|
            job_class = job[:class]
            job_started_at = job[:started_at]
            mini_scheduler_worker_thread_id = job[:thread_id]

            job_frequency_minutes =
              if job_class.daily
                1.day.in_minutes.minutes
              else
                job_class.every.in_minutes.minutes
              end

            warning_duration =
              begin
                if job_frequency_minutes < 30.minutes
                  30.minutes
                elsif job_frequency_minutes < 2.hours
                  job_frequency_minutes
                else
                  2.hours
                end
              end

            next if job_started_at >= (Time.zone.now - warning_duration)

            running_thread =
              Thread.list.find do |thread|
                thread[:mini_scheduler_worker_thread_id] == mini_scheduler_worker_thread_id
              end

            next if running_thread.nil?

            current_long_running_jobs << job_class

            next if @seen_long_running_jobs&.include?(job_class)

            Rails.logger.warn(<<~MSG)
                Sidekiq scheduled job `#{job_class}` has been running for more than #{warning_duration.in_minutes.to_i} minutes
                #{running_thread.backtrace.join("\n")}
                MSG
          end

          @seen_long_running_jobs = current_long_running_jobs

          yield if block_given?
        rescue => error
          Discourse.warn_exception(
            error,
            message: "Unexpected error in MiniSchedulerLongRunningJobLogger thread",
          )
        end

        sleep @poll_interval_seconds
      end
    end
end

#stopObject

Used for testing to stop the thread. In production, the thread is expected to live for the lifetime of the process.



93
94
95
96
97
98
99
100
101
# File 'lib/mini_scheduler_long_running_job_logger.rb', line 93

def stop
  @mutex.synchronize { @stop_requested = true }

  if @thread
    @thread.wakeup
    @thread.join
    @thread = nil
  end
end