ergon_health (ergon v0.5.0)

View Source

Liveness and diagnostics, in one call.

check/0 returns everything a /health endpoint needs:

  • db, whether the pool can serve a trivial query.
  • extensions, installed PostgreSQL extensions and their versions. Worth surfacing because Ergon's behaviour genuinely depends on them: without pg_cron no notification tick runs and every wake path falls back to polling.
  • jobs, per-queue counts from ergon.jobs.
  • pgmq, per-queue metrics for whichever pgmq queues were asked about.
{ergon, [{ergon_health, [{pgmq_queues, [~"events", ~"receipts"]}]}]}

pgmq queues have to be named because pgmq has no notion of which queues belong to this application, and reporting on every queue in the database would include other applications'. Job queues need no such list: they are rows in ergon.jobs, so the query finds them.

Reading the job counts

runnable is what checkout would take next, and uses the same predicate as jobs_fetch_idx. blocked is the one to watch: those jobs are available but waiting on a workflow parent, so they are invisible to checkout, and a queue full of them looks idle while holding work. Since a parent that ended failed or discarded never completes, its children stay blocked until an operator intervenes with ergon:cancel/1. Nothing else surfaces that.

A persistently high executing with no throughput means consumers died holding jobs, which is what ergon_reconciler is for.

Summary

Functions

The health snapshot, using the configured pgmq queue list.

Like check/0, overriding the pgmq queue list for this call with #{pgmq_queues => [...]}.

The pgmq queues configured for reporting.

Types

db_error()

-type db_error() ::
          empty_result | would_create_cycle |
          {job_not_found, ergon_job:job_id()} |
          {pgo_error, map()} |
          term().

health()

-type health() ::
          #{db := ok | {error, db_error()},
            extensions := #{binary() => binary()},
            jobs := #{binary() => job_metrics()},
            pgmq := #{binary() => pgmq_metrics() | {error, db_error()}}}.

job_metrics()

-type job_metrics() ::
          #{runnable := non_neg_integer(),
            blocked := non_neg_integer(),
            scheduled := non_neg_integer(),
            executing := non_neg_integer(),
            failed := non_neg_integer(),
            discarded := non_neg_integer()}.

pg_null()

-type pg_null() :: null.

pgmq_metrics()

-type pgmq_metrics() ::
          #{queue_length := non_neg_integer(),
            queue_visible_length := non_neg_integer(),
            oldest_msg_age_sec := number() | pg_null()}.

Functions

check()

-spec check() -> health().

The health snapshot, using the configured pgmq queue list.

check(Opts)

-spec check(#{pgmq_queues => [binary()]}) -> health().

Like check/0, overriding the pgmq queue list for this call with #{pgmq_queues => [...]}.

Never raises. Every section reports its own failure, because a health check that crashes when the database is down is answering the wrong question.

pgmq_queues()

-spec pgmq_queues() -> [binary()].

The pgmq queues configured for reporting.