Skip to content

Putting It All Together

The steps in the earlier chapters build and add one check in isolation. Here's the whole cycle end to end — namespace, category, check, threshold, a suite mixed with built-in checks, then actually running and evaluating it. Every command and its output below was run for real against a fresh install.

1. Catalog

Register a namespace, a category, the check's function, the check itself, and a threshold:

SELECT pghf.upsert_namespace('ACME', 'Acme Corp Custom Checks');
SELECT pghf.upsert_category('ACME', '01', 'Business Metrics');

CREATE OR REPLACE FUNCTION pghf.chk_acme01_001(p_run_id uuid) RETURNS pghf.check_result
LANGUAGE sql AS $$
    SELECT ROW('COLLECTED',
                jsonb_build_object('value', (SELECT count(*) FROM information_schema.tables WHERE table_schema = 'public')),
                NULL)::pghf.check_result;
$$;

SELECT pghf.create_check(
    p_namespace_code    => 'ACME',
    p_category_code     => '01',
    p_run_number        => 1,
    p_check_name        => 'public schema table count',
    p_routine           => 'pghf.chk_acme01_001(uuid)',
    p_result_type       => 'integer',
    p_rationale         => 'a runaway table count in public can indicate a migration or ORM leaking temp tables',
    p_numeric_direction => 'lower_is_better'
);
-- create_check
-- --------------
--  ACME01-001

SELECT pghf.set_threshold('ACME01-001', p_warn_value => '50'::jsonb, p_crit_value => '200'::jsonb);

(Swap the check body for whatever actually matters to you — a query against your own tables, a call out to an external API, anything an ordinary PostgreSQL function can do. The table-count query here is just a stand-in that runs on any install with no setup, and "ORM" refers to a code library that maps application objects to database tables — see the Glossary.)

2. Suite

Mix the new check with the built-in connection/availability checks:

SELECT pghf.create_check_run('nightly', 'nightly health + business checks');
SELECT * FROM pghf.add_checks_matching('nightly', 'PGHF01-*', false);   -- bulk-add the 6 built-ins
SELECT pghf.add_check_to_run('nightly', 'ACME01-001');                  -- append the custom one
 position |  check_id  | namespace_code |               check_name
----------+------------+-----------------+----------------------------------------
        1 | PGHF01-001 | PGHF            | SSL/TLS enabled
        2 | PGHF01-002 | PGHF            | PostgreSQL version
        3 | PGHF01-003 | PGHF            | Connection saturation
        4 | PGHF01-004 | PGHF            | Oldest idle-in-transaction session age
        5 | PGHF01-005 | PGHF            | Per-database connection counts
        6 | PGHF01-006 | PGHF            | pg_hba TRUST on non-loopback
        7 | ACME01-001 | ACME            | public schema table count

3. Run it

Collect, evaluate, then read the results (or use the combined convenience call to do the first two in one step — see Using the Seed Catalog in the User Guide):

CALL pghf.execute_check_run(p_run_key => 'nightly');
CALL pghf.evaluate_run(p_run_id => '<the run_id from pghf.runs>');
SELECT * FROM pghf.get_results_sql('<run_id>');
   severity    | category_id |  check_id  |               check_name               | value |                message
---------------+-------------+------------+-----------------------------------------+-------+-----------------------------------------
 ok            | ACME01      | ACME01-001 | public schema table count              | 0     |
 not_evaluable | PGHF01      | PGHF01-001 | SSL/TLS enabled                        | false | no threshold configured for this check
 not_evaluable | PGHF01      | PGHF01-002 | PostgreSQL version                     | 18    | no threshold configured for this check
 ok            | PGHF01      | PGHF01-003 | Connection saturation                  | 1.03  |
 ok            | PGHF01      | PGHF01-004 | Oldest idle-in-transaction session age | 0     |
 not_evaluable | PGHF01      | PGHF01-005 | Per-database connection counts         |       | no threshold configured for this check
 not_evaluable | PGHF01      | PGHF01-006 | pg_hba TRUST on non-loopback           | 0     | no threshold configured for this check

The custom check evaluates exactly like a built-in one — ok, because zero tables is comfortably under its warning threshold of 50 — because nothing in the evaluation engine distinguishes a built-in check from a custom one; both are simply rows in the same catalog tables. Every connection-and-availability check here genuinely collects real data too — none of them are permanent placeholders that only ever report themselves as skipped (see the Licence book).

Maintaining it afterwards

None of this needs another visit to the SQL above:

  • Inactivating a check (see Inactivating a Check) turns it off without touching its history.
  • Inactivating your own namespace or category (see Inactivating a Namespace or Category in the User Guide) blocks further edits without disturbing anything already running.
  • The framework's own seeding function never touches your custom namespace — it only ever writes to the built-in one — so resetting the built-in catalog (see Resetting to Defaults) leaves your own checks exactly as you left them.
  • The retention function trims old history when you decide you no longer need it (see Retention and Cleanup).

Continue to Passing In Extra Input.