# `QueueKeeperCheck`
[🔗](https://github.com/TitaniumMonkey/Queue-keeper-check/blob/v0.2.1/lib/queue_keeper_check.ex#L1)

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` - jobs `executing` far 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
  via `cron_missing: [expected: %{"MyApp.Nightly" => 90_000}]`

Options: any check name with a keyword list of thresholds, plus

* `:prefix` - schema of `oban_jobs` (default `"public"` on Postgres, none on SQLite/MySQL)
* `:now` - evaluation time (default `DateTime.utc_now/0`), handy in tests
* `:only` - list of checks to run (default all)
* `:statement_timeout`, `:lock_timeout`, `:read_only` - safety limits, see
  `QueueKeeperCheck.Config` and `QueueKeeperCheck.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.

# `checks`

All check names, in the order they run.

# `format`

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`

```elixir
@spec run(module(), keyword()) :: QueueKeeperCheck.Report.t()
```

Run the checks against `repo` and return a `QueueKeeperCheck.Report`.

# `slack_payload`

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.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
