Read-only health check for Oban queues.
report = QueueKeeperCheck.run(MyApp.Repo)
IO.puts(QueueKeeperCheck.format(report))Detects eight conditions (see checks/0 and QueueKeeperCheck.Config for thresholds):
:orphaned- jobsexecutingfar longer than any job should run (execution age only; possibly orphaned by a node crash):hanging- jobs executing past their per-worker time budget:backlog- too many due jobs, or the oldest due job waiting too long, per queue:stalled- due jobs waiting and nothing in that queue attempted recently (queue stopped, or every slot busy with long-running jobs):retry_storm- many jobs of one worker failed and awaiting retry, or retried, within a short window:discarded- spike of discarded jobs (silent failures):cancelled- spike of cancelled jobs:cron_missing- an expected (cron) worker has had no job inserted for too long; opt-in viacron_missing: [expected: %{"MyApp.Nightly" => 90_000}]
Options: any check name with a keyword list of thresholds, plus
:prefix- schema ofoban_jobs(default"public"on Postgres, none on SQLite/MySQL):now- evaluation time (defaultDateTime.utc_now/0), handy in tests:only- list of checks to run (default all):statement_timeout,:lock_timeout,:read_only- safety limits, seeQueueKeeperCheck.ConfigandQueueKeeperCheck.Guard
Invalid options raise ArgumentError. Database errors never raise: each check runs on its
own, and a check that fails (timeout, missing table or column, connection error) is
reported in report.errors with status :error (unless a finding is :critical).
After a connection error the remaining checks are skipped.
Summary
Functions
All check names, in the order they run.
Plain-text rendering, one line per finding or error. Queue and worker names come from
the database, so control and invisible formatting characters (including newlines and
bidirectional overrides) are replaced with ?: a crafted name cannot forge extra lines
or reorder text in a terminal or log.
Run the checks against repo and return a QueueKeeperCheck.Report.
Build (but do not send) a Slack incoming-webhook payload. Post it with your own HTTP
client, e.g. Req.post!(webhook_url, json: QueueKeeperCheck.slack_payload(report)).
&, < and > are escaped as Slack requires, so a queue or worker name cannot
inject <!channel> mentions or links.
Functions
All check names, in the order they run.
Plain-text rendering, one line per finding or error. Queue and worker names come from
the database, so control and invisible formatting characters (including newlines and
bidirectional overrides) are replaced with ?: a crafted name cannot forge extra lines
or reorder text in a terminal or log.
@spec run(module(), keyword()) :: QueueKeeperCheck.Report.t()
Run the checks against repo and return a QueueKeeperCheck.Report.
Build (but do not send) a Slack incoming-webhook payload. Post it with your own HTTP
client, e.g. Req.post!(webhook_url, json: QueueKeeperCheck.slack_payload(report)).
&, < and > are escaped as Slack requires, so a queue or worker name cannot
inject <!channel> mentions or links.