All Jobs have a built in interface for executing code before and after a
#perform method. This is a general purpose strategy for tasks like
preemption, monitoring, rate limiting, failure notifications, etc.
A before hook is one strategy to rate limit jobs which might have negative consequences for running too frequently.
When a Job run needs to be delayed consider using #retry_later or #fail.
class NotifyUserJob < Mosquito::QueuedJob
  param user_id : Int32
  before do
    # prevent spamming a user with notifications
    fail if (Time.utc - user.last_notification_sent) < 2.minutes
  end
  def perform
    user.last_notification_sent = Time.utc
    user.notify!
  end
  def user : User
    found_user = UserService.fetch user_id
    fail unless found_user
    found_user
  end
end
The mosquito built in Rate Limiting module makes use of a before hook to halt and reschedule a job run.
An after hook doesn’t have the ability to prevent a job from running. It gets run regardless of job success, even if a job throws an exception, or is never run due to a before hook.
class MonitoredJob < Mosquito::QueuedJob
  def perform
    System.long_running_failure_prone_task
  end
  after do
    System.send_admin_email("the long running task failed again") unless succeeded?
  end
end
If a job fails implicitly by throwing an exception it is stored in the #exception variable and can be inspected in an after hook:
class FailingJob < Mosquito::QueuedJob
  def perform
    raise IndexError.new("Index out of bounds")
  end
  after do
    if failed && thrown_exception = exception
      System.send_admin_email("Job failed with an exception. #{thrown_exception.message}")
    end
  end
end