class Minicron::Monitor

Used to monitor the executions in the database and look for any failures or missed executions based on the schedules minicron knows about

Public Class Methods

new() click to toggle source
# File lib/minicron/monitor.rb, line 13
def initialize
  @active = false
end

Public Instance Methods

running?() click to toggle source

Is the execution monitor running?

# File lib/minicron/monitor.rb, line 90
def running?
  @active
end
setup_db() click to toggle source

Establishes a database connection

# File lib/minicron/monitor.rb, line 18
def setup_db
  case Minicron.config['database']['type']
  when /mysql|postgres/
    # Establish a database connection
    ActiveRecord::Base.establish_connection(
      :adapter => Minicron.config['database']['type'],
      :host => Minicron.config['database']['host'],
      :database => Minicron.config['database']['database'],
      :username => Minicron.config['database']['username'],
      :password => Minicron.config['database']['password']
    )
  when 'sqlite'
    # Calculate the realtive path to the db because sqlite or activerecord is
    # weird and doesn't seem to handle abs paths correctly
    root = Pathname.new(Dir.pwd)
    db = Pathname.new(Minicron::HUB_PATH + '/db')
    db_rel_path = db.relative_path_from(root)

   ActiveRecord::Base.establish_connection(
      :adapter => 'sqlite3',
      :database => "#{db_rel_path}/minicron.sqlite3" # TODO: Allow configuring this but default to this value
    )
  else
    fail Exception, "The database #{Minicron.config['database']['type']} is not supported"
  end

  # Enable ActiveRecord logging if in verbose mode
  ActiveRecord::Base.logger = Minicron.config['verbose'] ? Logger.new(STDOUT) : nil
end
start!() click to toggle source

Starts the execution monitor in a new thread

# File lib/minicron/monitor.rb, line 49
def start!
  # Activate the monitor
  @active = true

  # Establish a database connection
  setup_db

  # Set the start time of the monitir
  @start_time = Time.now

  # Start a thread for the monitor
  @thread = Thread.new do
    # While the monitor is active run it in a loop ~every minute
    while @active
      # Get all the schedules
      schedules = Minicron::Hub::Schedule.all

      # Loop every schedule we know about
      schedules.each do |schedule|
        begin
          monitor(schedule)
        rescue Exception => e
          if Minicron.config['trace']
            puts e.message
            puts e.backtrace
          end
        end
      end

      sleep 59
    end
  end
end
stop!() click to toggle source

Stops the execution monitor

# File lib/minicron/monitor.rb, line 84
def stop!
  @active = false
  @thread.join
end

Private Instance Methods

monitor(schedule) click to toggle source

Handle the monitoring of a cron schedule

@param schedule [Minicron::Hub::Schedule]

# File lib/minicron/monitor.rb, line 99
def monitor(schedule)
  # Get an instance of the alert class
  alert = Minicron::Alert.new

  # Parse the cron expression
  cron = CronParser.new(schedule.formatted)

  # Find the time the cron was last expected to run with a 30 second pre buffer
  # and a 30 second post buffer (in addition to the 60 already in place) incase
  # jobs run early/late to allow for clock sync differences between client/hub
  expected_at = cron.last(Time.now) - 30
  expected_by = expected_at + 30 + 60 + 30 # pre buffer + minute wait + post buffer

  # We only need to check jobs that are expected to under the monitor start time
  # and jobs that have passed their expected by time and the time the schedule
  # was last updated isn't before when it was expected, i.e we aren't checking for something
  # that should have happened earlier in the day.
  if expected_at > @start_time && Time.now > expected_by && expected_by > schedule.updated_at
    # Check if this execution was created inside a minute window
    # starting when it was expected to run
    check = Minicron::Hub::Execution.exists?(
      :created_at => expected_at..expected_by,
      :job_id => schedule.job_id
    )

    # If the check failed
    unless check
      alert.send_all(
        :kind => 'miss',
        :schedule_id => schedule.id,
        :expected_at => expected_at,
        :job_id => schedule.job_id,
        :expected_at => expected_at
      )
    end
  end
end