diff --git a/changelogs/fragments/467.yml b/changelogs/fragments/467.yml new file mode 100644 index 00000000..03d628b8 --- /dev/null +++ b/changelogs/fragments/467.yml @@ -0,0 +1,9 @@ +--- +breaking_changes: + - sql_exporter - Dropped support for PostgreSQL 11 and 12 + +minor_changes: + - sql_exporter - Consolidated setup file from different PostgreSQL versions to a single common file + +bugfixes: + - sql_exporter - Removed the pg_settings_checksum metric since it was not working properly. Requires new version of pgmonitor-extension. diff --git a/hugo/content/exporter/_index.md b/hugo/content/exporter/_index.md index 8699336d..a4fa9e30 100644 --- a/hugo/content/exporter/_index.md +++ b/hugo/content/exporter/_index.md @@ -554,7 +554,7 @@ The files contained in this repository are assumed to be installed in the follow | pgMonitor Configuration File | System Location | |------------------------------|-----------------| -| postgres_exporter/common/pg##/setup.sql | /etc/postgres_exporter/##/setup.sql | +| postgres_exporter/common/setup.sql | /etc/postgres_exporter/##/setup.sql | | postgres_exporter/common/pg##/queries*.yml | /etc/postgres_exporter/##/queries*.yml | | postgres_exporter/common/queries*.yml | /etc/postgres_exporter/##/queries*.yml | | postgres_exporter/linux/crontab.txt | /etc/postgres_exporter/##/crontab.txt | diff --git a/postgres_exporter/common/pg11/queries_general.yml b/postgres_exporter/common/pg11/queries_general.yml deleted file mode 100644 index e2b97ebf..00000000 --- a/postgres_exporter/common/pg11/queries_general.yml +++ /dev/null @@ -1,50 +0,0 @@ -### -# -# Begin File: PG11 queries_general.yml -# -# Copyright © 2017-2025 Crunchy Data Solutions, Inc. All Rights Reserved. -# -### - -ccp_stat_bgwriter: - query: "SELECT checkpoints_timed, checkpoints_req, checkpoint_write_time, checkpoint_sync_time, buffers_checkpoint, buffers_clean, maxwritten_clean, buffers_backend, buffers_backend_fsync, buffers_alloc, stats_reset FROM pg_catalog.pg_stat_bgwriter" - metrics: - - checkpoints_timed: - usage: "GAUGE" - description: "Number of scheduled checkpoints that have been performed" - - checkpoints_req: - usage: "GAUGE" - description: "Number of requested checkpoints that have been performed" - - checkpoint_write_time: - usage: "GAUGE" - description: "Total amount of time that has been spent in the portion of checkpoint processing where files are written to disk, in milliseconds" - - checkpoint_sync_time: - usage: "GAUGE" - description: "Total amount of time that has been spent in the portion of checkpoint processing where files are synchronized to disk, in milliseconds" - - buffers_checkpoint: - usage: "GAUGE" - description: "Number of buffers written during checkpoints" - - buffers_clean: - usage: "GAUGE" - description: "Number of buffers written by the background writer" - - maxwritten_clean: - usage: "GAUGE" - description: "Number of times the background writer stopped a cleaning scan because it had written too many buffers" - - buffers_backend: - usage: "GAUGE" - description: "Number of buffers written directly by a backend" - - buffers_backend_fsync: - usage: "GAUGE" - description: "Number of times a backend had to execute its own fsync call (normally the background writer handles those even when the backend does its own write)" - - buffers_alloc: - usage: "GAUGE" - description: "Number of buffers allocated" - - stats_reset: - usage: "GAUGE" - description: "Time at which these statistics were last reset" - -### -# -# End File: PG11 queries_general.yml -# -### diff --git a/postgres_exporter/common/pg11/queries_pg_stat_statements.yml b/postgres_exporter/common/pg11/queries_pg_stat_statements.yml deleted file mode 100644 index 411dd445..00000000 --- a/postgres_exporter/common/pg11/queries_pg_stat_statements.yml +++ /dev/null @@ -1,135 +0,0 @@ -### -# -# Begin File: PG11 queries_pg_stat_statements.yml -# -# Copyright © 2017-2025 Crunchy Data Solutions, Inc. All Rights Reserved. -# -### - -ccp_pg_stat_statements_total: - query: "SELECT pg_get_userbyid(s.userid) as role, - d.datname AS dbname, - sum(s.calls) AS calls_count, - sum(s.total_time) AS exec_time_ms, - avg(s.mean_time) AS mean_exec_time_ms, - sum(s.rows) AS row_count - FROM public.pg_stat_statements s - JOIN pg_catalog.pg_database d - ON d.oid = s.dbid - GROUP BY 1,2" - metrics: - - role: - usage: "LABEL" - description: "Role that executed the statement" - - dbname: - usage: "LABEL" - description: "Database in which the statement was executed" - - calls_count: - usage: "GAUGE" - description: "Total number of queries run per user/database" - - exec_time_ms: - usage: "GAUGE" - description: "Total runtime of all queries per user/database" - - mean_exec_time_ms: - usage: "GAUGE" - description: "Mean runtime of all queries per user/database" - - row_count: - usage: "GAUGE" - description: "Total rows returned from all queries per user/database" - - -ccp_pg_stat_statements_top_mean: - query: "SELECT pg_get_userbyid(s.userid) as role, - d.datname AS dbname, - s.queryid, - btrim(replace(left(s.query, 40), '\n', '')) AS query, - max(s.mean_time) exec_time_ms - FROM public.pg_stat_statements s - JOIN pg_catalog.pg_database d - ON d.oid = s.dbid - GROUP BY 1,2,3,4 - ORDER BY 5 DESC - LIMIT #PG_STAT_STATEMENTS_LIMIT#" - metrics: - - role: - usage: "LABEL" - description: "Role that executed the statement" - - dbname: - usage: "LABEL" - description: "Database in which the statement was executed" - - queryid: - usage: "LABEL" - description: "Internal hash code, computed from the statement's parse tree" - - query: - usage: "LABEL" - description: "First 40 characters of query text" - - exec_time_ms: - usage: "GAUGE" - description: "Average query runtime in milliseconds" - - -# Note that individual query stats can only be reset in PG12 -ccp_pg_stat_statements_top_total: - query: "SELECT pg_get_userbyid(s.userid) as role, - d.datname AS dbname, - s.queryid, - btrim(replace(left(s.query, 40), '\n', '')) AS query, - s.total_time exec_time_ms - FROM public.pg_stat_statements s - JOIN pg_catalog.pg_database d - ON d.oid = s.dbid - ORDER BY 5 DESC - LIMIT #PG_STAT_STATEMENTS_LIMIT#" - metrics: - - role: - usage: "LABEL" - description: "Role that executed the statement" - - dbname: - usage: "LABEL" - description: "Database in which the statement was executed" - - queryid: - usage: "LABEL" - description: "Internal hash code, computed from the statement's parse tree" - - query: - usage: "LABEL" - description: "First 40 characters of query text" - - exec_time_ms: - usage: "GAUGE" - description: "Total time spent in the statement in milliseconds" - - -# Note that individual query stats can only be reset in PG12 -ccp_pg_stat_statements_top_max: - query: "SELECT pg_get_userbyid(s.userid) as role, - d.datname AS dbname, - s.queryid, - btrim(replace(left(s.query, 40), '\n', '')) AS query, - s.max_time AS exec_time_ms - FROM public.pg_stat_statements s - JOIN pg_catalog.pg_database d - ON d.oid = s.dbid - ORDER BY 5 DESC - LIMIT #PG_STAT_STATEMENTS_LIMIT#" - metrics: - - role: - usage: "LABEL" - description: "Role that executed the statement" - - dbname: - usage: "LABEL" - description: "Database in which the statement was executed" - - queryid: - usage: "LABEL" - description: "Internal hash code, computed from the statement's parse tree" - - query: - usage: "LABEL" - description: "First 40 characters of query text" - - exec_time_ms: - usage: "GAUGE" - description: "Maximum time spent in the statement in milliseconds" - - -### -# -# End File: PG11 queries_pg_stat_statements.yml -# -### diff --git a/postgres_exporter/common/pg11/setup.sql b/postgres_exporter/common/pg11/setup.sql deleted file mode 100644 index a1ffe357..00000000 --- a/postgres_exporter/common/pg11/setup.sql +++ /dev/null @@ -1,418 +0,0 @@ --- PG11 pgMonitor Setup --- --- Copyright © 2017-2025 Crunchy Data Solutions, Inc. All Rights Reserved. --- - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'ccp_monitoring') THEN - CREATE ROLE ccp_monitoring WITH LOGIN; - END IF; - - -- The pgmonitor role is required by the pgnodemx extension in PostgreSQL versions 9.5 and 9.6 - -- and should be removed when upgrading to PostgreSQL 10 and above. - IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'pgmonitor') THEN - DROP ROLE pgmonitor; - END IF; -END -$$; - -GRANT pg_monitor to ccp_monitoring; -GRANT pg_execute_server_program TO ccp_monitoring; - -ALTER ROLE ccp_monitoring SET lock_timeout TO '2min'; -ALTER ROLE ccp_monitoring SET jit TO 'off'; - -CREATE SCHEMA IF NOT EXISTS monitor AUTHORIZATION ccp_monitoring; - -DROP TABLE IF EXISTS monitor.pgbackrest_info CASCADE; -CREATE TABLE IF NOT EXISTS monitor.pgbackrest_info (config_file text NOT NULL, data jsonb NOT NULL, gather_timestamp timestamptz DEFAULT now() NOT NULL); --- Force more aggressive autovacuum to avoid table bloat over time -ALTER TABLE monitor.pgbackrest_info SET (autovacuum_analyze_scale_factor = 0, autovacuum_vacuum_scale_factor = 0, autovacuum_vacuum_threshold = 10, autovacuum_analyze_threshold = 10); - -DROP FUNCTION IF EXISTS monitor.pgbackrest_info(); -- old version from 2.3 -DROP FUNCTION IF EXISTS monitor.pgbackrest_info(int); -CREATE OR REPLACE FUNCTION monitor.pgbackrest_info(p_throttle_minutes int DEFAULT 10) RETURNS SETOF monitor.pgbackrest_info - LANGUAGE plpgsql - SET search_path TO pg_catalog, pg_temp -AS $function$ -DECLARE - -v_gather_timestamp timestamptz; -v_throttle interval; -v_system_identifier bigint; - -BEGIN --- Get pgBackRest info in JSON format - -v_throttle := make_interval(mins := p_throttle_minutes); - -SELECT COALESCE(max(gather_timestamp), '1970-01-01'::timestamptz) INTO v_gather_timestamp FROM monitor.pgbackrest_info; - -IF pg_catalog.pg_is_in_recovery() = 'f' THEN - IF ((CURRENT_TIMESTAMP - v_gather_timestamp) > v_throttle) THEN - - -- Ensure table is empty - DELETE FROM monitor.pgbackrest_info; - - SELECT system_identifier into v_system_identifier FROM pg_control_system(); - - -- Copy data into the table directory from the pgBackRest into command - EXECUTE format( $cmd$ COPY monitor.pgbackrest_info (config_file, data) FROM program '/usr/bin/pgbackrest-info.sh %s' WITH (format text,DELIMITER '|') $cmd$, v_system_identifier::text ); - - END IF; -END IF; - -RETURN QUERY SELECT * FROM monitor.pgbackrest_info; - -IF NOT FOUND THEN - RAISE EXCEPTION 'No backups being returned from pgbackrest info command'; -END IF; - -END -$function$; - - -DROP FUNCTION IF EXISTS monitor.sequence_status(); -CREATE FUNCTION monitor.sequence_status() RETURNS TABLE (sequence_name text, last_value bigint, slots numeric, used numeric, percent int, cycle boolean, numleft numeric, table_usage text) - LANGUAGE sql SECURITY DEFINER STABLE - SET search_path TO pg_catalog, pg_temp -AS $function$ - -/* - * Provide detailed status information of sequences in the current database - */ - -WITH default_value_sequences AS ( - -- Get sequences defined as default values with related table - -- Note this subquery can be locked/hung by DDL that affects tables with sequences. - -- Use monitor.sequence_exhaustion() to actually monitor for sequences running out - SELECT s.seqrelid, c.oid - FROM pg_catalog.pg_attribute a - JOIN pg_catalog.pg_attrdef ad on (ad.adrelid,ad.adnum) = (a.attrelid,a.attnum) - JOIN pg_catalog.pg_class c on a.attrelid = c.oid - JOIN pg_catalog.pg_sequence s ON s.seqrelid = regexp_replace(pg_get_expr(ad.adbin,ad.adrelid), $re$^nextval\('(.+?)'::regclass\)$$re$, $re$\1$re$)::regclass - WHERE (pg_get_expr(ad.adbin,ad.adrelid)) ~ '^nextval\(' -), dep_sequences AS ( - -- Get sequences set as dependencies with related tables (identities) - SELECT s.seqrelid, c.oid - FROM pg_catalog.pg_sequence s - JOIN pg_catalog.pg_depend d ON s.seqrelid = d.objid - JOIN pg_catalog.pg_class c ON d.refobjid = c.oid - UNION - SELECT seqrelid, oid FROM default_value_sequences -), all_sequences AS ( - -- Get any remaining sequences - SELECT s.seqrelid AS sequence_oid, ds.oid AS table_oid - FROM pg_catalog.pg_sequence s - LEFT JOIN dep_sequences ds ON s.seqrelid = ds.seqrelid -) -SELECT sequence_name - , last_value - , slots - , used - , ROUND(used/slots*100)::int AS percent - , cycle - , CASE WHEN slots < used THEN 0 ELSE slots - used END AS numleft - , table_usage -FROM ( - SELECT format('%I.%I',s.schemaname, s.sequencename)::text AS sequence_name - , COALESCE(s.last_value,s.min_value) AS last_value - , s.cycle - , CEIL((s.max_value-min_value::NUMERIC+1)/s.increment_by::NUMERIC) AS slots - , CEIL((COALESCE(s.last_value,s.min_value)-s.min_value::NUMERIC+1)/s.increment_by::NUMERIC) AS used - , string_agg(a.table_oid::regclass::text, ', ') AS table_usage - FROM pg_catalog.pg_sequences s - JOIN all_sequences a ON (format('%I.%I', s.schemaname, s.sequencename))::regclass = a.sequence_oid - GROUP BY 1,2,3,4,5 -) x -ORDER BY ROUND(used/slots*100) DESC - -$function$; - - -DROP FUNCTION IF EXISTS monitor.sequence_exhaustion(int); -CREATE FUNCTION monitor.sequence_exhaustion(p_percent integer DEFAULT 75, OUT count bigint) - LANGUAGE sql SECURITY DEFINER STABLE - SET search_path TO pg_catalog, pg_temp -AS $function$ - -/* - * Returns count of sequences that have used up the % value given via the p_percent parameter (default 75%) - */ - -SELECT count(*) AS count -FROM ( - SELECT CEIL((s.max_value-min_value::NUMERIC+1)/s.increment_by::NUMERIC) AS slots - , CEIL((COALESCE(s.last_value,s.min_value)-s.min_value::NUMERIC+1)/s.increment_by::NUMERIC) AS used - FROM pg_catalog.pg_sequences s -) x -WHERE (ROUND(used/slots*100)::int) > p_percent; - -$function$; - -/* - * Tables and functions for monitoring changes to pg_settings and pg_hba_file_rules system catalogs. - * Can't just do a raw check for the hash value since Prometheus only records numeric values for alerts - * Tables allow recording of existing settings so they can be referred back to to see what changed - * If either checksum function returns 0, then NO settings have changed - * If either checksum function returns 1, then something has changed since last known valid state - * For replicas, logging past settings is not possible to compare what may have changed - * For replicas, by default, it is expected that its settings will match the primary - * For replicas, if the pg_settings or pg_hba.conf are necessarily different from the primary, a known good hash of that replica's - settings can be sent as an argument to the relevant checksum function. Views are provided to easily obtain the hash values used by this monitoring tool. - * If any known hash parameters are passed to the checksum functions, note that it will override any past hash values stored in the log table when doing comparisons and completely re-evaluate the entire state. This is true even if done on a primary where the current state will then also be logged for comparison if it differs from the given hash. - */ - -DROP TABLE IF EXISTS monitor.pg_settings_checksum; -DROP TABLE IF EXISTS monitor.pg_hba_checksum; - -CREATE TABLE monitor.pg_settings_checksum ( - settings_hash_generated text NOT NULL - , settings_hash_known_provided text - , settings_string text NOT NULL - , created_at timestamptz DEFAULT now() NOT NULL - , valid smallint NOT NULL ); - -COMMENT ON COLUMN monitor.pg_settings_checksum.valid IS 'Set this column to zero if this group of settings is a valid change'; -CREATE INDEX ON monitor.pg_settings_checksum (created_at); - -CREATE TABLE monitor.pg_hba_checksum ( - hba_hash_generated text NOT NULL - , hba_hash_known_provided text - , hba_string text NOT NULL - , created_at timestamptz DEFAULT now() NOT NULL - , valid smallint NOT NULL ); - -COMMENT ON COLUMN monitor.pg_hba_checksum.valid IS 'Set this column to zero if this group of settings is a valid change'; -CREATE INDEX ON monitor.pg_hba_checksum (created_at); - - -DROP FUNCTION IF EXISTS monitor.pg_settings_checksum(text); -CREATE FUNCTION monitor.pg_settings_checksum(p_known_settings_hash text DEFAULT NULL) - RETURNS smallint - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO pg_catalog, pg_temp -AS $function$ -DECLARE - -v_is_in_recovery boolean; -v_settings_hash text; -v_settings_hash_old text; -v_settings_match smallint := 0; -v_settings_string text; -v_settings_string_old text; -v_valid smallint; - -BEGIN - -SELECT pg_is_in_recovery() INTO v_is_in_recovery; - -SELECT md5_hash - , settings_string -INTO v_settings_hash - , v_settings_string -FROM monitor.pg_settings_hash; - -SELECT settings_hash_generated, valid -INTO v_settings_hash_old, v_valid -FROM monitor.pg_settings_checksum -ORDER BY created_at DESC LIMIT 1; - -IF p_known_settings_hash IS NOT NULL THEN - v_settings_hash_old := p_known_settings_hash; - -- Do not base validity on the stored value if manual hash is given. - v_valid := 0; -END IF; - -IF (v_settings_hash_old IS NOT NULL) THEN - - IF (v_settings_hash != v_settings_hash_old) THEN - - v_valid := 1; - - IF v_is_in_recovery = false THEN - INSERT INTO monitor.pg_settings_checksum ( - settings_hash_generated - , settings_hash_known_provided - , settings_string - , valid) - VALUES ( - v_settings_hash - , p_known_settings_hash - , v_settings_string - , v_valid); - END IF; - END IF; - -ELSE - - v_valid := 0; - IF v_is_in_recovery = false THEN - INSERT INTO monitor.pg_settings_checksum ( - settings_hash_generated - , settings_hash_known_provided - , settings_string - , valid) - VALUES (v_settings_hash - , p_known_settings_hash - , v_settings_string - , v_valid); - END IF; - -END IF; - -RETURN v_valid; - -END -$function$; - - -DROP FUNCTION IF EXISTS monitor.pg_hba_checksum(text); -CREATE FUNCTION monitor.pg_hba_checksum(p_known_hba_hash text DEFAULT NULL) - RETURNS smallint - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO pg_catalog, pg_temp -AS $function$ -DECLARE - -v_hba_hash text; -v_hba_hash_old text; -v_hba_match smallint := 0; -v_hba_string text; -v_hba_string_old text; -v_is_in_recovery boolean; -v_valid smallint; - -BEGIN - -SELECT pg_is_in_recovery() INTO v_is_in_recovery; - -IF current_setting('server_version_num')::int >= 100000 THEN - - SELECT md5_hash - , hba_string - INTO v_hba_hash - , v_hba_string - FROM monitor.pg_hba_hash; - -ELSE - RAISE EXCEPTION 'pg_hba change monitoring unsupported in versions older than PostgreSQL 10'; -END IF; - -SELECT hba_hash_generated, valid -INTO v_hba_hash_old, v_valid -FROM monitor.pg_hba_checksum -ORDER BY created_at DESC LIMIT 1; - -IF p_known_hba_hash IS NOT NULL THEN - v_hba_hash_old := p_known_hba_hash; - -- Do not base validity on the stored value if manual hash is given. - v_valid := 0; -END IF; - -IF (v_hba_hash_old IS NOT NULL) THEN - - IF (v_hba_hash != v_hba_hash_old) THEN - - v_valid := 1; - - IF v_is_in_recovery = false THEN - INSERT INTO monitor.pg_hba_checksum ( - hba_hash_generated - , hba_hash_known_provided - , hba_string - , valid) - VALUES ( - v_hba_hash - , p_known_hba_hash - , v_hba_string - , v_valid); - END IF; - END IF; - -ELSE - - v_valid := 0; - IF v_is_in_recovery = false THEN - INSERT INTO monitor.pg_hba_checksum ( - hba_hash_generated - , hba_hash_known_provided - , hba_string - , valid) - VALUES (v_hba_hash - , p_known_hba_hash - , v_hba_string - , v_valid); - END IF; - -END IF; - -RETURN v_valid; - -END -$function$; - - -DROP FUNCTION IF EXISTS monitor.pg_settings_checksum_set_valid(); -/* - * This function provides quick, clear interface for resetting the checksum monitor to treat the currently detected configuration as valid after alerting on a change. Note that configuration history will be cleared. - */ -CREATE FUNCTION monitor.pg_settings_checksum_set_valid() RETURNS smallint - LANGUAGE sql -AS $function$ - -TRUNCATE monitor.pg_settings_checksum; - -SELECT monitor.pg_settings_checksum(); - -$function$; - - -DROP FUNCTION IF EXISTS monitor.pg_hba_checksum_set_valid(); -/* - * This function provides quick, clear interface for resetting the checksum monitor to treat the currently detected configuration as valid after alerting on a change. Note that configuration history will be cleared. - */ -CREATE FUNCTION monitor.pg_hba_checksum_set_valid() RETURNS smallint - LANGUAGE sql -AS $function$ - -TRUNCATE monitor.pg_hba_checksum; - -SELECT monitor.pg_hba_checksum(); - -$function$; - - -DROP VIEW IF EXISTS monitor.pg_settings_hash; -CREATE VIEW monitor.pg_settings_hash AS - WITH settings_ordered_list AS ( - SELECT name - , COALESCE(setting, '<>') AS setting - FROM pg_catalog.pg_settings - ORDER BY name, setting) - SELECT md5(string_agg(name||setting, ',')) AS md5_hash - , string_agg(name||setting, ',') AS settings_string - FROM settings_ordered_list; - - -DROP VIEW IF EXISTS monitor.pg_hba_hash; -CREATE VIEW monitor.pg_hba_hash AS - -- Order by line number so it's caught if no content is changed but the order of entries is changed - WITH hba_ordered_list AS ( - SELECT COALESCE(type, '<>') AS type - , array_to_string(COALESCE(database, ARRAY['<>']), ',') AS database - , array_to_string(COALESCE(user_name, ARRAY['<>']), ',') AS user_name - , COALESCE(address, '<>') AS address - , COALESCE(netmask, '<>') AS netmask - , COALESCE(auth_method, '<>') AS auth_method - , array_to_string(COALESCE(options, ARRAY['<>']), ',') AS options - FROM pg_catalog.pg_hba_file_rules - ORDER BY line_number) - SELECT md5(string_agg(type||database||user_name||address||netmask||auth_method||options, ',')) AS md5_hash - , string_agg(type||database||user_name||address||netmask||auth_method||options, ',') AS hba_string - FROM hba_ordered_list; - - -GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA monitor TO ccp_monitoring; -GRANT ALL ON ALL TABLES IN SCHEMA monitor TO ccp_monitoring; diff --git a/postgres_exporter/common/pg12/queries_general.yml b/postgres_exporter/common/pg12/queries_general.yml deleted file mode 100644 index ca7c3c1e..00000000 --- a/postgres_exporter/common/pg12/queries_general.yml +++ /dev/null @@ -1,67 +0,0 @@ -### -# -# Begin File: PG12 queries_general.yml -# -# Copyright © 2017-2025 Crunchy Data Solutions, Inc. All Rights Reserved. -# -### - -ccp_stat_bgwriter: - query: "SELECT checkpoints_timed, checkpoints_req, checkpoint_write_time, checkpoint_sync_time, buffers_checkpoint, buffers_clean, maxwritten_clean, buffers_backend, buffers_backend_fsync, buffers_alloc, stats_reset FROM pg_catalog.pg_stat_bgwriter" - metrics: - - checkpoints_timed: - usage: "GAUGE" - description: "Number of scheduled checkpoints that have been performed" - - checkpoints_req: - usage: "GAUGE" - description: "Number of requested checkpoints that have been performed" - - checkpoint_write_time: - usage: "GAUGE" - description: "Total amount of time that has been spent in the portion of checkpoint processing where files are written to disk, in milliseconds" - - checkpoint_sync_time: - usage: "GAUGE" - description: "Total amount of time that has been spent in the portion of checkpoint processing where files are synchronized to disk, in milliseconds" - - buffers_checkpoint: - usage: "GAUGE" - description: "Number of buffers written during checkpoints" - - buffers_clean: - usage: "GAUGE" - description: "Number of buffers written by the background writer" - - maxwritten_clean: - usage: "GAUGE" - description: "Number of times the background writer stopped a cleaning scan because it had written too many buffers" - - buffers_backend: - usage: "GAUGE" - description: "Number of buffers written directly by a backend" - - buffers_backend_fsync: - usage: "GAUGE" - description: "Number of times a backend had to execute its own fsync call (normally the background writer handles those even when the backend does its own write)" - - buffers_alloc: - usage: "GAUGE" - description: "Number of buffers allocated" - - stats_reset: - usage: "GAUGE" - description: "Time at which these statistics were last reset" - -ccp_data_checksum_failure: - query: "SELECT datname AS dbname - , checksum_failures AS count - , coalesce(extract(epoch from (clock_timestamp() - checksum_last_failure)), 0) AS time_since_last_failure_seconds - FROM pg_catalog.pg_stat_database;" - metrics: - - dbname: - usage: "LABEL" - description: "Database name" - - count: - usage: "GAUGE" - description: "Total number of checksum failures on this database" - - time_since_last_failure_seconds: - usage: "GAUGE" - description: "Time interval in seconds since the last checksum failure was encountered" - - -### -# -# End File: PG12 queries_general.yml -# -### diff --git a/postgres_exporter/common/pg12/queries_pg_stat_statements.yml b/postgres_exporter/common/pg12/queries_pg_stat_statements.yml deleted file mode 100644 index 56f3a9a7..00000000 --- a/postgres_exporter/common/pg12/queries_pg_stat_statements.yml +++ /dev/null @@ -1,135 +0,0 @@ -### -# -# Begin File: PG12 queries_pg_stat_statements.yml -# -# Copyright © 2017-2025 Crunchy Data Solutions, Inc. All Rights Reserved. -# -### - -ccp_pg_stat_statements_total: - query: "SELECT pg_get_userbyid(s.userid) as role, - d.datname AS dbname, - sum(s.calls) AS calls_count, - sum(s.total_time) AS exec_time_ms, - avg(s.mean_time) AS mean_exec_time_ms, - sum(s.rows) AS row_count - FROM public.pg_stat_statements s - JOIN pg_catalog.pg_database d - ON d.oid = s.dbid - GROUP BY 1,2" - metrics: - - role: - usage: "LABEL" - description: "Role that executed the statement" - - dbname: - usage: "LABEL" - description: "Database in which the statement was executed" - - calls_count: - usage: "GAUGE" - description: "Total number of queries run per user/database" - - exec_time_ms: - usage: "GAUGE" - description: "Total runtime of all queries per user/database" - - mean_exec_time_ms: - usage: "GAUGE" - description: "Mean runtime of all queries per user/database" - - row_count: - usage: "GAUGE" - description: "Total rows returned from all queries per user/database" - - -ccp_pg_stat_statements_top_mean: - query: "SELECT pg_get_userbyid(s.userid) as role, - d.datname AS dbname, - s.queryid, - btrim(replace(left(s.query, 40), '\n', '')) AS query, - max(s.mean_time) exec_time_ms - FROM public.pg_stat_statements s - JOIN pg_catalog.pg_database d - ON d.oid = s.dbid - GROUP BY 1,2,3,4 - ORDER BY 5 DESC - LIMIT #PG_STAT_STATEMENTS_LIMIT#" - metrics: - - role: - usage: "LABEL" - description: "Role that executed the statement" - - dbname: - usage: "LABEL" - description: "Database in which the statement was executed" - - queryid: - usage: "LABEL" - description: "Internal hash code, computed from the statement's parse tree" - - query: - usage: "LABEL" - description: "First 40 characters of query text" - - exec_time_ms: - usage: "GAUGE" - description: "Average query runtime in milliseconds" - - -# Note that individual query stats can only be reset in PG12 -ccp_pg_stat_statements_top_total: - query: "SELECT pg_get_userbyid(s.userid) as role, - d.datname AS dbname, - s.queryid, - btrim(replace(left(s.query, 40), '\n', '')) AS query, - s.total_time exec_time_ms - FROM public.pg_stat_statements s - JOIN pg_catalog.pg_database d - ON d.oid = s.dbid - ORDER BY 5 DESC - LIMIT #PG_STAT_STATEMENTS_LIMIT#" - metrics: - - role: - usage: "LABEL" - description: "Role that executed the statement" - - dbname: - usage: "LABEL" - description: "Database in which the statement was executed" - - queryid: - usage: "LABEL" - description: "Internal hash code, computed from the statement's parse tree" - - query: - usage: "LABEL" - description: "First 40 characters of query text" - - exec_time_ms: - usage: "GAUGE" - description: "Total time spent in the statement in milliseconds" - - -# Note that individual query stats can only be reset in PG12 -ccp_pg_stat_statements_top_max: - query: "SELECT pg_get_userbyid(s.userid) as role, - d.datname AS dbname, - s.queryid, - btrim(replace(left(s.query, 40), '\n', '')) AS query, - s.max_time AS exec_time_ms - FROM public.pg_stat_statements s - JOIN pg_catalog.pg_database d - ON d.oid = s.dbid - ORDER BY 5 DESC - LIMIT #PG_STAT_STATEMENTS_LIMIT#" - metrics: - - role: - usage: "LABEL" - description: "Role that executed the statement" - - dbname: - usage: "LABEL" - description: "Database in which the statement was executed" - - queryid: - usage: "LABEL" - description: "Internal hash code, computed from the statement's parse tree" - - query: - usage: "LABEL" - description: "First 40 characters of query text" - - exec_time_ms: - usage: "GAUGE" - description: "Maximum time spent in the statement in milliseconds" - - -### -# -# End File: PG12 queries_pg_stat_statements.yml -# -### diff --git a/postgres_exporter/common/pg12/queries_pg_stat_statements_reset_info.yml b/postgres_exporter/common/pg12/queries_pg_stat_statements_reset_info.yml deleted file mode 100644 index e5331dd3..00000000 --- a/postgres_exporter/common/pg12/queries_pg_stat_statements_reset_info.yml +++ /dev/null @@ -1,19 +0,0 @@ -### -# -# Begin File: pg_stat_statements_reset_info.yml -# -# Copyright © 2017-2025 Crunchy Data Solutions, Inc. All Rights Reserved. -# -### -ccp_pg_stat_statements_reset: - query: "select monitor.pg_stat_statements_reset_info(#PG_STAT_STATEMENTS_THROTTLE_MINUTES#) as time" - metrics: - - time: - usage: "GAUGE" - description: "Epoch time when stats were reset" - -### -# -# End File: pg_stat_statements_reset_info.yml -# -### diff --git a/postgres_exporter/common/pg13/setup.sql b/postgres_exporter/common/pg13/setup.sql deleted file mode 100644 index 382f1c35..00000000 --- a/postgres_exporter/common/pg13/setup.sql +++ /dev/null @@ -1,463 +0,0 @@ --- PG13 pgMonitor Setup --- --- Copyright © 2017-2025 Crunchy Data Solutions, Inc. All Rights Reserved. --- - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'ccp_monitoring') THEN - CREATE ROLE ccp_monitoring WITH LOGIN; - END IF; - - -- The pgmonitor role is required by the pgnodemx extension in PostgreSQL versions 9.5 and 9.6 - -- and should be removed when upgrading to PostgreSQL 10 and above. - IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'pgmonitor') THEN - DROP ROLE pgmonitor; - END IF; -END -$$; - -GRANT pg_monitor to ccp_monitoring; -GRANT pg_execute_server_program TO ccp_monitoring; - -ALTER ROLE ccp_monitoring SET lock_timeout TO '2min'; -ALTER ROLE ccp_monitoring SET jit TO 'off'; - -CREATE SCHEMA IF NOT EXISTS monitor AUTHORIZATION ccp_monitoring; - -DROP TABLE IF EXISTS monitor.pgbackrest_info CASCADE; -CREATE TABLE IF NOT EXISTS monitor.pgbackrest_info (config_file text NOT NULL, data jsonb NOT NULL, gather_timestamp timestamptz DEFAULT now() NOT NULL); --- Force more aggressive autovacuum to avoid table bloat over time -ALTER TABLE monitor.pgbackrest_info SET (autovacuum_analyze_scale_factor = 0, autovacuum_vacuum_scale_factor = 0, autovacuum_vacuum_threshold = 10, autovacuum_analyze_threshold = 10); - -DROP FUNCTION IF EXISTS monitor.pgbackrest_info(); -- old version from 2.3 -DROP FUNCTION IF EXISTS monitor.pgbackrest_info(int); -CREATE OR REPLACE FUNCTION monitor.pgbackrest_info(p_throttle_minutes int DEFAULT 10) RETURNS SETOF monitor.pgbackrest_info - LANGUAGE plpgsql - SET search_path TO pg_catalog, pg_temp -AS $function$ -DECLARE - -v_gather_timestamp timestamptz; -v_throttle interval; -v_system_identifier bigint; - -BEGIN --- Get pgBackRest info in JSON format - -v_throttle := make_interval(mins := p_throttle_minutes); - -SELECT COALESCE(max(gather_timestamp), '1970-01-01'::timestamptz) INTO v_gather_timestamp FROM monitor.pgbackrest_info; - -IF pg_catalog.pg_is_in_recovery() = 'f' THEN - IF ((CURRENT_TIMESTAMP - v_gather_timestamp) > v_throttle) THEN - - -- Ensure table is empty - DELETE FROM monitor.pgbackrest_info; - - SELECT system_identifier into v_system_identifier FROM pg_control_system(); - - -- Copy data into the table directory from the pgBackRest into command - EXECUTE format( $cmd$ COPY monitor.pgbackrest_info (config_file, data) FROM program '/usr/bin/pgbackrest-info.sh %s' WITH (format text,DELIMITER '|') $cmd$, v_system_identifier::text ); - - END IF; -END IF; - -RETURN QUERY SELECT * FROM monitor.pgbackrest_info; - -IF NOT FOUND THEN - RAISE EXCEPTION 'No backups being returned from pgbackrest info command'; -END IF; - -END -$function$; - - -DROP FUNCTION IF EXISTS monitor.sequence_status(); -CREATE FUNCTION monitor.sequence_status() RETURNS TABLE (sequence_name text, last_value bigint, slots numeric, used numeric, percent int, cycle boolean, numleft numeric, table_usage text) - LANGUAGE sql SECURITY DEFINER STABLE - SET search_path TO pg_catalog, pg_temp -AS $function$ - -/* - * Provide detailed status information of sequences in the current database - */ - -WITH default_value_sequences AS ( - -- Get sequences defined as default values with related table - -- Note this subquery can be locked/hung by DDL that affects tables with sequences. - -- Use monitor.sequence_exhaustion() to actually monitor for sequences running out - SELECT s.seqrelid, c.oid - FROM pg_catalog.pg_attribute a - JOIN pg_catalog.pg_attrdef ad on (ad.adrelid,ad.adnum) = (a.attrelid,a.attnum) - JOIN pg_catalog.pg_class c on a.attrelid = c.oid - JOIN pg_catalog.pg_sequence s ON s.seqrelid = regexp_replace(pg_get_expr(ad.adbin,ad.adrelid), $re$^nextval\('(.+?)'::regclass\)$$re$, $re$\1$re$)::regclass - WHERE (pg_get_expr(ad.adbin,ad.adrelid)) ~ '^nextval\(' -), dep_sequences AS ( - -- Get sequences set as dependencies with related tables (identities) - SELECT s.seqrelid, c.oid - FROM pg_catalog.pg_sequence s - JOIN pg_catalog.pg_depend d ON s.seqrelid = d.objid - JOIN pg_catalog.pg_class c ON d.refobjid = c.oid - UNION - SELECT seqrelid, oid FROM default_value_sequences -), all_sequences AS ( - -- Get any remaining sequences - SELECT s.seqrelid AS sequence_oid, ds.oid AS table_oid - FROM pg_catalog.pg_sequence s - LEFT JOIN dep_sequences ds ON s.seqrelid = ds.seqrelid -) -SELECT sequence_name - , last_value - , slots - , used - , ROUND(used/slots*100)::int AS percent - , cycle - , CASE WHEN slots < used THEN 0 ELSE slots - used END AS numleft - , table_usage -FROM ( - SELECT format('%I.%I',s.schemaname, s.sequencename)::text AS sequence_name - , COALESCE(s.last_value,s.min_value) AS last_value - , s.cycle - , CEIL((s.max_value-min_value::NUMERIC+1)/s.increment_by::NUMERIC) AS slots - , CEIL((COALESCE(s.last_value,s.min_value)-s.min_value::NUMERIC+1)/s.increment_by::NUMERIC) AS used - , string_agg(a.table_oid::regclass::text, ', ') AS table_usage - FROM pg_catalog.pg_sequences s - JOIN all_sequences a ON (format('%I.%I', s.schemaname, s.sequencename))::regclass = a.sequence_oid - GROUP BY 1,2,3,4,5 -) x -ORDER BY ROUND(used/slots*100) DESC - -$function$; - - -DROP FUNCTION IF EXISTS monitor.sequence_exhaustion(int); -CREATE FUNCTION monitor.sequence_exhaustion(p_percent integer DEFAULT 75, OUT count bigint) - LANGUAGE sql SECURITY DEFINER STABLE - SET search_path TO pg_catalog, pg_temp -AS $function$ - -/* - * Returns count of sequences that have used up the % value given via the p_percent parameter (default 75%) - */ - -SELECT count(*) AS count -FROM ( - SELECT CEIL((s.max_value-min_value::NUMERIC+1)/s.increment_by::NUMERIC) AS slots - , CEIL((COALESCE(s.last_value,s.min_value)-s.min_value::NUMERIC+1)/s.increment_by::NUMERIC) AS used - FROM pg_catalog.pg_sequences s -) x -WHERE (ROUND(used/slots*100)::int) > p_percent; - -$function$; - -/* - * Tables and functions for monitoring changes to pg_settings and pg_hba_file_rules system catalogs. - * Can't just do a raw check for the hash value since Prometheus only records numeric values for alerts - * Tables allow recording of existing settings so they can be referred back to to see what changed - * If either checksum function returns 0, then NO settings have changed - * If either checksum function returns 1, then something has changed since last known valid state - * For replicas, logging past settings is not possible to compare what may have changed - * For replicas, by default, it is expected that its settings will match the primary - * For replicas, if the pg_settings or pg_hba.conf are necessarily different from the primary, a known good hash of that replica's - settings can be sent as an argument to the relevant checksum function. Views are provided to easily obtain the hash values used by this monitoring tool. - * If any known hash parameters are passed to the checksum functions, note that it will override any past hash values stored in the log table when doing comparisons and completely re-evaluate the entire state. This is true even if done on a primary where the current state will then also be logged for comparison if it differs from the given hash. - */ - -DROP TABLE IF EXISTS monitor.pg_settings_checksum; -DROP TABLE IF EXISTS monitor.pg_hba_checksum; - -CREATE TABLE monitor.pg_settings_checksum ( - settings_hash_generated text NOT NULL - , settings_hash_known_provided text - , settings_string text NOT NULL - , created_at timestamptz DEFAULT now() NOT NULL - , valid smallint NOT NULL ); - -COMMENT ON COLUMN monitor.pg_settings_checksum.valid IS 'Set this column to zero if this group of settings is a valid change'; -CREATE INDEX ON monitor.pg_settings_checksum (created_at); - -CREATE TABLE monitor.pg_hba_checksum ( - hba_hash_generated text NOT NULL - , hba_hash_known_provided text - , hba_string text NOT NULL - , created_at timestamptz DEFAULT now() NOT NULL - , valid smallint NOT NULL ); - -COMMENT ON COLUMN monitor.pg_hba_checksum.valid IS 'Set this column to zero if this group of settings is a valid change'; -CREATE INDEX ON monitor.pg_hba_checksum (created_at); - - -DROP FUNCTION IF EXISTS monitor.pg_settings_checksum(text); -CREATE FUNCTION monitor.pg_settings_checksum(p_known_settings_hash text DEFAULT NULL) - RETURNS smallint - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO pg_catalog, pg_temp -AS $function$ -DECLARE - -v_is_in_recovery boolean; -v_settings_hash text; -v_settings_hash_old text; -v_settings_match smallint := 0; -v_settings_string text; -v_settings_string_old text; -v_valid smallint; - -BEGIN - -SELECT pg_is_in_recovery() INTO v_is_in_recovery; - -SELECT md5_hash - , settings_string -INTO v_settings_hash - , v_settings_string -FROM monitor.pg_settings_hash; - -SELECT settings_hash_generated, valid -INTO v_settings_hash_old, v_valid -FROM monitor.pg_settings_checksum -ORDER BY created_at DESC LIMIT 1; - -IF p_known_settings_hash IS NOT NULL THEN - v_settings_hash_old := p_known_settings_hash; - -- Do not base validity on the stored value if manual hash is given. - v_valid := 0; -END IF; - -IF (v_settings_hash_old IS NOT NULL) THEN - - IF (v_settings_hash != v_settings_hash_old) THEN - - v_valid := 1; - - IF v_is_in_recovery = false THEN - INSERT INTO monitor.pg_settings_checksum ( - settings_hash_generated - , settings_hash_known_provided - , settings_string - , valid) - VALUES ( - v_settings_hash - , p_known_settings_hash - , v_settings_string - , v_valid); - END IF; - END IF; - -ELSE - - v_valid := 0; - IF v_is_in_recovery = false THEN - INSERT INTO monitor.pg_settings_checksum ( - settings_hash_generated - , settings_hash_known_provided - , settings_string - , valid) - VALUES (v_settings_hash - , p_known_settings_hash - , v_settings_string - , v_valid); - END IF; - -END IF; - -RETURN v_valid; - -END -$function$; - - -DROP FUNCTION IF EXISTS monitor.pg_hba_checksum(text); -CREATE FUNCTION monitor.pg_hba_checksum(p_known_hba_hash text DEFAULT NULL) - RETURNS smallint - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO pg_catalog, pg_temp -AS $function$ -DECLARE - -v_hba_hash text; -v_hba_hash_old text; -v_hba_match smallint := 0; -v_hba_string text; -v_hba_string_old text; -v_is_in_recovery boolean; -v_valid smallint; - -BEGIN - -SELECT pg_is_in_recovery() INTO v_is_in_recovery; - -IF current_setting('server_version_num')::int >= 100000 THEN - - SELECT md5_hash - , hba_string - INTO v_hba_hash - , v_hba_string - FROM monitor.pg_hba_hash; - -ELSE - RAISE EXCEPTION 'pg_hba change monitoring unsupported in versions older than PostgreSQL 10'; -END IF; - -SELECT hba_hash_generated, valid -INTO v_hba_hash_old, v_valid -FROM monitor.pg_hba_checksum -ORDER BY created_at DESC LIMIT 1; - -IF p_known_hba_hash IS NOT NULL THEN - v_hba_hash_old := p_known_hba_hash; - -- Do not base validity on the stored value if manual hash is given. - v_valid := 0; -END IF; - -IF (v_hba_hash_old IS NOT NULL) THEN - - IF (v_hba_hash != v_hba_hash_old) THEN - - v_valid := 1; - - IF v_is_in_recovery = false THEN - INSERT INTO monitor.pg_hba_checksum ( - hba_hash_generated - , hba_hash_known_provided - , hba_string - , valid) - VALUES ( - v_hba_hash - , p_known_hba_hash - , v_hba_string - , v_valid); - END IF; - END IF; - -ELSE - - v_valid := 0; - IF v_is_in_recovery = false THEN - INSERT INTO monitor.pg_hba_checksum ( - hba_hash_generated - , hba_hash_known_provided - , hba_string - , valid) - VALUES (v_hba_hash - , p_known_hba_hash - , v_hba_string - , v_valid); - END IF; - -END IF; - -RETURN v_valid; - -END -$function$; - - -DROP FUNCTION IF EXISTS monitor.pg_settings_checksum_set_valid(); -/* - * This function provides quick, clear interface for resetting the checksum monitor to treat the currently detected configuration as valid after alerting on a change. Note that configuration history will be cleared. - */ -CREATE FUNCTION monitor.pg_settings_checksum_set_valid() RETURNS smallint - LANGUAGE sql -AS $function$ - -TRUNCATE monitor.pg_settings_checksum; - -SELECT monitor.pg_settings_checksum(); - -$function$; - - -DROP FUNCTION IF EXISTS monitor.pg_hba_checksum_set_valid(); -/* - * This function provides quick, clear interface for resetting the checksum monitor to treat the currently detected configuration as valid after alerting on a change. Note that configuration history will be cleared. - */ -CREATE FUNCTION monitor.pg_hba_checksum_set_valid() RETURNS smallint - LANGUAGE sql -AS $function$ - -TRUNCATE monitor.pg_hba_checksum; - -SELECT monitor.pg_hba_checksum(); - -$function$; - - -DROP VIEW IF EXISTS monitor.pg_settings_hash; -CREATE VIEW monitor.pg_settings_hash AS - WITH settings_ordered_list AS ( - SELECT name - , COALESCE(setting, '<>') AS setting - FROM pg_catalog.pg_settings - ORDER BY name, setting) - SELECT md5(string_agg(name||setting, ',')) AS md5_hash - , string_agg(name||setting, ',') AS settings_string - FROM settings_ordered_list; - - -DROP VIEW IF EXISTS monitor.pg_hba_hash; -CREATE VIEW monitor.pg_hba_hash AS - -- Order by line number so it's caught if no content is changed but the order of entries is changed - WITH hba_ordered_list AS ( - SELECT COALESCE(type, '<>') AS type - , array_to_string(COALESCE(database, ARRAY['<>']), ',') AS database - , array_to_string(COALESCE(user_name, ARRAY['<>']), ',') AS user_name - , COALESCE(address, '<>') AS address - , COALESCE(netmask, '<>') AS netmask - , COALESCE(auth_method, '<>') AS auth_method - , array_to_string(COALESCE(options, ARRAY['<>']), ',') AS options - FROM pg_catalog.pg_hba_file_rules - ORDER BY line_number) - SELECT md5(string_agg(type||database||user_name||address||netmask||auth_method||options, ',')) AS md5_hash - , string_agg(type||database||user_name||address||netmask||auth_method||options, ',') AS hba_string - FROM hba_ordered_list; - - - -DROP TABLE IF EXISTS monitor.pg_stat_statements_reset_info; --- Table to store last reset time for pg_stat_statements -CREATE TABLE monitor.pg_stat_statements_reset_info( - reset_time timestamptz -); - -DROP FUNCTION IF EXISTS monitor.pg_stat_statements_reset_info(int); --- Function to reset pg_stat_statements periodically -CREATE FUNCTION monitor.pg_stat_statements_reset_info(p_throttle_minutes integer DEFAULT 1440) - RETURNS bigint - LANGUAGE plpgsql - SECURITY DEFINER - SET search_path TO pg_catalog, pg_temp -AS $function$ -DECLARE - - v_reset_timestamp timestamptz; - v_throttle interval; - -BEGIN - - IF p_throttle_minutes < 0 THEN - RETURN 0; - END IF; - - v_throttle := make_interval(mins := p_throttle_minutes); - - SELECT COALESCE(max(reset_time), '1970-01-01'::timestamptz) INTO v_reset_timestamp FROM monitor.pg_stat_statements_reset_info; - - IF ((CURRENT_TIMESTAMP - v_reset_timestamp) > v_throttle) THEN - -- Ensure table is empty - DELETE FROM monitor.pg_stat_statements_reset_info; - PERFORM pg_stat_statements_reset(); - INSERT INTO monitor.pg_stat_statements_reset_info(reset_time) values (now()); - END IF; - - RETURN (SELECT extract(epoch from reset_time) FROM monitor.pg_stat_statements_reset_info); - -EXCEPTION - WHEN others then - RETURN 0; -END -$function$; - -GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA monitor TO ccp_monitoring; -GRANT ALL ON ALL TABLES IN SCHEMA monitor TO ccp_monitoring; diff --git a/postgres_exporter/common/pg14/setup.sql b/postgres_exporter/common/pg14/setup.sql deleted file mode 100644 index a9ce3a95..00000000 --- a/postgres_exporter/common/pg14/setup.sql +++ /dev/null @@ -1,463 +0,0 @@ --- PG14 pgMonitor Setup --- --- Copyright © 2017-2025 Crunchy Data Solutions, Inc. All Rights Reserved. --- - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'ccp_monitoring') THEN - CREATE ROLE ccp_monitoring WITH LOGIN; - END IF; - - -- The pgmonitor role is required by the pgnodemx extension in PostgreSQL versions 9.5 and 9.6 - -- and should be removed when upgrading to PostgreSQL 10 and above. - IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'pgmonitor') THEN - DROP ROLE pgmonitor; - END IF; -END -$$; - -GRANT pg_monitor to ccp_monitoring; -GRANT pg_execute_server_program TO ccp_monitoring; - -ALTER ROLE ccp_monitoring SET lock_timeout TO '2min'; -ALTER ROLE ccp_monitoring SET jit TO 'off'; - -CREATE SCHEMA IF NOT EXISTS monitor AUTHORIZATION ccp_monitoring; - -DROP TABLE IF EXISTS monitor.pgbackrest_info CASCADE; -CREATE TABLE IF NOT EXISTS monitor.pgbackrest_info (config_file text NOT NULL, data jsonb NOT NULL, gather_timestamp timestamptz DEFAULT now() NOT NULL); --- Force more aggressive autovacuum to avoid table bloat over time -ALTER TABLE monitor.pgbackrest_info SET (autovacuum_analyze_scale_factor = 0, autovacuum_vacuum_scale_factor = 0, autovacuum_vacuum_threshold = 10, autovacuum_analyze_threshold = 10); - -DROP FUNCTION IF EXISTS monitor.pgbackrest_info(); -- old version from 2.3 -DROP FUNCTION IF EXISTS monitor.pgbackrest_info(int); -CREATE OR REPLACE FUNCTION monitor.pgbackrest_info(p_throttle_minutes int DEFAULT 10) RETURNS SETOF monitor.pgbackrest_info - LANGUAGE plpgsql - SET search_path TO pg_catalog, pg_temp -AS $function$ -DECLARE - -v_gather_timestamp timestamptz; -v_throttle interval; -v_system_identifier bigint; - -BEGIN --- Get pgBackRest info in JSON format - -v_throttle := make_interval(mins := p_throttle_minutes); - -SELECT COALESCE(max(gather_timestamp), '1970-01-01'::timestamptz) INTO v_gather_timestamp FROM monitor.pgbackrest_info; - -IF pg_catalog.pg_is_in_recovery() = 'f' THEN - IF ((CURRENT_TIMESTAMP - v_gather_timestamp) > v_throttle) THEN - - -- Ensure table is empty - DELETE FROM monitor.pgbackrest_info; - - SELECT system_identifier into v_system_identifier FROM pg_control_system(); - - -- Copy data into the table directory from the pgBackRest into command - EXECUTE format( $cmd$ COPY monitor.pgbackrest_info (config_file, data) FROM program '/usr/bin/pgbackrest-info.sh %s' WITH (format text,DELIMITER '|') $cmd$, v_system_identifier::text ); - - END IF; -END IF; - -RETURN QUERY SELECT * FROM monitor.pgbackrest_info; - -IF NOT FOUND THEN - RAISE EXCEPTION 'No backups being returned from pgbackrest info command'; -END IF; - -END -$function$; - - -DROP FUNCTION IF EXISTS monitor.sequence_status(); -CREATE FUNCTION monitor.sequence_status() RETURNS TABLE (sequence_name text, last_value bigint, slots numeric, used numeric, percent int, cycle boolean, numleft numeric, table_usage text) - LANGUAGE sql SECURITY DEFINER STABLE - SET search_path TO pg_catalog, pg_temp -AS $function$ - -/* - * Provide detailed status information of sequences in the current database - */ - -WITH default_value_sequences AS ( - -- Get sequences defined as default values with related table - -- Note this subquery can be locked/hung by DDL that affects tables with sequences. - -- Use monitor.sequence_exhaustion() to actually monitor for sequences running out - SELECT s.seqrelid, c.oid - FROM pg_catalog.pg_attribute a - JOIN pg_catalog.pg_attrdef ad on (ad.adrelid,ad.adnum) = (a.attrelid,a.attnum) - JOIN pg_catalog.pg_class c on a.attrelid = c.oid - JOIN pg_catalog.pg_sequence s ON s.seqrelid = regexp_replace(pg_get_expr(ad.adbin,ad.adrelid), $re$^nextval\('(.+?)'::regclass\)$$re$, $re$\1$re$)::regclass - WHERE (pg_get_expr(ad.adbin,ad.adrelid)) ~ '^nextval\(' -), dep_sequences AS ( - -- Get sequences set as dependencies with related tables (identities) - SELECT s.seqrelid, c.oid - FROM pg_catalog.pg_sequence s - JOIN pg_catalog.pg_depend d ON s.seqrelid = d.objid - JOIN pg_catalog.pg_class c ON d.refobjid = c.oid - UNION - SELECT seqrelid, oid FROM default_value_sequences -), all_sequences AS ( - -- Get any remaining sequences - SELECT s.seqrelid AS sequence_oid, ds.oid AS table_oid - FROM pg_catalog.pg_sequence s - LEFT JOIN dep_sequences ds ON s.seqrelid = ds.seqrelid -) -SELECT sequence_name - , last_value - , slots - , used - , ROUND(used/slots*100)::int AS percent - , cycle - , CASE WHEN slots < used THEN 0 ELSE slots - used END AS numleft - , table_usage -FROM ( - SELECT format('%I.%I',s.schemaname, s.sequencename)::text AS sequence_name - , COALESCE(s.last_value,s.min_value) AS last_value - , s.cycle - , CEIL((s.max_value-min_value::NUMERIC+1)/s.increment_by::NUMERIC) AS slots - , CEIL((COALESCE(s.last_value,s.min_value)-s.min_value::NUMERIC+1)/s.increment_by::NUMERIC) AS used - , string_agg(a.table_oid::regclass::text, ', ') AS table_usage - FROM pg_catalog.pg_sequences s - JOIN all_sequences a ON (format('%I.%I', s.schemaname, s.sequencename))::regclass = a.sequence_oid - GROUP BY 1,2,3,4,5 -) x -ORDER BY ROUND(used/slots*100) DESC - -$function$; - - -DROP FUNCTION IF EXISTS monitor.sequence_exhaustion(int); -CREATE FUNCTION monitor.sequence_exhaustion(p_percent integer DEFAULT 75, OUT count bigint) - LANGUAGE sql SECURITY DEFINER STABLE - SET search_path TO pg_catalog, pg_temp -AS $function$ - -/* - * Returns count of sequences that have used up the % value given via the p_percent parameter (default 75%) - */ - -SELECT count(*) AS count -FROM ( - SELECT CEIL((s.max_value-min_value::NUMERIC+1)/s.increment_by::NUMERIC) AS slots - , CEIL((COALESCE(s.last_value,s.min_value)-s.min_value::NUMERIC+1)/s.increment_by::NUMERIC) AS used - FROM pg_catalog.pg_sequences s -) x -WHERE (ROUND(used/slots*100)::int) > p_percent; - -$function$; - -/* - * Tables and functions for monitoring changes to pg_settings and pg_hba_file_rules system catalogs. - * Can't just do a raw check for the hash value since Prometheus only records numeric values for alerts - * Tables allow recording of existing settings so they can be referred back to to see what changed - * If either checksum function returns 0, then NO settings have changed - * If either checksum function returns 1, then something has changed since last known valid state - * For replicas, logging past settings is not possible to compare what may have changed - * For replicas, by default, it is expected that its settings will match the primary - * For replicas, if the pg_settings or pg_hba.conf are necessarily different from the primary, a known good hash of that replica's - settings can be sent as an argument to the relevant checksum function. Views are provided to easily obtain the hash values used by this monitoring tool. - * If any known hash parameters are passed to the checksum functions, note that it will override any past hash values stored in the log table when doing comparisons and completely re-evaluate the entire state. This is true even if done on a primary where the current state will then also be logged for comparison if it differs from the given hash. - */ - -DROP TABLE IF EXISTS monitor.pg_settings_checksum; -DROP TABLE IF EXISTS monitor.pg_hba_checksum; - -CREATE TABLE monitor.pg_settings_checksum ( - settings_hash_generated text NOT NULL - , settings_hash_known_provided text - , settings_string text NOT NULL - , created_at timestamptz DEFAULT now() NOT NULL - , valid smallint NOT NULL ); - -COMMENT ON COLUMN monitor.pg_settings_checksum.valid IS 'Set this column to zero if this group of settings is a valid change'; -CREATE INDEX ON monitor.pg_settings_checksum (created_at); - -CREATE TABLE monitor.pg_hba_checksum ( - hba_hash_generated text NOT NULL - , hba_hash_known_provided text - , hba_string text NOT NULL - , created_at timestamptz DEFAULT now() NOT NULL - , valid smallint NOT NULL ); - -COMMENT ON COLUMN monitor.pg_hba_checksum.valid IS 'Set this column to zero if this group of settings is a valid change'; -CREATE INDEX ON monitor.pg_hba_checksum (created_at); - - -DROP FUNCTION IF EXISTS monitor.pg_settings_checksum(text); -CREATE FUNCTION monitor.pg_settings_checksum(p_known_settings_hash text DEFAULT NULL) - RETURNS smallint - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO pg_catalog, pg_temp -AS $function$ -DECLARE - -v_is_in_recovery boolean; -v_settings_hash text; -v_settings_hash_old text; -v_settings_match smallint := 0; -v_settings_string text; -v_settings_string_old text; -v_valid smallint; - -BEGIN - -SELECT pg_is_in_recovery() INTO v_is_in_recovery; - -SELECT md5_hash - , settings_string -INTO v_settings_hash - , v_settings_string -FROM monitor.pg_settings_hash; - -SELECT settings_hash_generated, valid -INTO v_settings_hash_old, v_valid -FROM monitor.pg_settings_checksum -ORDER BY created_at DESC LIMIT 1; - -IF p_known_settings_hash IS NOT NULL THEN - v_settings_hash_old := p_known_settings_hash; - -- Do not base validity on the stored value if manual hash is given. - v_valid := 0; -END IF; - -IF (v_settings_hash_old IS NOT NULL) THEN - - IF (v_settings_hash != v_settings_hash_old) THEN - - v_valid := 1; - - IF v_is_in_recovery = false THEN - INSERT INTO monitor.pg_settings_checksum ( - settings_hash_generated - , settings_hash_known_provided - , settings_string - , valid) - VALUES ( - v_settings_hash - , p_known_settings_hash - , v_settings_string - , v_valid); - END IF; - END IF; - -ELSE - - v_valid := 0; - IF v_is_in_recovery = false THEN - INSERT INTO monitor.pg_settings_checksum ( - settings_hash_generated - , settings_hash_known_provided - , settings_string - , valid) - VALUES (v_settings_hash - , p_known_settings_hash - , v_settings_string - , v_valid); - END IF; - -END IF; - -RETURN v_valid; - -END -$function$; - - -DROP FUNCTION IF EXISTS monitor.pg_hba_checksum(text); -CREATE FUNCTION monitor.pg_hba_checksum(p_known_hba_hash text DEFAULT NULL) - RETURNS smallint - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO pg_catalog, pg_temp -AS $function$ -DECLARE - -v_hba_hash text; -v_hba_hash_old text; -v_hba_match smallint := 0; -v_hba_string text; -v_hba_string_old text; -v_is_in_recovery boolean; -v_valid smallint; - -BEGIN - -SELECT pg_is_in_recovery() INTO v_is_in_recovery; - -IF current_setting('server_version_num')::int >= 100000 THEN - - SELECT md5_hash - , hba_string - INTO v_hba_hash - , v_hba_string - FROM monitor.pg_hba_hash; - -ELSE - RAISE EXCEPTION 'pg_hba change monitoring unsupported in versions older than PostgreSQL 10'; -END IF; - -SELECT hba_hash_generated, valid -INTO v_hba_hash_old, v_valid -FROM monitor.pg_hba_checksum -ORDER BY created_at DESC LIMIT 1; - -IF p_known_hba_hash IS NOT NULL THEN - v_hba_hash_old := p_known_hba_hash; - -- Do not base validity on the stored value if manual hash is given. - v_valid := 0; -END IF; - -IF (v_hba_hash_old IS NOT NULL) THEN - - IF (v_hba_hash != v_hba_hash_old) THEN - - v_valid := 1; - - IF v_is_in_recovery = false THEN - INSERT INTO monitor.pg_hba_checksum ( - hba_hash_generated - , hba_hash_known_provided - , hba_string - , valid) - VALUES ( - v_hba_hash - , p_known_hba_hash - , v_hba_string - , v_valid); - END IF; - END IF; - -ELSE - - v_valid := 0; - IF v_is_in_recovery = false THEN - INSERT INTO monitor.pg_hba_checksum ( - hba_hash_generated - , hba_hash_known_provided - , hba_string - , valid) - VALUES (v_hba_hash - , p_known_hba_hash - , v_hba_string - , v_valid); - END IF; - -END IF; - -RETURN v_valid; - -END -$function$; - - -DROP FUNCTION IF EXISTS monitor.pg_settings_checksum_set_valid(); -/* - * This function provides quick, clear interface for resetting the checksum monitor to treat the currently detected configuration as valid after alerting on a change. Note that configuration history will be cleared. - */ -CREATE FUNCTION monitor.pg_settings_checksum_set_valid() RETURNS smallint - LANGUAGE sql -AS $function$ - -TRUNCATE monitor.pg_settings_checksum; - -SELECT monitor.pg_settings_checksum(); - -$function$; - - -DROP FUNCTION IF EXISTS monitor.pg_hba_checksum_set_valid(); -/* - * This function provides quick, clear interface for resetting the checksum monitor to treat the currently detected configuration as valid after alerting on a change. Note that configuration history will be cleared. - */ -CREATE FUNCTION monitor.pg_hba_checksum_set_valid() RETURNS smallint - LANGUAGE sql -AS $function$ - -TRUNCATE monitor.pg_hba_checksum; - -SELECT monitor.pg_hba_checksum(); - -$function$; - - -DROP VIEW IF EXISTS monitor.pg_settings_hash; -CREATE VIEW monitor.pg_settings_hash AS - WITH settings_ordered_list AS ( - SELECT name - , COALESCE(setting, '<>') AS setting - FROM pg_catalog.pg_settings - ORDER BY name, setting) - SELECT md5(string_agg(name||setting, ',')) AS md5_hash - , string_agg(name||setting, ',') AS settings_string - FROM settings_ordered_list; - - -DROP VIEW IF EXISTS monitor.pg_hba_hash; -CREATE VIEW monitor.pg_hba_hash AS - -- Order by line number so it's caught if no content is changed but the order of entries is changed - WITH hba_ordered_list AS ( - SELECT COALESCE(type, '<>') AS type - , array_to_string(COALESCE(database, ARRAY['<>']), ',') AS database - , array_to_string(COALESCE(user_name, ARRAY['<>']), ',') AS user_name - , COALESCE(address, '<>') AS address - , COALESCE(netmask, '<>') AS netmask - , COALESCE(auth_method, '<>') AS auth_method - , array_to_string(COALESCE(options, ARRAY['<>']), ',') AS options - FROM pg_catalog.pg_hba_file_rules - ORDER BY line_number) - SELECT md5(string_agg(type||database||user_name||address||netmask||auth_method||options, ',')) AS md5_hash - , string_agg(type||database||user_name||address||netmask||auth_method||options, ',') AS hba_string - FROM hba_ordered_list; - - - -DROP TABLE IF EXISTS monitor.pg_stat_statements_reset_info; --- Table to store last reset time for pg_stat_statements -CREATE TABLE monitor.pg_stat_statements_reset_info( - reset_time timestamptz -); - -DROP FUNCTION IF EXISTS monitor.pg_stat_statements_reset_info(int); --- Function to reset pg_stat_statements periodically -CREATE FUNCTION monitor.pg_stat_statements_reset_info(p_throttle_minutes integer DEFAULT 1440) - RETURNS bigint - LANGUAGE plpgsql - SECURITY DEFINER - SET search_path TO pg_catalog, pg_temp -AS $function$ -DECLARE - - v_reset_timestamp timestamptz; - v_throttle interval; - -BEGIN - - IF p_throttle_minutes < 0 THEN - RETURN 0; - END IF; - - v_throttle := make_interval(mins := p_throttle_minutes); - - SELECT COALESCE(max(reset_time), '1970-01-01'::timestamptz) INTO v_reset_timestamp FROM monitor.pg_stat_statements_reset_info; - - IF ((CURRENT_TIMESTAMP - v_reset_timestamp) > v_throttle) THEN - -- Ensure table is empty - DELETE FROM monitor.pg_stat_statements_reset_info; - PERFORM pg_stat_statements_reset(); - INSERT INTO monitor.pg_stat_statements_reset_info(reset_time) values (now()); - END IF; - - RETURN (SELECT extract(epoch from reset_time) FROM monitor.pg_stat_statements_reset_info); - -EXCEPTION - WHEN others then - RETURN 0; -END -$function$; - -GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA monitor TO ccp_monitoring; -GRANT ALL ON ALL TABLES IN SCHEMA monitor TO ccp_monitoring; diff --git a/postgres_exporter/common/pg15/setup.sql b/postgres_exporter/common/pg15/setup.sql deleted file mode 100644 index fa435ad3..00000000 --- a/postgres_exporter/common/pg15/setup.sql +++ /dev/null @@ -1,463 +0,0 @@ --- PG15 pgMonitor Setup --- --- Copyright © 2017-2025 Crunchy Data Solutions, Inc. All Rights Reserved. --- - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'ccp_monitoring') THEN - CREATE ROLE ccp_monitoring WITH LOGIN; - END IF; - - -- The pgmonitor role is required by the pgnodemx extension in PostgreSQL versions 9.5 and 9.6 - -- and should be removed when upgrading to PostgreSQL 10 and above. - IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'pgmonitor') THEN - DROP ROLE pgmonitor; - END IF; -END -$$; - -GRANT pg_monitor to ccp_monitoring; -GRANT pg_execute_server_program TO ccp_monitoring; - -ALTER ROLE ccp_monitoring SET lock_timeout TO '2min'; -ALTER ROLE ccp_monitoring SET jit TO 'off'; - -CREATE SCHEMA IF NOT EXISTS monitor AUTHORIZATION ccp_monitoring; - -DROP TABLE IF EXISTS monitor.pgbackrest_info CASCADE; -CREATE TABLE IF NOT EXISTS monitor.pgbackrest_info (config_file text NOT NULL, data jsonb NOT NULL, gather_timestamp timestamptz DEFAULT now() NOT NULL); --- Force more aggressive autovacuum to avoid table bloat over time -ALTER TABLE monitor.pgbackrest_info SET (autovacuum_analyze_scale_factor = 0, autovacuum_vacuum_scale_factor = 0, autovacuum_vacuum_threshold = 10, autovacuum_analyze_threshold = 10); - -DROP FUNCTION IF EXISTS monitor.pgbackrest_info(); -- old version from 2.3 -DROP FUNCTION IF EXISTS monitor.pgbackrest_info(int); -CREATE OR REPLACE FUNCTION monitor.pgbackrest_info(p_throttle_minutes int DEFAULT 10) RETURNS SETOF monitor.pgbackrest_info - LANGUAGE plpgsql - SET search_path TO pg_catalog, pg_temp -AS $function$ -DECLARE - -v_gather_timestamp timestamptz; -v_throttle interval; -v_system_identifier bigint; - -BEGIN --- Get pgBackRest info in JSON format - -v_throttle := make_interval(mins := p_throttle_minutes); - -SELECT COALESCE(max(gather_timestamp), '1970-01-01'::timestamptz) INTO v_gather_timestamp FROM monitor.pgbackrest_info; - -IF pg_catalog.pg_is_in_recovery() = 'f' THEN - IF ((CURRENT_TIMESTAMP - v_gather_timestamp) > v_throttle) THEN - - -- Ensure table is empty - DELETE FROM monitor.pgbackrest_info; - - SELECT system_identifier into v_system_identifier FROM pg_control_system(); - - -- Copy data into the table directory from the pgBackRest into command - EXECUTE format( $cmd$ COPY monitor.pgbackrest_info (config_file, data) FROM program '/usr/bin/pgbackrest-info.sh %s' WITH (format text,DELIMITER '|') $cmd$, v_system_identifier::text ); - - END IF; -END IF; - -RETURN QUERY SELECT * FROM monitor.pgbackrest_info; - -IF NOT FOUND THEN - RAISE EXCEPTION 'No backups being returned from pgbackrest info command'; -END IF; - -END -$function$; - - -DROP FUNCTION IF EXISTS monitor.sequence_status(); -CREATE FUNCTION monitor.sequence_status() RETURNS TABLE (sequence_name text, last_value bigint, slots numeric, used numeric, percent int, cycle boolean, numleft numeric, table_usage text) - LANGUAGE sql SECURITY DEFINER STABLE - SET search_path TO pg_catalog, pg_temp -AS $function$ - -/* - * Provide detailed status information of sequences in the current database - */ - -WITH default_value_sequences AS ( - -- Get sequences defined as default values with related table - -- Note this subquery can be locked/hung by DDL that affects tables with sequences. - -- Use monitor.sequence_exhaustion() to actually monitor for sequences running out - SELECT s.seqrelid, c.oid - FROM pg_catalog.pg_attribute a - JOIN pg_catalog.pg_attrdef ad on (ad.adrelid,ad.adnum) = (a.attrelid,a.attnum) - JOIN pg_catalog.pg_class c on a.attrelid = c.oid - JOIN pg_catalog.pg_sequence s ON s.seqrelid = regexp_replace(pg_get_expr(ad.adbin,ad.adrelid), $re$^nextval\('(.+?)'::regclass\)$$re$, $re$\1$re$)::regclass - WHERE (pg_get_expr(ad.adbin,ad.adrelid)) ~ '^nextval\(' -), dep_sequences AS ( - -- Get sequences set as dependencies with related tables (identities) - SELECT s.seqrelid, c.oid - FROM pg_catalog.pg_sequence s - JOIN pg_catalog.pg_depend d ON s.seqrelid = d.objid - JOIN pg_catalog.pg_class c ON d.refobjid = c.oid - UNION - SELECT seqrelid, oid FROM default_value_sequences -), all_sequences AS ( - -- Get any remaining sequences - SELECT s.seqrelid AS sequence_oid, ds.oid AS table_oid - FROM pg_catalog.pg_sequence s - LEFT JOIN dep_sequences ds ON s.seqrelid = ds.seqrelid -) -SELECT sequence_name - , last_value - , slots - , used - , ROUND(used/slots*100)::int AS percent - , cycle - , CASE WHEN slots < used THEN 0 ELSE slots - used END AS numleft - , table_usage -FROM ( - SELECT format('%I.%I',s.schemaname, s.sequencename)::text AS sequence_name - , COALESCE(s.last_value,s.min_value) AS last_value - , s.cycle - , CEIL((s.max_value-min_value::NUMERIC+1)/s.increment_by::NUMERIC) AS slots - , CEIL((COALESCE(s.last_value,s.min_value)-s.min_value::NUMERIC+1)/s.increment_by::NUMERIC) AS used - , string_agg(a.table_oid::regclass::text, ', ') AS table_usage - FROM pg_catalog.pg_sequences s - JOIN all_sequences a ON (format('%I.%I', s.schemaname, s.sequencename))::regclass = a.sequence_oid - GROUP BY 1,2,3,4,5 -) x -ORDER BY ROUND(used/slots*100) DESC - -$function$; - - -DROP FUNCTION IF EXISTS monitor.sequence_exhaustion(int); -CREATE FUNCTION monitor.sequence_exhaustion(p_percent integer DEFAULT 75, OUT count bigint) - LANGUAGE sql SECURITY DEFINER STABLE - SET search_path TO pg_catalog, pg_temp -AS $function$ - -/* - * Returns count of sequences that have used up the % value given via the p_percent parameter (default 75%) - */ - -SELECT count(*) AS count -FROM ( - SELECT CEIL((s.max_value-min_value::NUMERIC+1)/s.increment_by::NUMERIC) AS slots - , CEIL((COALESCE(s.last_value,s.min_value)-s.min_value::NUMERIC+1)/s.increment_by::NUMERIC) AS used - FROM pg_catalog.pg_sequences s -) x -WHERE (ROUND(used/slots*100)::int) > p_percent; - -$function$; - -/* - * Tables and functions for monitoring changes to pg_settings and pg_hba_file_rules system catalogs. - * Can't just do a raw check for the hash value since Prometheus only records numeric values for alerts - * Tables allow recording of existing settings so they can be referred back to to see what changed - * If either checksum function returns 0, then NO settings have changed - * If either checksum function returns 1, then something has changed since last known valid state - * For replicas, logging past settings is not possible to compare what may have changed - * For replicas, by default, it is expected that its settings will match the primary - * For replicas, if the pg_settings or pg_hba.conf are necessarily different from the primary, a known good hash of that replica's - settings can be sent as an argument to the relevant checksum function. Views are provided to easily obtain the hash values used by this monitoring tool. - * If any known hash parameters are passed to the checksum functions, note that it will override any past hash values stored in the log table when doing comparisons and completely re-evaluate the entire state. This is true even if done on a primary where the current state will then also be logged for comparison if it differs from the given hash. - */ - -DROP TABLE IF EXISTS monitor.pg_settings_checksum; -DROP TABLE IF EXISTS monitor.pg_hba_checksum; - -CREATE TABLE monitor.pg_settings_checksum ( - settings_hash_generated text NOT NULL - , settings_hash_known_provided text - , settings_string text NOT NULL - , created_at timestamptz DEFAULT now() NOT NULL - , valid smallint NOT NULL ); - -COMMENT ON COLUMN monitor.pg_settings_checksum.valid IS 'Set this column to zero if this group of settings is a valid change'; -CREATE INDEX ON monitor.pg_settings_checksum (created_at); - -CREATE TABLE monitor.pg_hba_checksum ( - hba_hash_generated text NOT NULL - , hba_hash_known_provided text - , hba_string text NOT NULL - , created_at timestamptz DEFAULT now() NOT NULL - , valid smallint NOT NULL ); - -COMMENT ON COLUMN monitor.pg_hba_checksum.valid IS 'Set this column to zero if this group of settings is a valid change'; -CREATE INDEX ON monitor.pg_hba_checksum (created_at); - - -DROP FUNCTION IF EXISTS monitor.pg_settings_checksum(text); -CREATE FUNCTION monitor.pg_settings_checksum(p_known_settings_hash text DEFAULT NULL) - RETURNS smallint - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO pg_catalog, pg_temp -AS $function$ -DECLARE - -v_is_in_recovery boolean; -v_settings_hash text; -v_settings_hash_old text; -v_settings_match smallint := 0; -v_settings_string text; -v_settings_string_old text; -v_valid smallint; - -BEGIN - -SELECT pg_is_in_recovery() INTO v_is_in_recovery; - -SELECT md5_hash - , settings_string -INTO v_settings_hash - , v_settings_string -FROM monitor.pg_settings_hash; - -SELECT settings_hash_generated, valid -INTO v_settings_hash_old, v_valid -FROM monitor.pg_settings_checksum -ORDER BY created_at DESC LIMIT 1; - -IF p_known_settings_hash IS NOT NULL THEN - v_settings_hash_old := p_known_settings_hash; - -- Do not base validity on the stored value if manual hash is given. - v_valid := 0; -END IF; - -IF (v_settings_hash_old IS NOT NULL) THEN - - IF (v_settings_hash != v_settings_hash_old) THEN - - v_valid := 1; - - IF v_is_in_recovery = false THEN - INSERT INTO monitor.pg_settings_checksum ( - settings_hash_generated - , settings_hash_known_provided - , settings_string - , valid) - VALUES ( - v_settings_hash - , p_known_settings_hash - , v_settings_string - , v_valid); - END IF; - END IF; - -ELSE - - v_valid := 0; - IF v_is_in_recovery = false THEN - INSERT INTO monitor.pg_settings_checksum ( - settings_hash_generated - , settings_hash_known_provided - , settings_string - , valid) - VALUES (v_settings_hash - , p_known_settings_hash - , v_settings_string - , v_valid); - END IF; - -END IF; - -RETURN v_valid; - -END -$function$; - - -DROP FUNCTION IF EXISTS monitor.pg_hba_checksum(text); -CREATE FUNCTION monitor.pg_hba_checksum(p_known_hba_hash text DEFAULT NULL) - RETURNS smallint - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO pg_catalog, pg_temp -AS $function$ -DECLARE - -v_hba_hash text; -v_hba_hash_old text; -v_hba_match smallint := 0; -v_hba_string text; -v_hba_string_old text; -v_is_in_recovery boolean; -v_valid smallint; - -BEGIN - -SELECT pg_is_in_recovery() INTO v_is_in_recovery; - -IF current_setting('server_version_num')::int >= 100000 THEN - - SELECT md5_hash - , hba_string - INTO v_hba_hash - , v_hba_string - FROM monitor.pg_hba_hash; - -ELSE - RAISE EXCEPTION 'pg_hba change monitoring unsupported in versions older than PostgreSQL 10'; -END IF; - -SELECT hba_hash_generated, valid -INTO v_hba_hash_old, v_valid -FROM monitor.pg_hba_checksum -ORDER BY created_at DESC LIMIT 1; - -IF p_known_hba_hash IS NOT NULL THEN - v_hba_hash_old := p_known_hba_hash; - -- Do not base validity on the stored value if manual hash is given. - v_valid := 0; -END IF; - -IF (v_hba_hash_old IS NOT NULL) THEN - - IF (v_hba_hash != v_hba_hash_old) THEN - - v_valid := 1; - - IF v_is_in_recovery = false THEN - INSERT INTO monitor.pg_hba_checksum ( - hba_hash_generated - , hba_hash_known_provided - , hba_string - , valid) - VALUES ( - v_hba_hash - , p_known_hba_hash - , v_hba_string - , v_valid); - END IF; - END IF; - -ELSE - - v_valid := 0; - IF v_is_in_recovery = false THEN - INSERT INTO monitor.pg_hba_checksum ( - hba_hash_generated - , hba_hash_known_provided - , hba_string - , valid) - VALUES (v_hba_hash - , p_known_hba_hash - , v_hba_string - , v_valid); - END IF; - -END IF; - -RETURN v_valid; - -END -$function$; - - -DROP FUNCTION IF EXISTS monitor.pg_settings_checksum_set_valid(); -/* - * This function provides quick, clear interface for resetting the checksum monitor to treat the currently detected configuration as valid after alerting on a change. Note that configuration history will be cleared. - */ -CREATE FUNCTION monitor.pg_settings_checksum_set_valid() RETURNS smallint - LANGUAGE sql -AS $function$ - -TRUNCATE monitor.pg_settings_checksum; - -SELECT monitor.pg_settings_checksum(); - -$function$; - - -DROP FUNCTION IF EXISTS monitor.pg_hba_checksum_set_valid(); -/* - * This function provides quick, clear interface for resetting the checksum monitor to treat the currently detected configuration as valid after alerting on a change. Note that configuration history will be cleared. - */ -CREATE FUNCTION monitor.pg_hba_checksum_set_valid() RETURNS smallint - LANGUAGE sql -AS $function$ - -TRUNCATE monitor.pg_hba_checksum; - -SELECT monitor.pg_hba_checksum(); - -$function$; - - -DROP VIEW IF EXISTS monitor.pg_settings_hash; -CREATE VIEW monitor.pg_settings_hash AS - WITH settings_ordered_list AS ( - SELECT name - , COALESCE(setting, '<>') AS setting - FROM pg_catalog.pg_settings - ORDER BY name, setting) - SELECT md5(string_agg(name||setting, ',')) AS md5_hash - , string_agg(name||setting, ',') AS settings_string - FROM settings_ordered_list; - - -DROP VIEW IF EXISTS monitor.pg_hba_hash; -CREATE VIEW monitor.pg_hba_hash AS - -- Order by line number so it's caught if no content is changed but the order of entries is changed - WITH hba_ordered_list AS ( - SELECT COALESCE(type, '<>') AS type - , array_to_string(COALESCE(database, ARRAY['<>']), ',') AS database - , array_to_string(COALESCE(user_name, ARRAY['<>']), ',') AS user_name - , COALESCE(address, '<>') AS address - , COALESCE(netmask, '<>') AS netmask - , COALESCE(auth_method, '<>') AS auth_method - , array_to_string(COALESCE(options, ARRAY['<>']), ',') AS options - FROM pg_catalog.pg_hba_file_rules - ORDER BY line_number) - SELECT md5(string_agg(type||database||user_name||address||netmask||auth_method||options, ',')) AS md5_hash - , string_agg(type||database||user_name||address||netmask||auth_method||options, ',') AS hba_string - FROM hba_ordered_list; - - - -DROP TABLE IF EXISTS monitor.pg_stat_statements_reset_info; --- Table to store last reset time for pg_stat_statements -CREATE TABLE monitor.pg_stat_statements_reset_info( - reset_time timestamptz -); - -DROP FUNCTION IF EXISTS monitor.pg_stat_statements_reset_info(int); --- Function to reset pg_stat_statements periodically -CREATE FUNCTION monitor.pg_stat_statements_reset_info(p_throttle_minutes integer DEFAULT 1440) - RETURNS bigint - LANGUAGE plpgsql - SECURITY DEFINER - SET search_path TO pg_catalog, pg_temp -AS $function$ -DECLARE - - v_reset_timestamp timestamptz; - v_throttle interval; - -BEGIN - - IF p_throttle_minutes < 0 THEN - RETURN 0; - END IF; - - v_throttle := make_interval(mins := p_throttle_minutes); - - SELECT COALESCE(max(reset_time), '1970-01-01'::timestamptz) INTO v_reset_timestamp FROM monitor.pg_stat_statements_reset_info; - - IF ((CURRENT_TIMESTAMP - v_reset_timestamp) > v_throttle) THEN - -- Ensure table is empty - DELETE FROM monitor.pg_stat_statements_reset_info; - PERFORM pg_stat_statements_reset(); - INSERT INTO monitor.pg_stat_statements_reset_info(reset_time) values (now()); - END IF; - - RETURN (SELECT extract(epoch from reset_time) FROM monitor.pg_stat_statements_reset_info); - -EXCEPTION - WHEN others then - RETURN 0; -END -$function$; - -GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA monitor TO ccp_monitoring; -GRANT ALL ON ALL TABLES IN SCHEMA monitor TO ccp_monitoring; diff --git a/postgres_exporter/common/pg16/setup.sql b/postgres_exporter/common/pg16/setup.sql deleted file mode 100644 index 0faab9d1..00000000 --- a/postgres_exporter/common/pg16/setup.sql +++ /dev/null @@ -1,463 +0,0 @@ --- PG16 pgMonitor Setup --- --- Copyright © 2017-2025 Crunchy Data Solutions, Inc. All Rights Reserved. --- - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'ccp_monitoring') THEN - CREATE ROLE ccp_monitoring WITH LOGIN; - END IF; - - -- The pgmonitor role is required by the pgnodemx extension in PostgreSQL versions 9.5 and 9.6 - -- and should be removed when upgrading to PostgreSQL 10 and above. - IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'pgmonitor') THEN - DROP ROLE pgmonitor; - END IF; -END -$$; - -GRANT pg_monitor to ccp_monitoring; -GRANT pg_execute_server_program TO ccp_monitoring; - -ALTER ROLE ccp_monitoring SET lock_timeout TO '2min'; -ALTER ROLE ccp_monitoring SET jit TO 'off'; - -CREATE SCHEMA IF NOT EXISTS monitor AUTHORIZATION ccp_monitoring; - -DROP TABLE IF EXISTS monitor.pgbackrest_info CASCADE; -CREATE TABLE IF NOT EXISTS monitor.pgbackrest_info (config_file text NOT NULL, data jsonb NOT NULL, gather_timestamp timestamptz DEFAULT now() NOT NULL); --- Force more aggressive autovacuum to avoid table bloat over time -ALTER TABLE monitor.pgbackrest_info SET (autovacuum_analyze_scale_factor = 0, autovacuum_vacuum_scale_factor = 0, autovacuum_vacuum_threshold = 10, autovacuum_analyze_threshold = 10); - -DROP FUNCTION IF EXISTS monitor.pgbackrest_info(); -- old version from 2.3 -DROP FUNCTION IF EXISTS monitor.pgbackrest_info(int); -CREATE OR REPLACE FUNCTION monitor.pgbackrest_info(p_throttle_minutes int DEFAULT 10) RETURNS SETOF monitor.pgbackrest_info - LANGUAGE plpgsql - SET search_path TO pg_catalog, pg_temp -AS $function$ -DECLARE - -v_gather_timestamp timestamptz; -v_throttle interval; -v_system_identifier bigint; - -BEGIN --- Get pgBackRest info in JSON format - -v_throttle := make_interval(mins := p_throttle_minutes); - -SELECT COALESCE(max(gather_timestamp), '1970-01-01'::timestamptz) INTO v_gather_timestamp FROM monitor.pgbackrest_info; - -IF pg_catalog.pg_is_in_recovery() = 'f' THEN - IF ((CURRENT_TIMESTAMP - v_gather_timestamp) > v_throttle) THEN - - -- Ensure table is empty - DELETE FROM monitor.pgbackrest_info; - - SELECT system_identifier into v_system_identifier FROM pg_control_system(); - - -- Copy data into the table directory from the pgBackRest into command - EXECUTE format( $cmd$ COPY monitor.pgbackrest_info (config_file, data) FROM program '/usr/bin/pgbackrest-info.sh %s' WITH (format text,DELIMITER '|') $cmd$, v_system_identifier::text ); - - END IF; -END IF; - -RETURN QUERY SELECT * FROM monitor.pgbackrest_info; - -IF NOT FOUND THEN - RAISE EXCEPTION 'No backups being returned from pgbackrest info command'; -END IF; - -END -$function$; - - -DROP FUNCTION IF EXISTS monitor.sequence_status(); -CREATE FUNCTION monitor.sequence_status() RETURNS TABLE (sequence_name text, last_value bigint, slots numeric, used numeric, percent int, cycle boolean, numleft numeric, table_usage text) - LANGUAGE sql SECURITY DEFINER STABLE - SET search_path TO pg_catalog, pg_temp -AS $function$ - -/* - * Provide detailed status information of sequences in the current database - */ - -WITH default_value_sequences AS ( - -- Get sequences defined as default values with related table - -- Note this subquery can be locked/hung by DDL that affects tables with sequences. - -- Use monitor.sequence_exhaustion() to actually monitor for sequences running out - SELECT s.seqrelid, c.oid - FROM pg_catalog.pg_attribute a - JOIN pg_catalog.pg_attrdef ad on (ad.adrelid,ad.adnum) = (a.attrelid,a.attnum) - JOIN pg_catalog.pg_class c on a.attrelid = c.oid - JOIN pg_catalog.pg_sequence s ON s.seqrelid = regexp_replace(pg_get_expr(ad.adbin,ad.adrelid), $re$^nextval\('(.+?)'::regclass\)$$re$, $re$\1$re$)::regclass - WHERE (pg_get_expr(ad.adbin,ad.adrelid)) ~ '^nextval\(' -), dep_sequences AS ( - -- Get sequences set as dependencies with related tables (identities) - SELECT s.seqrelid, c.oid - FROM pg_catalog.pg_sequence s - JOIN pg_catalog.pg_depend d ON s.seqrelid = d.objid - JOIN pg_catalog.pg_class c ON d.refobjid = c.oid - UNION - SELECT seqrelid, oid FROM default_value_sequences -), all_sequences AS ( - -- Get any remaining sequences - SELECT s.seqrelid AS sequence_oid, ds.oid AS table_oid - FROM pg_catalog.pg_sequence s - LEFT JOIN dep_sequences ds ON s.seqrelid = ds.seqrelid -) -SELECT sequence_name - , last_value - , slots - , used - , ROUND(used/slots*100)::int AS percent - , cycle - , CASE WHEN slots < used THEN 0 ELSE slots - used END AS numleft - , table_usage -FROM ( - SELECT format('%I.%I',s.schemaname, s.sequencename)::text AS sequence_name - , COALESCE(s.last_value,s.min_value) AS last_value - , s.cycle - , CEIL((s.max_value-min_value::NUMERIC+1)/s.increment_by::NUMERIC) AS slots - , CEIL((COALESCE(s.last_value,s.min_value)-s.min_value::NUMERIC+1)/s.increment_by::NUMERIC) AS used - , string_agg(a.table_oid::regclass::text, ', ') AS table_usage - FROM pg_catalog.pg_sequences s - JOIN all_sequences a ON (format('%I.%I', s.schemaname, s.sequencename))::regclass = a.sequence_oid - GROUP BY 1,2,3,4,5 -) x -ORDER BY ROUND(used/slots*100) DESC - -$function$; - - -DROP FUNCTION IF EXISTS monitor.sequence_exhaustion(int); -CREATE FUNCTION monitor.sequence_exhaustion(p_percent integer DEFAULT 75, OUT count bigint) - LANGUAGE sql SECURITY DEFINER STABLE - SET search_path TO pg_catalog, pg_temp -AS $function$ - -/* - * Returns count of sequences that have used up the % value given via the p_percent parameter (default 75%) - */ - -SELECT count(*) AS count -FROM ( - SELECT CEIL((s.max_value-min_value::NUMERIC+1)/s.increment_by::NUMERIC) AS slots - , CEIL((COALESCE(s.last_value,s.min_value)-s.min_value::NUMERIC+1)/s.increment_by::NUMERIC) AS used - FROM pg_catalog.pg_sequences s -) x -WHERE (ROUND(used/slots*100)::int) > p_percent; - -$function$; - -/* - * Tables and functions for monitoring changes to pg_settings and pg_hba_file_rules system catalogs. - * Can't just do a raw check for the hash value since Prometheus only records numeric values for alerts - * Tables allow recording of existing settings so they can be referred back to to see what changed - * If either checksum function returns 0, then NO settings have changed - * If either checksum function returns 1, then something has changed since last known valid state - * For replicas, logging past settings is not possible to compare what may have changed - * For replicas, by default, it is expected that its settings will match the primary - * For replicas, if the pg_settings or pg_hba.conf are necessarily different from the primary, a known good hash of that replica's - settings can be sent as an argument to the relevant checksum function. Views are provided to easily obtain the hash values used by this monitoring tool. - * If any known hash parameters are passed to the checksum functions, note that it will override any past hash values stored in the log table when doing comparisons and completely re-evaluate the entire state. This is true even if done on a primary where the current state will then also be logged for comparison if it differs from the given hash. - */ - -DROP TABLE IF EXISTS monitor.pg_settings_checksum; -DROP TABLE IF EXISTS monitor.pg_hba_checksum; - -CREATE TABLE monitor.pg_settings_checksum ( - settings_hash_generated text NOT NULL - , settings_hash_known_provided text - , settings_string text NOT NULL - , created_at timestamptz DEFAULT now() NOT NULL - , valid smallint NOT NULL ); - -COMMENT ON COLUMN monitor.pg_settings_checksum.valid IS 'Set this column to zero if this group of settings is a valid change'; -CREATE INDEX ON monitor.pg_settings_checksum (created_at); - -CREATE TABLE monitor.pg_hba_checksum ( - hba_hash_generated text NOT NULL - , hba_hash_known_provided text - , hba_string text NOT NULL - , created_at timestamptz DEFAULT now() NOT NULL - , valid smallint NOT NULL ); - -COMMENT ON COLUMN monitor.pg_hba_checksum.valid IS 'Set this column to zero if this group of settings is a valid change'; -CREATE INDEX ON monitor.pg_hba_checksum (created_at); - - -DROP FUNCTION IF EXISTS monitor.pg_settings_checksum(text); -CREATE FUNCTION monitor.pg_settings_checksum(p_known_settings_hash text DEFAULT NULL) - RETURNS smallint - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO pg_catalog, pg_temp -AS $function$ -DECLARE - -v_is_in_recovery boolean; -v_settings_hash text; -v_settings_hash_old text; -v_settings_match smallint := 0; -v_settings_string text; -v_settings_string_old text; -v_valid smallint; - -BEGIN - -SELECT pg_is_in_recovery() INTO v_is_in_recovery; - -SELECT md5_hash - , settings_string -INTO v_settings_hash - , v_settings_string -FROM monitor.pg_settings_hash; - -SELECT settings_hash_generated, valid -INTO v_settings_hash_old, v_valid -FROM monitor.pg_settings_checksum -ORDER BY created_at DESC LIMIT 1; - -IF p_known_settings_hash IS NOT NULL THEN - v_settings_hash_old := p_known_settings_hash; - -- Do not base validity on the stored value if manual hash is given. - v_valid := 0; -END IF; - -IF (v_settings_hash_old IS NOT NULL) THEN - - IF (v_settings_hash != v_settings_hash_old) THEN - - v_valid := 1; - - IF v_is_in_recovery = false THEN - INSERT INTO monitor.pg_settings_checksum ( - settings_hash_generated - , settings_hash_known_provided - , settings_string - , valid) - VALUES ( - v_settings_hash - , p_known_settings_hash - , v_settings_string - , v_valid); - END IF; - END IF; - -ELSE - - v_valid := 0; - IF v_is_in_recovery = false THEN - INSERT INTO monitor.pg_settings_checksum ( - settings_hash_generated - , settings_hash_known_provided - , settings_string - , valid) - VALUES (v_settings_hash - , p_known_settings_hash - , v_settings_string - , v_valid); - END IF; - -END IF; - -RETURN v_valid; - -END -$function$; - - -DROP FUNCTION IF EXISTS monitor.pg_hba_checksum(text); -CREATE FUNCTION monitor.pg_hba_checksum(p_known_hba_hash text DEFAULT NULL) - RETURNS smallint - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO pg_catalog, pg_temp -AS $function$ -DECLARE - -v_hba_hash text; -v_hba_hash_old text; -v_hba_match smallint := 0; -v_hba_string text; -v_hba_string_old text; -v_is_in_recovery boolean; -v_valid smallint; - -BEGIN - -SELECT pg_is_in_recovery() INTO v_is_in_recovery; - -IF current_setting('server_version_num')::int >= 100000 THEN - - SELECT md5_hash - , hba_string - INTO v_hba_hash - , v_hba_string - FROM monitor.pg_hba_hash; - -ELSE - RAISE EXCEPTION 'pg_hba change monitoring unsupported in versions older than PostgreSQL 10'; -END IF; - -SELECT hba_hash_generated, valid -INTO v_hba_hash_old, v_valid -FROM monitor.pg_hba_checksum -ORDER BY created_at DESC LIMIT 1; - -IF p_known_hba_hash IS NOT NULL THEN - v_hba_hash_old := p_known_hba_hash; - -- Do not base validity on the stored value if manual hash is given. - v_valid := 0; -END IF; - -IF (v_hba_hash_old IS NOT NULL) THEN - - IF (v_hba_hash != v_hba_hash_old) THEN - - v_valid := 1; - - IF v_is_in_recovery = false THEN - INSERT INTO monitor.pg_hba_checksum ( - hba_hash_generated - , hba_hash_known_provided - , hba_string - , valid) - VALUES ( - v_hba_hash - , p_known_hba_hash - , v_hba_string - , v_valid); - END IF; - END IF; - -ELSE - - v_valid := 0; - IF v_is_in_recovery = false THEN - INSERT INTO monitor.pg_hba_checksum ( - hba_hash_generated - , hba_hash_known_provided - , hba_string - , valid) - VALUES (v_hba_hash - , p_known_hba_hash - , v_hba_string - , v_valid); - END IF; - -END IF; - -RETURN v_valid; - -END -$function$; - - -DROP FUNCTION IF EXISTS monitor.pg_settings_checksum_set_valid(); -/* - * This function provides quick, clear interface for resetting the checksum monitor to treat the currently detected configuration as valid after alerting on a change. Note that configuration history will be cleared. - */ -CREATE FUNCTION monitor.pg_settings_checksum_set_valid() RETURNS smallint - LANGUAGE sql -AS $function$ - -TRUNCATE monitor.pg_settings_checksum; - -SELECT monitor.pg_settings_checksum(); - -$function$; - - -DROP FUNCTION IF EXISTS monitor.pg_hba_checksum_set_valid(); -/* - * This function provides quick, clear interface for resetting the checksum monitor to treat the currently detected configuration as valid after alerting on a change. Note that configuration history will be cleared. - */ -CREATE FUNCTION monitor.pg_hba_checksum_set_valid() RETURNS smallint - LANGUAGE sql -AS $function$ - -TRUNCATE monitor.pg_hba_checksum; - -SELECT monitor.pg_hba_checksum(); - -$function$; - - -DROP VIEW IF EXISTS monitor.pg_settings_hash; -CREATE VIEW monitor.pg_settings_hash AS - WITH settings_ordered_list AS ( - SELECT name - , COALESCE(setting, '<>') AS setting - FROM pg_catalog.pg_settings - ORDER BY name, setting) - SELECT md5(string_agg(name||setting, ',')) AS md5_hash - , string_agg(name||setting, ',') AS settings_string - FROM settings_ordered_list; - - -DROP VIEW IF EXISTS monitor.pg_hba_hash; -CREATE VIEW monitor.pg_hba_hash AS - -- Order by line number so it's caught if no content is changed but the order of entries is changed - WITH hba_ordered_list AS ( - SELECT COALESCE(type, '<>') AS type - , array_to_string(COALESCE(database, ARRAY['<>']), ',') AS database - , array_to_string(COALESCE(user_name, ARRAY['<>']), ',') AS user_name - , COALESCE(address, '<>') AS address - , COALESCE(netmask, '<>') AS netmask - , COALESCE(auth_method, '<>') AS auth_method - , array_to_string(COALESCE(options, ARRAY['<>']), ',') AS options - FROM pg_catalog.pg_hba_file_rules - ORDER BY line_number) - SELECT md5(string_agg(type||database||user_name||address||netmask||auth_method||options, ',')) AS md5_hash - , string_agg(type||database||user_name||address||netmask||auth_method||options, ',') AS hba_string - FROM hba_ordered_list; - - - -DROP TABLE IF EXISTS monitor.pg_stat_statements_reset_info; --- Table to store last reset time for pg_stat_statements -CREATE TABLE monitor.pg_stat_statements_reset_info( - reset_time timestamptz -); - -DROP FUNCTION IF EXISTS monitor.pg_stat_statements_reset_info(int); --- Function to reset pg_stat_statements periodically -CREATE FUNCTION monitor.pg_stat_statements_reset_info(p_throttle_minutes integer DEFAULT 1440) - RETURNS bigint - LANGUAGE plpgsql - SECURITY DEFINER - SET search_path TO pg_catalog, pg_temp -AS $function$ -DECLARE - - v_reset_timestamp timestamptz; - v_throttle interval; - -BEGIN - - IF p_throttle_minutes < 0 THEN - RETURN 0; - END IF; - - v_throttle := make_interval(mins := p_throttle_minutes); - - SELECT COALESCE(max(reset_time), '1970-01-01'::timestamptz) INTO v_reset_timestamp FROM monitor.pg_stat_statements_reset_info; - - IF ((CURRENT_TIMESTAMP - v_reset_timestamp) > v_throttle) THEN - -- Ensure table is empty - DELETE FROM monitor.pg_stat_statements_reset_info; - PERFORM pg_stat_statements_reset(); - INSERT INTO monitor.pg_stat_statements_reset_info(reset_time) values (now()); - END IF; - - RETURN (SELECT extract(epoch from reset_time) FROM monitor.pg_stat_statements_reset_info); - -EXCEPTION - WHEN others then - RETURN 0; -END -$function$; - -GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA monitor TO ccp_monitoring; -GRANT ALL ON ALL TABLES IN SCHEMA monitor TO ccp_monitoring; diff --git a/postgres_exporter/common/pg17/setup.sql b/postgres_exporter/common/pg17/setup.sql deleted file mode 100644 index 4c9752c1..00000000 --- a/postgres_exporter/common/pg17/setup.sql +++ /dev/null @@ -1,463 +0,0 @@ --- PG17 pgMonitor Setup --- --- Copyright © 2017-2025 Crunchy Data Solutions, Inc. All Rights Reserved. --- - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'ccp_monitoring') THEN - CREATE ROLE ccp_monitoring WITH LOGIN; - END IF; - - -- The pgmonitor role is required by the pgnodemx extension in PostgreSQL versions 9.5 and 9.6 - -- and should be removed when upgrading to PostgreSQL 10 and above. - IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'pgmonitor') THEN - DROP ROLE pgmonitor; - END IF; -END -$$; - -GRANT pg_monitor to ccp_monitoring; -GRANT pg_execute_server_program TO ccp_monitoring; - -ALTER ROLE ccp_monitoring SET lock_timeout TO '2min'; -ALTER ROLE ccp_monitoring SET jit TO 'off'; - -CREATE SCHEMA IF NOT EXISTS monitor AUTHORIZATION ccp_monitoring; - -DROP TABLE IF EXISTS monitor.pgbackrest_info CASCADE; -CREATE TABLE IF NOT EXISTS monitor.pgbackrest_info (config_file text NOT NULL, data jsonb NOT NULL, gather_timestamp timestamptz DEFAULT now() NOT NULL); --- Force more aggressive autovacuum to avoid table bloat over time -ALTER TABLE monitor.pgbackrest_info SET (autovacuum_analyze_scale_factor = 0, autovacuum_vacuum_scale_factor = 0, autovacuum_vacuum_threshold = 10, autovacuum_analyze_threshold = 10); - -DROP FUNCTION IF EXISTS monitor.pgbackrest_info(); -- old version from 2.3 -DROP FUNCTION IF EXISTS monitor.pgbackrest_info(int); -CREATE OR REPLACE FUNCTION monitor.pgbackrest_info(p_throttle_minutes int DEFAULT 10) RETURNS SETOF monitor.pgbackrest_info - LANGUAGE plpgsql - SET search_path TO pg_catalog, pg_temp -AS $function$ -DECLARE - -v_gather_timestamp timestamptz; -v_throttle interval; -v_system_identifier bigint; - -BEGIN --- Get pgBackRest info in JSON format - -v_throttle := make_interval(mins := p_throttle_minutes); - -SELECT COALESCE(max(gather_timestamp), '1970-01-01'::timestamptz) INTO v_gather_timestamp FROM monitor.pgbackrest_info; - -IF pg_catalog.pg_is_in_recovery() = 'f' THEN - IF ((CURRENT_TIMESTAMP - v_gather_timestamp) > v_throttle) THEN - - -- Ensure table is empty - DELETE FROM monitor.pgbackrest_info; - - SELECT system_identifier into v_system_identifier FROM pg_control_system(); - - -- Copy data into the table directory from the pgBackRest into command - EXECUTE format( $cmd$ COPY monitor.pgbackrest_info (config_file, data) FROM program '/usr/bin/pgbackrest-info.sh %s' WITH (format text,DELIMITER '|') $cmd$, v_system_identifier::text ); - - END IF; -END IF; - -RETURN QUERY SELECT * FROM monitor.pgbackrest_info; - -IF NOT FOUND THEN - RAISE EXCEPTION 'No backups being returned from pgbackrest info command'; -END IF; - -END -$function$; - - -DROP FUNCTION IF EXISTS monitor.sequence_status(); -CREATE FUNCTION monitor.sequence_status() RETURNS TABLE (sequence_name text, last_value bigint, slots numeric, used numeric, percent int, cycle boolean, numleft numeric, table_usage text) - LANGUAGE sql SECURITY DEFINER STABLE - SET search_path TO pg_catalog, pg_temp -AS $function$ - -/* - * Provide detailed status information of sequences in the current database - */ - -WITH default_value_sequences AS ( - -- Get sequences defined as default values with related table - -- Note this subquery can be locked/hung by DDL that affects tables with sequences. - -- Use monitor.sequence_exhaustion() to actually monitor for sequences running out - SELECT s.seqrelid, c.oid - FROM pg_catalog.pg_attribute a - JOIN pg_catalog.pg_attrdef ad on (ad.adrelid,ad.adnum) = (a.attrelid,a.attnum) - JOIN pg_catalog.pg_class c on a.attrelid = c.oid - JOIN pg_catalog.pg_sequence s ON s.seqrelid = regexp_replace(pg_get_expr(ad.adbin,ad.adrelid), $re$^nextval\('(.+?)'::regclass\)$$re$, $re$\1$re$)::regclass - WHERE (pg_get_expr(ad.adbin,ad.adrelid)) ~ '^nextval\(' -), dep_sequences AS ( - -- Get sequences set as dependencies with related tables (identities) - SELECT s.seqrelid, c.oid - FROM pg_catalog.pg_sequence s - JOIN pg_catalog.pg_depend d ON s.seqrelid = d.objid - JOIN pg_catalog.pg_class c ON d.refobjid = c.oid - UNION - SELECT seqrelid, oid FROM default_value_sequences -), all_sequences AS ( - -- Get any remaining sequences - SELECT s.seqrelid AS sequence_oid, ds.oid AS table_oid - FROM pg_catalog.pg_sequence s - LEFT JOIN dep_sequences ds ON s.seqrelid = ds.seqrelid -) -SELECT sequence_name - , last_value - , slots - , used - , ROUND(used/slots*100)::int AS percent - , cycle - , CASE WHEN slots < used THEN 0 ELSE slots - used END AS numleft - , table_usage -FROM ( - SELECT format('%I.%I',s.schemaname, s.sequencename)::text AS sequence_name - , COALESCE(s.last_value,s.min_value) AS last_value - , s.cycle - , CEIL((s.max_value-min_value::NUMERIC+1)/s.increment_by::NUMERIC) AS slots - , CEIL((COALESCE(s.last_value,s.min_value)-s.min_value::NUMERIC+1)/s.increment_by::NUMERIC) AS used - , string_agg(a.table_oid::regclass::text, ', ') AS table_usage - FROM pg_catalog.pg_sequences s - JOIN all_sequences a ON (format('%I.%I', s.schemaname, s.sequencename))::regclass = a.sequence_oid - GROUP BY 1,2,3,4,5 -) x -ORDER BY ROUND(used/slots*100) DESC - -$function$; - - -DROP FUNCTION IF EXISTS monitor.sequence_exhaustion(int); -CREATE FUNCTION monitor.sequence_exhaustion(p_percent integer DEFAULT 75, OUT count bigint) - LANGUAGE sql SECURITY DEFINER STABLE - SET search_path TO pg_catalog, pg_temp -AS $function$ - -/* - * Returns count of sequences that have used up the % value given via the p_percent parameter (default 75%) - */ - -SELECT count(*) AS count -FROM ( - SELECT CEIL((s.max_value-min_value::NUMERIC+1)/s.increment_by::NUMERIC) AS slots - , CEIL((COALESCE(s.last_value,s.min_value)-s.min_value::NUMERIC+1)/s.increment_by::NUMERIC) AS used - FROM pg_catalog.pg_sequences s -) x -WHERE (ROUND(used/slots*100)::int) > p_percent; - -$function$; - -/* - * Tables and functions for monitoring changes to pg_settings and pg_hba_file_rules system catalogs. - * Can't just do a raw check for the hash value since Prometheus only records numeric values for alerts - * Tables allow recording of existing settings so they can be referred back to to see what changed - * If either checksum function returns 0, then NO settings have changed - * If either checksum function returns 1, then something has changed since last known valid state - * For replicas, logging past settings is not possible to compare what may have changed - * For replicas, by default, it is expected that its settings will match the primary - * For replicas, if the pg_settings or pg_hba.conf are necessarily different from the primary, a known good hash of that replica's - settings can be sent as an argument to the relevant checksum function. Views are provided to easily obtain the hash values used by this monitoring tool. - * If any known hash parameters are passed to the checksum functions, note that it will override any past hash values stored in the log table when doing comparisons and completely re-evaluate the entire state. This is true even if done on a primary where the current state will then also be logged for comparison if it differs from the given hash. - */ - -DROP TABLE IF EXISTS monitor.pg_settings_checksum; -DROP TABLE IF EXISTS monitor.pg_hba_checksum; - -CREATE TABLE monitor.pg_settings_checksum ( - settings_hash_generated text NOT NULL - , settings_hash_known_provided text - , settings_string text NOT NULL - , created_at timestamptz DEFAULT now() NOT NULL - , valid smallint NOT NULL ); - -COMMENT ON COLUMN monitor.pg_settings_checksum.valid IS 'Set this column to zero if this group of settings is a valid change'; -CREATE INDEX ON monitor.pg_settings_checksum (created_at); - -CREATE TABLE monitor.pg_hba_checksum ( - hba_hash_generated text NOT NULL - , hba_hash_known_provided text - , hba_string text NOT NULL - , created_at timestamptz DEFAULT now() NOT NULL - , valid smallint NOT NULL ); - -COMMENT ON COLUMN monitor.pg_hba_checksum.valid IS 'Set this column to zero if this group of settings is a valid change'; -CREATE INDEX ON monitor.pg_hba_checksum (created_at); - - -DROP FUNCTION IF EXISTS monitor.pg_settings_checksum(text); -CREATE FUNCTION monitor.pg_settings_checksum(p_known_settings_hash text DEFAULT NULL) - RETURNS smallint - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO pg_catalog, pg_temp -AS $function$ -DECLARE - -v_is_in_recovery boolean; -v_settings_hash text; -v_settings_hash_old text; -v_settings_match smallint := 0; -v_settings_string text; -v_settings_string_old text; -v_valid smallint; - -BEGIN - -SELECT pg_is_in_recovery() INTO v_is_in_recovery; - -SELECT md5_hash - , settings_string -INTO v_settings_hash - , v_settings_string -FROM monitor.pg_settings_hash; - -SELECT settings_hash_generated, valid -INTO v_settings_hash_old, v_valid -FROM monitor.pg_settings_checksum -ORDER BY created_at DESC LIMIT 1; - -IF p_known_settings_hash IS NOT NULL THEN - v_settings_hash_old := p_known_settings_hash; - -- Do not base validity on the stored value if manual hash is given. - v_valid := 0; -END IF; - -IF (v_settings_hash_old IS NOT NULL) THEN - - IF (v_settings_hash != v_settings_hash_old) THEN - - v_valid := 1; - - IF v_is_in_recovery = false THEN - INSERT INTO monitor.pg_settings_checksum ( - settings_hash_generated - , settings_hash_known_provided - , settings_string - , valid) - VALUES ( - v_settings_hash - , p_known_settings_hash - , v_settings_string - , v_valid); - END IF; - END IF; - -ELSE - - v_valid := 0; - IF v_is_in_recovery = false THEN - INSERT INTO monitor.pg_settings_checksum ( - settings_hash_generated - , settings_hash_known_provided - , settings_string - , valid) - VALUES (v_settings_hash - , p_known_settings_hash - , v_settings_string - , v_valid); - END IF; - -END IF; - -RETURN v_valid; - -END -$function$; - - -DROP FUNCTION IF EXISTS monitor.pg_hba_checksum(text); -CREATE FUNCTION monitor.pg_hba_checksum(p_known_hba_hash text DEFAULT NULL) - RETURNS smallint - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO pg_catalog, pg_temp -AS $function$ -DECLARE - -v_hba_hash text; -v_hba_hash_old text; -v_hba_match smallint := 0; -v_hba_string text; -v_hba_string_old text; -v_is_in_recovery boolean; -v_valid smallint; - -BEGIN - -SELECT pg_is_in_recovery() INTO v_is_in_recovery; - -IF current_setting('server_version_num')::int >= 100000 THEN - - SELECT md5_hash - , hba_string - INTO v_hba_hash - , v_hba_string - FROM monitor.pg_hba_hash; - -ELSE - RAISE EXCEPTION 'pg_hba change monitoring unsupported in versions older than PostgreSQL 10'; -END IF; - -SELECT hba_hash_generated, valid -INTO v_hba_hash_old, v_valid -FROM monitor.pg_hba_checksum -ORDER BY created_at DESC LIMIT 1; - -IF p_known_hba_hash IS NOT NULL THEN - v_hba_hash_old := p_known_hba_hash; - -- Do not base validity on the stored value if manual hash is given. - v_valid := 0; -END IF; - -IF (v_hba_hash_old IS NOT NULL) THEN - - IF (v_hba_hash != v_hba_hash_old) THEN - - v_valid := 1; - - IF v_is_in_recovery = false THEN - INSERT INTO monitor.pg_hba_checksum ( - hba_hash_generated - , hba_hash_known_provided - , hba_string - , valid) - VALUES ( - v_hba_hash - , p_known_hba_hash - , v_hba_string - , v_valid); - END IF; - END IF; - -ELSE - - v_valid := 0; - IF v_is_in_recovery = false THEN - INSERT INTO monitor.pg_hba_checksum ( - hba_hash_generated - , hba_hash_known_provided - , hba_string - , valid) - VALUES (v_hba_hash - , p_known_hba_hash - , v_hba_string - , v_valid); - END IF; - -END IF; - -RETURN v_valid; - -END -$function$; - - -DROP FUNCTION IF EXISTS monitor.pg_settings_checksum_set_valid(); -/* - * This function provides quick, clear interface for resetting the checksum monitor to treat the currently detected configuration as valid after alerting on a change. Note that configuration history will be cleared. - */ -CREATE FUNCTION monitor.pg_settings_checksum_set_valid() RETURNS smallint - LANGUAGE sql -AS $function$ - -TRUNCATE monitor.pg_settings_checksum; - -SELECT monitor.pg_settings_checksum(); - -$function$; - - -DROP FUNCTION IF EXISTS monitor.pg_hba_checksum_set_valid(); -/* - * This function provides quick, clear interface for resetting the checksum monitor to treat the currently detected configuration as valid after alerting on a change. Note that configuration history will be cleared. - */ -CREATE FUNCTION monitor.pg_hba_checksum_set_valid() RETURNS smallint - LANGUAGE sql -AS $function$ - -TRUNCATE monitor.pg_hba_checksum; - -SELECT monitor.pg_hba_checksum(); - -$function$; - - -DROP VIEW IF EXISTS monitor.pg_settings_hash; -CREATE VIEW monitor.pg_settings_hash AS - WITH settings_ordered_list AS ( - SELECT name - , COALESCE(setting, '<>') AS setting - FROM pg_catalog.pg_settings - ORDER BY name, setting) - SELECT md5(string_agg(name||setting, ',')) AS md5_hash - , string_agg(name||setting, ',') AS settings_string - FROM settings_ordered_list; - - -DROP VIEW IF EXISTS monitor.pg_hba_hash; -CREATE VIEW monitor.pg_hba_hash AS - -- Order by line number so it's caught if no content is changed but the order of entries is changed - WITH hba_ordered_list AS ( - SELECT COALESCE(type, '<>') AS type - , array_to_string(COALESCE(database, ARRAY['<>']), ',') AS database - , array_to_string(COALESCE(user_name, ARRAY['<>']), ',') AS user_name - , COALESCE(address, '<>') AS address - , COALESCE(netmask, '<>') AS netmask - , COALESCE(auth_method, '<>') AS auth_method - , array_to_string(COALESCE(options, ARRAY['<>']), ',') AS options - FROM pg_catalog.pg_hba_file_rules - ORDER BY line_number) - SELECT md5(string_agg(type||database||user_name||address||netmask||auth_method||options, ',')) AS md5_hash - , string_agg(type||database||user_name||address||netmask||auth_method||options, ',') AS hba_string - FROM hba_ordered_list; - - - -DROP TABLE IF EXISTS monitor.pg_stat_statements_reset_info; --- Table to store last reset time for pg_stat_statements -CREATE TABLE monitor.pg_stat_statements_reset_info( - reset_time timestamptz -); - -DROP FUNCTION IF EXISTS monitor.pg_stat_statements_reset_info(int); --- Function to reset pg_stat_statements periodically -CREATE FUNCTION monitor.pg_stat_statements_reset_info(p_throttle_minutes integer DEFAULT 1440) - RETURNS bigint - LANGUAGE plpgsql - SECURITY DEFINER - SET search_path TO pg_catalog, pg_temp -AS $function$ -DECLARE - - v_reset_timestamp timestamptz; - v_throttle interval; - -BEGIN - - IF p_throttle_minutes < 0 THEN - RETURN 0; - END IF; - - v_throttle := make_interval(mins := p_throttle_minutes); - - SELECT COALESCE(max(reset_time), '1970-01-01'::timestamptz) INTO v_reset_timestamp FROM monitor.pg_stat_statements_reset_info; - - IF ((CURRENT_TIMESTAMP - v_reset_timestamp) > v_throttle) THEN - -- Ensure table is empty - DELETE FROM monitor.pg_stat_statements_reset_info; - PERFORM pg_stat_statements_reset(); - INSERT INTO monitor.pg_stat_statements_reset_info(reset_time) values (now()); - END IF; - - RETURN (SELECT extract(epoch from reset_time) FROM monitor.pg_stat_statements_reset_info); - -EXCEPTION - WHEN others then - RETURN 0; -END -$function$; - -GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA monitor TO ccp_monitoring; -GRANT ALL ON ALL TABLES IN SCHEMA monitor TO ccp_monitoring; diff --git a/postgres_exporter/common/queries_global.yml b/postgres_exporter/common/queries_global.yml index a393049f..146b0f2a 100644 --- a/postgres_exporter/common/queries_global.yml +++ b/postgres_exporter/common/queries_global.yml @@ -177,14 +177,6 @@ ccp_postmaster_uptime: description: "Time interval in seconds since PostgreSQL database was last restarted" -ccp_pg_settings_checksum: - query: "SELECT monitor.pg_settings_checksum() AS status" - metrics: - - status: - usage: "GAUGE" - description: "Value of checksum monitioring status for pg_catalog.pg_settings (postgresql.conf). 0 = valid config. 1 = settings changed. To reset current config to valid after alert, run monitor.pg_settings_checksum_set_valid()." - - ccp_settings_pending_restart: query: "SELECT count(*) AS count FROM pg_catalog.pg_settings WHERE pending_restart = true" metrics: diff --git a/postgres_exporter/common/pg12/setup.sql b/postgres_exporter/common/setup.sql similarity index 76% rename from postgres_exporter/common/pg12/setup.sql rename to postgres_exporter/common/setup.sql index c218a355..d4428b18 100644 --- a/postgres_exporter/common/pg12/setup.sql +++ b/postgres_exporter/common/setup.sql @@ -1,4 +1,4 @@ --- PG12 pgMonitor Setup +-- pgMonitor Setup -- -- Copyright © 2017-2025 Crunchy Data Solutions, Inc. All Rights Reserved. -- @@ -152,30 +152,26 @@ WHERE (ROUND(used/slots*100)::int) > p_percent; $function$; /* - * Tables and functions for monitoring changes to pg_settings and pg_hba_file_rules system catalogs. + * Tables and functions for monitoring changes to pg_hba_file_rules system catalogs. * Can't just do a raw check for the hash value since Prometheus only records numeric values for alerts * Tables allow recording of existing settings so they can be referred back to to see what changed - * If either checksum function returns 0, then NO settings have changed - * If either checksum function returns 1, then something has changed since last known valid state + * If checksum function returns 0, then NO settings have changed + * If checksum function returns 1, then something has changed since last known valid state * For replicas, logging past settings is not possible to compare what may have changed * For replicas, by default, it is expected that its settings will match the primary - * For replicas, if the pg_settings or pg_hba.conf are necessarily different from the primary, a known good hash of that replica's - settings can be sent as an argument to the relevant checksum function. Views are provided to easily obtain the hash values used by this monitoring tool. + * For replicas, if the pg_hba.conf is necessarily different from the primary, a known good hash of that replica's + settings can be sent as an argument to the checksum function. Views are provided to easily obtain the hash values used by this monitoring tool. * If any known hash parameters are passed to the checksum functions, note that it will override any past hash values stored in the log table when doing comparisons and completely re-evaluate the entire state. This is true even if done on a primary where the current state will then also be logged for comparison if it differs from the given hash. */ +-- These objects were dropped in 5.3.0. Make sure they're gone DROP TABLE IF EXISTS monitor.pg_settings_checksum; -DROP TABLE IF EXISTS monitor.pg_hba_checksum; +DROP FUNCTION IF EXISTS monitor.pg_settings_checksum(text); +DROP FUNCTION IF EXISTS monitor.pg_settings_checksum_set_valid(); +DROP VIEW IF EXISTS monitor.pg_settings_hash; -CREATE TABLE monitor.pg_settings_checksum ( - settings_hash_generated text NOT NULL - , settings_hash_known_provided text - , settings_string text NOT NULL - , created_at timestamptz DEFAULT now() NOT NULL - , valid smallint NOT NULL ); -COMMENT ON COLUMN monitor.pg_settings_checksum.valid IS 'Set this column to zero if this group of settings is a valid change'; -CREATE INDEX ON monitor.pg_settings_checksum (created_at); +DROP TABLE IF EXISTS monitor.pg_hba_checksum; CREATE TABLE monitor.pg_hba_checksum ( hba_hash_generated text NOT NULL @@ -187,87 +183,6 @@ CREATE TABLE monitor.pg_hba_checksum ( COMMENT ON COLUMN monitor.pg_hba_checksum.valid IS 'Set this column to zero if this group of settings is a valid change'; CREATE INDEX ON monitor.pg_hba_checksum (created_at); - -DROP FUNCTION IF EXISTS monitor.pg_settings_checksum(text); -CREATE FUNCTION monitor.pg_settings_checksum(p_known_settings_hash text DEFAULT NULL) - RETURNS smallint - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO pg_catalog, pg_temp -AS $function$ -DECLARE - -v_is_in_recovery boolean; -v_settings_hash text; -v_settings_hash_old text; -v_settings_match smallint := 0; -v_settings_string text; -v_settings_string_old text; -v_valid smallint; - -BEGIN - -SELECT pg_is_in_recovery() INTO v_is_in_recovery; - -SELECT md5_hash - , settings_string -INTO v_settings_hash - , v_settings_string -FROM monitor.pg_settings_hash; - -SELECT settings_hash_generated, valid -INTO v_settings_hash_old, v_valid -FROM monitor.pg_settings_checksum -ORDER BY created_at DESC LIMIT 1; - -IF p_known_settings_hash IS NOT NULL THEN - v_settings_hash_old := p_known_settings_hash; - -- Do not base validity on the stored value if manual hash is given. - v_valid := 0; -END IF; - -IF (v_settings_hash_old IS NOT NULL) THEN - - IF (v_settings_hash != v_settings_hash_old) THEN - - v_valid := 1; - - IF v_is_in_recovery = false THEN - INSERT INTO monitor.pg_settings_checksum ( - settings_hash_generated - , settings_hash_known_provided - , settings_string - , valid) - VALUES ( - v_settings_hash - , p_known_settings_hash - , v_settings_string - , v_valid); - END IF; - END IF; - -ELSE - - v_valid := 0; - IF v_is_in_recovery = false THEN - INSERT INTO monitor.pg_settings_checksum ( - settings_hash_generated - , settings_hash_known_provided - , settings_string - , valid) - VALUES (v_settings_hash - , p_known_settings_hash - , v_settings_string - , v_valid); - END IF; - -END IF; - -RETURN v_valid; - -END -$function$; - - DROP FUNCTION IF EXISTS monitor.pg_hba_checksum(text); CREATE FUNCTION monitor.pg_hba_checksum(p_known_hba_hash text DEFAULT NULL) RETURNS smallint @@ -354,21 +269,6 @@ END $function$; -DROP FUNCTION IF EXISTS monitor.pg_settings_checksum_set_valid(); -/* - * This function provides quick, clear interface for resetting the checksum monitor to treat the currently detected configuration as valid after alerting on a change. Note that configuration history will be cleared. - */ -CREATE FUNCTION monitor.pg_settings_checksum_set_valid() RETURNS smallint - LANGUAGE sql -AS $function$ - -TRUNCATE monitor.pg_settings_checksum; - -SELECT monitor.pg_settings_checksum(); - -$function$; - - DROP FUNCTION IF EXISTS monitor.pg_hba_checksum_set_valid(); /* * This function provides quick, clear interface for resetting the checksum monitor to treat the currently detected configuration as valid after alerting on a change. Note that configuration history will be cleared. @@ -384,18 +284,6 @@ SELECT monitor.pg_hba_checksum(); $function$; -DROP VIEW IF EXISTS monitor.pg_settings_hash; -CREATE VIEW monitor.pg_settings_hash AS - WITH settings_ordered_list AS ( - SELECT name - , COALESCE(setting, '<>') AS setting - FROM pg_catalog.pg_settings - ORDER BY name, setting) - SELECT md5(string_agg(name||setting, ',')) AS md5_hash - , string_agg(name||setting, ',') AS settings_string - FROM settings_ordered_list; - - DROP VIEW IF EXISTS monitor.pg_hba_hash; CREATE VIEW monitor.pg_hba_hash AS -- Order by line number so it's caught if no content is changed but the order of entries is changed diff --git a/sql_exporter/common/crunchy_global_collector.yml b/sql_exporter/common/crunchy_global_collector.yml index 69f952d3..41bad1a6 100644 --- a/sql_exporter/common/crunchy_global_collector.yml +++ b/sql_exporter/common/crunchy_global_collector.yml @@ -98,13 +98,6 @@ metrics: query_ref: ccp_pg_hba_checksum - - metric_name: ccp_pg_settings_checksum_status - type: gauge - help: "Value of checksum monitioring status for pg_catalog.pg_settings (postgresql.conf). 0 = valid config. 1 = settings changed. To reset current config to valid after alert, run pgmonitor_ext.pg_settings_checksum_set_valid()." - values: [status] - query_ref: ccp_pg_settings_checksum - - - metric_name: ccp_postgresql_version_current type: gauge help: "The current version of PostgreSQL that this exporter is running on as a 6 digit integer (######)." @@ -475,16 +468,12 @@ queries: SELECT pgmonitor_ext.pg_hba_checksum() AS status - - query_name: ccp_pg_settings_checksum - query: | - SELECT pgmonitor_ext.pg_settings_checksum() AS status - # The version case here is to check for the minimum version of the pgmonitor-extension required by this version of pgMonitor -# As of Feb 2025, this is 2.1.0 or 20100. 0 for true, 1 for false +# As of July 2025, this is 2.2.0 or 20200. 0 for true, 1 for false - query_name: ccp_pgmonitor_extension_global query: | SELECT split_part(extversion, '.', 1) || lpad(split_part(extversion, '.', 2), 2, '0') || lpad(split_part(extversion, '.', 3), 2, '0') AS version - , CASE WHEN (split_part(extversion, '.', 1) || lpad(split_part(extversion, '.', 2), 2, '0') || lpad(split_part(extversion, '.', 3), 2, '0'))::int >= 20100 THEN + , CASE WHEN (split_part(extversion, '.', 1) || lpad(split_part(extversion, '.', 2), 2, '0') || lpad(split_part(extversion, '.', 3), 2, '0'))::int >= 20200 THEN 0 ELSE 1 END AS min_version_installed FROM pg_catalog.pg_extension