require 'delayed_job' module JobsAutoscaling class Monitor IDLE = :idle BUSY = :busy def initialize(action: ) @action = action end def activate! Delayed::Worker.lifecycle.after(:work_queue_pop, &method(:work_queue_pop)) load_preexisting_jobs change_state(preexisting_jobs_running? ? BUSY : IDLE) end protected def load_preexisting_jobs @process_mtime_map = {} Delayed::Job.processes_locked_locally.each do |pid| mtime = Delayed::Worker::ProcessHelper.mtime(pid) @process_mtime_map[pid] = mtime if mtime end end def preexisting_jobs_running? @process_mtime_map.each do |pid, mtime| unless Delayed::Worker::ProcessHelper.process_is_still_running?(pid, mtime) @process_mtime_map.delete(pid) end end @process_mtime_map.any? end def work_queue_pop(work_queue, _worker_config) unless work_queue.respond_to?(:all_workers_idle?) return end new_state = work_queue.all_workers_idle? && !preexisting_jobs_running? ? IDLE : BUSY if new_state != @worker_state change_state(new_state) end end def change_state(new_state) @worker_state = new_state @action.public_send(new_state) end end end