Skip to content

Parallel Dispatch (pg_relay)

Optional parallel execution of a suite's checks through the pg_relay extension's queue and workers — see Parallel Execution via pg_relay in the User Guide for the narrative. The feature is spread across functions documented in their own chapters — p_use_pg_relay on pghf.create_check_run(), the enqueue behaviour of pghf.execute_check_run(), and the completion handling in pghf.evaluate_run() / pghf.run_and_evaluate() — leaving one function that lives here: the worker-side executor.

One-time setup (an operator with pg_relay management rights), registering the fixed channel the collection engine enqueues on:

SELECT pgrelay.register(
    p_channel          => 'pghf_check',
    p_action           => 'CALL pghf.execute_relay_check($1)',
    p_notes            => 'pg_health_framework parallel check execution',
    p_action_type      => 'sql',
    p_concurrency_mode => 'channel_payload');

'channel_payload' (pg_relay 1.1+) lets different checks run in parallel while serialising duplicate events for the same check of the same run — extra insurance on top of pghf.execute_relay_check()'s own duplicate guard. On pg_relay 1.0, register without that parameter.

pghf.execute_relay_check(p_payload text)

The worker-side executor — the action behind pg_relay channel pghf_check, invoked by a pg_relay worker once per enqueued check. Performs, for that one check, the identical invoke / "value"-key contract check / error-capture / result-logging sequence the collection engine's sequential path performs. Not normally called by hand — but it's an ordinary procedure, and calling it directly with a well-formed payload is harmless (and is exactly how the test suite exercises it without pg_relay).

Parameter Type Mandatory Default Description
p_payload text Yes The event payload the collection engine enqueued: {"run_id": "<uuid>", "check_uid": "<uuid>", "critical_only": false}. critical_only mirrors the enqueuing run's p_critical_only and is optional, defaulting false if absent. Raises on anything malformed, naming the payload. The run and check must both exist.

Returns: nothing. This is a procedure, and it deliberately never commits — it runs inside pg_relay's own worker transaction, so a failure rolls back atomically and lands in pg_relay's log as the audit record.

Behaviour worth knowing:

  • Idempotent per (run, check) — a duplicate delivery (a manual re-notify, a replayed event) finds the result row already present and silently does nothing, keeping evaluation's one-row-per-check assumption true.
  • Re-checks blackout windows at execution time — the collection engine already gates on blackouts at submission, but a window that opens between enqueue and worker dispatch still suppresses: the worker logs the standard SKIPPED row with the [blackout <id>] note instead of running the check.
  • Re-checks p_critical_only at execution time, the same way — a check enqueued while critical_only was true and it was 'critical' can, in principle, be confirmed back to non-critical by another run before a worker ever picks up the pending event; this re-check catches that and skips instead of running a now-stale critical-only request. See Scheduling Health Checks for why you'd use this at all.
  • Stamps the run's finish time — whichever worker logs the run's final expected result also sets finished_at, serialised via a row lock so concurrent finishers can't miss each other. This is what pghf.get_run_status()'s is_collection_complete reflects, and what pghf.evaluate_run() gates on.
  • Runs as the worker's own role — deliberately not SECURITY DEFINER (the same dynamic-dispatch reasoning as the collection engine; see The Access Model). The pg_relay worker role therefore needs operator-grade access: INSERT on pghf.raw_results and UPDATE on pghf.runs (open by default), explicit SELECT on pghf.runs/pghf.raw_results/pghf.run_config (not open by default), and whatever the check bodies themselves read (pg_monitor covers the built-ins).
  • Pins search_path to pg_catalog, public — pg_relay's dispatcher runs actions under its own internal path, which check bodies would otherwise inherit. Built-in checks are unaffected; a custom check reading objects in any other schema must schema-qualify them.

Continue to the Evaluation Engine.