From 43fdee9e281c80d9da9794a79a109cb2ab7128d7 Mon Sep 17 00:00:00 2001 From: Mohamed Ali <75553212+mmohali@users.noreply.github.com> Date: Mon, 9 Sep 2024 15:08:23 -0700 Subject: [PATCH] pg-collector V1.1 for postgresql 16 --- CHANGELOG.md | 9 + README.md | 12 + pg_collector.sql | 799 +++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 793 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6dd5df..cc7a1d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,3 +11,12 @@ All notable changes to pg collector will be documented in this file. 3- Add reserved_connections parameter to Reserved connections settings. ``` +# V1.1 + +``` +1- The script should display the message "Report Generated Successfully" upon successful completion and relocate the report file name and location information to the end of the script.. +2- Add a new section for Amazon Aurora PostgreSQL. +3- Add a new section for Invalid databases. +4- Implement a check for the pg_stat_statements extension. If the extension is not installed, the script should print the message "pg_stat_statements extension is not installed" in the report, instead of displaying the errors "ERROR: relation "pg_stat_statements_info" does not exist" or "ERROR: relation "pg_stat_statements" does not exist". +5- Add a new section "List of role grants" under "Users & Roles Info". +``` \ No newline at end of file diff --git a/README.md b/README.md index 82596d4..53054d7 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,18 @@ postgres=> ``` +3- It is acceptable to observe the following errors while executing the pg_collector.sql script on Amazon Aurora PostgreSQL if the Cluster Cache Manager is disabled. + +``` +postgres=> \i pg_collector.sql +Output format is html. +psql:/tmp/pg_collector.sql:2766: ERROR: Cluster Cache Manager is disabled +psql:/tmp/pg_collector.sql:2769: ERROR: Cluster Cache Manager is disabled +Report Generated Successfully +Report name and location: /tmp/pg_collector_postgres-2024-09-09_161216.html + +``` + # License This library is licensed under the MIT-0 License. See the LICENSE file. \ No newline at end of file diff --git a/pg_collector.sql b/pg_collector.sql index c8d8e06..cf2cb94 100644 --- a/pg_collector.sql +++ b/pg_collector.sql @@ -4,14 +4,13 @@ -- | -- Create Date : 16 SEPT 2019 | -- | -- Description : Script to Collect PostgreSQL Database Informations | -- | and generate HTML Report | --- | -- version : V1 for PostgreSQL 16 | +-- | -- version : V1.1 for PostgreSQL 16 | -- | -- Changelog : https://github.com/awslabs/pg-collector/blob/pg-collector-for-postgresql-16/CHANGELOG.md | -- | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | -- | SPDX-License-Identifier: MIT-0 | -- +-------------------------------------------------------------------------------------------------------------+ \H \set filename :DBNAME-`date +%Y-%m-%d_%H%M%S` -\echo Report name and location: /tmp/pg_collector_:filename.html \o /tmp/pg_collector_:filename.html \pset footer off \qecho -\qecho

PG COLLECTOR V1 for PostgreSQL 16

+\qecho

PG COLLECTOR V1.1 for PostgreSQL 16

\qecho For more information about PG Collector, visit the project github repository
\qecho DB INFO
\qecho
@@ -170,6 +169,36 @@ select * from pg_database; \qecho Triggers \qecho pg_config \qecho COPY command progress +\qecho Invalid databases +\qecho +\qecho +\qecho
+\qecho
+\qecho
+\qecho +\qecho +\qecho +\qecho +\qecho +\qecho +\qecho +\qecho +\qecho +\qecho +\qecho +\qecho +\qecho +\qecho +\qecho +\qecho +\qecho +\qecho +\qecho +\qecho +\qecho +\qecho +\qecho +\qecho \qecho \qecho \qecho
Amazon Aurora PostgreSQL
Aurora versionAurora PostgreSQL built-in functionsAurora db instance identifierAurora cluster instances
Aurora reader instances - Replica LagAurora cluster cache management (CCM)Aurora global db statusAurora wait event stat
Query Plan Management (QPM)Aurora DML activityprocess memory context usageAurora_stat_statements
Aurora_stat_plansLogical replication write-through cache************
@@ -270,6 +299,13 @@ from pg_replication_slots where active = true order by age(xmin) desc; +\qecho

Invalid databases count:

+select count(*) FROM pg_database WHERE datconnlimit = '-2' ; + + +\qecho

Invalid databases list:

+SELECT * FROM pg_database WHERE datconnlimit = '-2' ; + \qecho

Orphaned prepared transactions:

@@ -339,16 +375,22 @@ ORDER BY age(relfrozenxid) DESC ; \qecho

autovacuum progress per day:

+\qecho
+\qecho

Note:

+\qecho

This section presents the number of tables that have been vacuumed by the autovacuum , grouped by the date (in the format YYYY-MM-DD) of the last_autovacuum column.

+\qecho

If the value in the date column is NULL, it indicates that the corresponding value in the table count column represents the number of tables that the autovacuum did not vacuum.

+\qecho
-select to_char(last_autovacuum, 'YYYY-MM-DD') as date, -count(*) from pg_stat_all_tables -group by to_char(last_autovacuum, 'YYYY-MM-DD') order by 1; +select to_char(last_autovacuum, 'YYYY-MM-DD') as date , count(*) as table_count from pg_stat_all_tables group by to_char(last_autovacuum, 'YYYY-MM-DD') order by 1; - -\qecho

when the last autovacuum succeeded ?

-select relname as table_name,n_live_tup, n_tup_upd, n_tup_del,n_tup_ins, n_dead_tup, + +\qecho

The most recent 20 tables that have been vacuumed by the autovacuum:

+\qecho

Note:

+\qecho

- If the value in the last_autovacuum column is NULL, it indicates that the autovacuum did not vacuum this table.

+\qecho
+select schemaname as schema_name,relname as table_name,n_live_tup, n_tup_upd, n_tup_del, n_dead_tup, last_vacuum, last_autovacuum, last_analyze, last_autoanalyze from pg_stat_all_tables order by last_autovacuum desc limit 20 ; @@ -371,7 +413,7 @@ order by 2 desc limit 20; -\qecho

Indexs Inforamtion for Top-20 tables order by xid age:

+\qecho

Index Inforamtion for Top-20 tables order by xid age:

SELECT schemaname,relname AS tablename, indexrelname AS indexname, @@ -425,6 +467,8 @@ order by 2,8 desc; \qecho
\qecho

biggest 50 tables in the DB:

\qecho
+\qecho

Note:

+\qecho

- If the table has never yet been vacuumed or analyzed, ROW_ESTIMATE column (pg_class.reltuples) contains -1 indicating that the row count is unknown.

\qecho
SELECT *, pg_size_pretty(total_bytes) AS TOTAL_PRETTY , pg_size_pretty(index_bytes) AS INDEX_PRETTY @@ -471,7 +515,7 @@ FROM pg_catalog.pg_statio_all_indexes ORDER BY 1,2 desc ; \qecho
\qecho
SELECT -schemaname,relname as "Table", +schemaname as schema_name,relname as "Table", indexrelname AS indexname, pg_relation_size(indexrelid), pg_size_pretty(pg_relation_size(indexrelid)) AS index_size @@ -523,14 +567,22 @@ SELECT p.pid, now() - a.xact_start AS duration, coalesce(wait_event_type ||'.'|| \qecho
\qecho

Autovacuum progress per day:

\qecho
+\qecho

Note:

+\qecho

This section presents the number of tables that have been vacuumed by the autovacuum , grouped by the date (in the format YYYY-MM-DD) of the last_autovacuum column.

+\qecho

If the value in the date column is NULL, it indicates that the corresponding value in the table count column represents the number of tables that the autovacuum did not vacuum.

+\qecho
\qecho
-select to_char(last_autovacuum, 'YYYY-MM-DD') as date, count(*) from pg_stat_all_tables group by to_char(last_autovacuum, 'YYYY-MM-DD') order by 1; +select to_char(last_autovacuum, 'YYYY-MM-DD') as date , count(*) as table_count from pg_stat_all_tables group by to_char(last_autovacuum, 'YYYY-MM-DD') order by 1; \qecho
\qecho
\qecho

Autoanalyze progress per day:

\qecho
+\qecho

Note:

+\qecho

This section presents the number of tables that have been analyzed by the autoanalyze , grouped by the date (in the format YYYY-MM-DD) of the last_autoanalyze column.

+\qecho

If the value in the date column is NULL, it indicates that the corresponding value in the table count column represents the number of tables that the autoanalyze did not analyze.

+\qecho
\qecho
-select to_char(last_autoanalyze, 'YYYY-MM-DD') as date, count(*) from pg_stat_all_tables group by to_char(last_autoanalyze, 'YYYY-MM-DD') order by 1; +select to_char(last_autoanalyze, 'YYYY-MM-DD') as date , count(*) as table_count from pg_stat_all_tables group by to_char(last_autoanalyze, 'YYYY-MM-DD') order by 1; \qecho
\qecho
--Which tables are currently eligible for autovacuum based on curret parameters @@ -575,7 +627,7 @@ SELECT name, setting FROM pg_settings WHERE name='track_counts'; \qecho

Top 50 tables based on number of dead tuples:

\qecho
\qecho
-select relname,n_live_tup, n_tup_upd, n_tup_del, n_dead_tup, last_vacuum, last_autovacuum, last_analyze, last_autoanalyze from pg_stat_all_tables order by n_dead_tup desc limit 50; +select schemaname as schema_name,relname AS table_name,n_live_tup, n_tup_upd, n_tup_del, n_dead_tup, last_vacuum, last_autovacuum, last_analyze, last_autoanalyze from pg_stat_all_tables order by n_dead_tup desc limit 50; \qecho
\qecho
\qecho

Tables have more than 20% dead rows :

@@ -764,12 +816,14 @@ FROM \qecho \qecho pg_stat_statements extension
\qecho
+select count(*) > 0 is_pg_stat_statements_enabled FROM pg_catalog.pg_extension where extname = 'pg_stat_statements' \gset +\if :is_pg_stat_statements_enabled \qecho

pg_stat_statements installed version:

\qecho
\qecho
SELECT e.extname AS "Extension Name", e.extversion AS "Version", n.nspname AS "Schema",pg_get_userbyid(e.extowner) as Owner, c.description AS "Description" , e.extrelocatable as "relocatable to another schema", e.extconfig ,e.extcondition - FROM pg_catalog.pg_extension e LEFT JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace LEFT JOIN pg_catalog.pg_description c ON c.objoid = e.oid AND c.classoid = 'pg_catalog.pg_extension'::pg_catalog.regclass - where e.extname = 'pg_stat_statements'; +FROM pg_catalog.pg_extension e LEFT JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace LEFT JOIN pg_catalog.pg_description c ON c.objoid = e.oid AND c.classoid = 'pg_catalog.pg_extension'::pg_catalog.regclass +where e.extname = 'pg_stat_statements'; \qecho
\qecho

Parameters values:

-- pg_stat_statements extension configuration @@ -805,7 +859,6 @@ order by b.name , b.version ) e group by name ) as r where r.extension_name='pg_stat_statements'; -; \qecho
\qecho
\qecho

pg_stat_statements_info view:

@@ -905,6 +958,13 @@ shared_blks_read from pg_stat_statements order by shared_blks_read desc limit 20; \qecho
+\else + \if yes + \qecho 'pg_stat_statements extension is not installed' + \endif +\endif + + \qecho
[Top]

@@ -923,6 +983,21 @@ order by shared_blks_read desc limit 20; \drds \qecho
select * FROM pg_user; +\qecho
+\qecho

List of role grants:

+SELECT m.rolname AS "Role name", r.rolname AS "Member of", + pg_catalog.concat_ws(', ', + CASE WHEN pam.admin_option THEN 'ADMIN' END, + CASE WHEN pam.inherit_option THEN 'INHERIT' END, + CASE WHEN pam.set_option THEN 'SET' END + ) AS "Options", + g.rolname AS "Grantor" +FROM pg_catalog.pg_roles m + JOIN pg_catalog.pg_auth_members pam ON (pam.member = m.oid) + LEFT JOIN pg_catalog.pg_roles r ON (pam.roleid = r.oid) + LEFT JOIN pg_catalog.pg_roles g ON (pam.grantor = g.oid) +WHERE m.rolname !~ '^pg_' +ORDER BY 1, 2, 4; \qecho
@@ -1376,11 +1451,11 @@ order by physical_reads_percent desc limit 50 ; \qecho
SELECT current_database(), schemaname, tablename, /*reltuples::bigint, relpages::bigint, otta,*/ - ROUND((CASE WHEN otta=0 THEN 0.0 ELSE sml.relpages::FLOAT/otta END)::NUMERIC,1) AS "table_bloat_%", + ROUND((CASE WHEN otta=0 THEN 0.0 ELSE sml.relpages::FLOAT/otta END)::NUMERIC,1) AS "table_bloat_ratio", CASE WHEN relpages < otta THEN 0 ELSE bs*(sml.relpages-otta)::BIGINT END AS wastedbytes, pg_size_pretty(CASE WHEN relpages < otta THEN 0 ELSE bs*(sml.relpages-otta)::BIGINT END) AS table_wasted_size, iname AS Index_nam, /*ituples::bigint, ipages::bigint, iotta,*/ - ROUND((CASE WHEN iotta=0 OR ipages=0 THEN 0.0 ELSE ipages::FLOAT/iotta END)::NUMERIC,1) AS "Index_bloat_%", + ROUND((CASE WHEN iotta=0 OR ipages=0 THEN 0.0 ELSE ipages::FLOAT/iotta END)::NUMERIC,1) AS "Index_bloat_ratio", CASE WHEN ipages < iotta THEN 0 ELSE bs*(ipages-iotta) END AS wastedibytes, pg_size_pretty(CASE WHEN ipages < iotta THEN 0 ELSE bs*(ipages-iotta) ::BIGINT END) AS Index_wasted_size FROM ( @@ -1424,16 +1499,16 @@ ORDER BY wastedbytes DESC; \qecho
\qecho
-\qecho

Tables and indexes Bloat [Fragmentation] order by table wasted % :

+\qecho

Tables and indexes Bloat [Fragmentation] order by table wasted ratio :

\qecho
\qecho
SELECT current_database(), schemaname, tablename, /*reltuples::bigint, relpages::bigint, otta,*/ - ROUND((CASE WHEN otta=0 THEN 0.0 ELSE sml.relpages::FLOAT/otta END)::NUMERIC,1) AS "table_bloat_%", + ROUND((CASE WHEN otta=0 THEN 0.0 ELSE sml.relpages::FLOAT/otta END)::NUMERIC,1) AS "table_bloat_ratio", CASE WHEN relpages < otta THEN 0 ELSE bs*(sml.relpages-otta)::BIGINT END AS wastedbytes, pg_size_pretty(CASE WHEN relpages < otta THEN 0 ELSE bs*(sml.relpages-otta)::BIGINT END) AS table_wasted_size, iname AS Index_nam, /*ituples::bigint, ipages::bigint, iotta,*/ - ROUND((CASE WHEN iotta=0 OR ipages=0 THEN 0.0 ELSE ipages::FLOAT/iotta END)::NUMERIC,1) AS "Index_bloat_%", + ROUND((CASE WHEN iotta=0 OR ipages=0 THEN 0.0 ELSE ipages::FLOAT/iotta END)::NUMERIC,1) AS "Index_bloat_ratio", CASE WHEN ipages < iotta THEN 0 ELSE bs*(ipages-iotta) END AS wastedibytes, pg_size_pretty(CASE WHEN ipages < iotta THEN 0 ELSE bs*(ipages-iotta) ::BIGINT END) AS Index_wasted_size FROM ( @@ -1477,16 +1552,16 @@ ORDER BY 4 desc; \qecho
\qecho
-\qecho

Tables and indexes Bloat [Fragmentation] order by index wasted % :

+\qecho

Tables and indexes Bloat [Fragmentation] order by index wasted ratio :

\qecho
\qecho
SELECT current_database(), schemaname, tablename, /*reltuples::bigint, relpages::bigint, otta,*/ - ROUND((CASE WHEN otta=0 THEN 0.0 ELSE sml.relpages::FLOAT/otta END)::NUMERIC,1) AS "table_bloat_%", + ROUND((CASE WHEN otta=0 THEN 0.0 ELSE sml.relpages::FLOAT/otta END)::NUMERIC,1) AS "table_bloat_ratio", CASE WHEN relpages < otta THEN 0 ELSE bs*(sml.relpages-otta)::BIGINT END AS wastedbytes, pg_size_pretty(CASE WHEN relpages < otta THEN 0 ELSE bs*(sml.relpages-otta)::BIGINT END) AS table_wasted_size, iname AS Index_nam, /*ituples::bigint, ipages::bigint, iotta,*/ - ROUND((CASE WHEN iotta=0 OR ipages=0 THEN 0.0 ELSE ipages::FLOAT/iotta END)::NUMERIC,1) AS "Index_bloat_%", + ROUND((CASE WHEN iotta=0 OR ipages=0 THEN 0.0 ELSE ipages::FLOAT/iotta END)::NUMERIC,1) AS "Index_bloat_ratio", CASE WHEN ipages < iotta THEN 0 ELSE bs*(ipages-iotta) END AS wastedibytes, pg_size_pretty(CASE WHEN ipages < iotta THEN 0 ELSE bs*(ipages-iotta) ::BIGINT END) AS Index_wasted_size FROM ( @@ -2460,7 +2535,14 @@ select * from pg_stat_database; \qecho

pg_stat_database_conflicts view:

select * from pg_stat_database_conflicts; \qecho

pg_stat_wal view:

-SELECT * FROM pg_stat_wal; +select count(*) > 0 isaurora from pg_settings where name='rds.extensions' and setting like '%aurora_stat_utils%' \gset +\if :isaurora +\qecho 'pg_stat_wal is currently not supported for Aurora' +\else + \if yes + SELECT * FROM pg_stat_wal; + \endif +\endif \qecho
@@ -2559,7 +2641,14 @@ order by table_id , trigger_status_code; \qecho
\qecho

The view pg_config describes the compile-time configuration parameters of the currently installed version of PostgreSQL.

\qecho
-select * from pg_config(); +select count(*) > 0 isaurora from pg_settings where name='rds.extensions' and setting like '%aurora_stat_utils%' \gset +\if :isaurora +\qecho 'pg_config() is currently not supported for Aurora' +\else + \if yes + select * from pg_config(); + \endif +\endif \qecho \qecho
[Top]

@@ -2595,6 +2684,30 @@ SELECT * FROM pg_stat_progress_copy ; +-- +----------------------------------------------------------------------------+ +-- | - Invalid_databases - | +-- +----------------------------------------------------------------------------+ + + +\qecho +\qecho Invalid databases


+\qecho
+\qecho

If the DROP DATABASE command is interrupted, the database will become invalid,starting from versions 11.21 and later, 12.16 and later, 13.12 and later, 14.9 and later, 15.4 and later, all versions of 16 as well as PostgreSQL 16 and all subsequent major versions.

+\qecho

you will not be able to connect to it again. In this case, you will see the below error message:

+\qecho

failed: FATAL: cannot connect to invalid database

+\qecho

HINT: Use DROP DATABASE to drop invalid databases.

+\qecho
+\qecho
+\qecho

Invalid databases count:

+select count(*) FROM pg_database WHERE datconnlimit = '-2' ; + +\qecho
+\qecho

Invalid databases list:

+SELECT * FROM pg_database WHERE datconnlimit = '-2' ; +\qecho
+ +\qecho
[Top]

+ -- +----------------------------------------------------------------------------+ -- | - ************* - | -- +----------------------------------------------------------------------------+ @@ -2609,6 +2722,638 @@ SELECT * FROM pg_stat_progress_copy ; \qecho

[Top]

+-- +----------------------------------------------------------------------------+ +-- | - Aurora_version - | +-- +----------------------------------------------------------------------------+ + +\qecho +\qecho Aurora version


+\qecho
+\qecho
+select count(*) > 0 isaurora from pg_settings where name='rds.extensions' and setting like '%aurora_stat_utils%' \gset +\if :isaurora +SELECT * FROM aurora_version(); +\else + \if yes + \qecho 'This is not Aurora instance' + \endif +\endif +\qecho
+ +\qecho
[Top]

+ + + + +-- +----------------------------------------------------------------------------+ +-- | - Aurora_PostgreSQL_built-in_functions - | +-- +----------------------------------------------------------------------------+ + + +\qecho +\qecho Aurora PostgreSQL built-in functions


+\qecho
+\qecho
+select count(*) > 0 isaurora from pg_settings where name='rds.extensions' and setting like '%aurora_stat_utils%' \gset +\if :isaurora +SELECT * FROM aurora_list_builtins(); +\else + \if yes + \qecho 'This is not Aurora instance' + \endif +\endif +\qecho
+\qecho
[Top]

+ + +-- +----------------------------------------------------------------------------+ +-- | - Aurora_db_instance_identifier - | +-- +----------------------------------------------------------------------------+ + + +\qecho +\qecho Aurora db instance identifier


+\qecho
+\qecho
+select count(*) > 0 isaurora from pg_settings where name='rds.extensions' and setting like '%aurora_stat_utils%' \gset +\if :isaurora +SELECT server_id, + CASE + WHEN 'MASTER_SESSION_ID' = session_id THEN 'writer' + ELSE 'reader' + END AS instance_role +FROM aurora_replica_status() rt, + aurora_db_instance_identifier() di + WHERE rt.server_id = di; +\else + \if yes + \qecho 'This is not Aurora instance' + \endif +\endif +\qecho
+\qecho
[Top]

+ +-- +----------------------------------------------------------------------------+ +-- | - Aurora_cluster_instances - | +-- +----------------------------------------------------------------------------+ + +\qecho +\qecho Aurora cluster instances


+\qecho
+\qecho
+select count(*) > 0 isaurora from pg_settings where name='rds.extensions' and setting like '%aurora_stat_utils%' \gset +\if :isaurora + SELECT server_id, + CASE + WHEN 'MASTER_SESSION_ID' = session_id THEN 'writer' + ELSE 'reader' + END AS instance_role +FROM aurora_replica_status() ; +\else + \if yes + \qecho 'This is not Aurora instance' + \endif +\endif +\qecho
+\qecho
[Top]

+ +-- +----------------------------------------------------------------------------+ +-- | - Aurora_reader_instances-Replica_Lag - | +-- +----------------------------------------------------------------------------+ + + +\qecho +\qecho Aurora reader instances - Replica Lag


+\qecho
+\qecho
+select count(*) > 0 isaurora from pg_settings where name='rds.extensions' and setting like '%aurora_stat_utils%' \gset +\if :isaurora +SELECT server_id, + CASE + WHEN 'MASTER_SESSION_ID' = session_id THEN 'writer' + ELSE 'reader' + END AS instance_role, + replica_lag_in_msec AS replica_lag_ms, + round(extract (epoch FROM (SELECT age(clock_timestamp(), last_update_timestamp))) * 1000) AS last_update_age_ms +FROM aurora_replica_status() +ORDER BY replica_lag_in_msec NULLS FIRST; +\else + \if yes + \qecho 'This is not Aurora instance' + \endif +\endif +\qecho
+\qecho
[Top]

+ + + +-- +----------------------------------------------------------------------------+ +-- | - Aurora_cluster_cache_management_(CCM) - | +-- +----------------------------------------------------------------------------+ + + +\qecho +\qecho Aurora cluster cache management (CCM)


+\qecho
+\qecho
+select count(*) > 0 isaurora from pg_settings where name='rds.extensions' and setting like '%aurora_stat_utils%' \gset +\if :isaurora +SELECT * FROM aurora_ccm_status(); +SELECT buffers_sent_last_minute * 8/60 AS warm_rate_kbps, +100 * (1.0-buffers_sent_last_scan/buffers_found_last_scan) AS warm_percent +FROM aurora_ccm_status (); +\else + \if yes + \qecho 'This is not Aurora instance' + \endif +\endif +\qecho
+\qecho
[Top]

+ + + +-- +----------------------------------------------------------------------------+ +-- | - Aurora_global_db_status - | +-- +----------------------------------------------------------------------------+ + + +\qecho +\qecho Aurora global db status


+\qecho
+\qecho
+select count(*) > 0 isaurora from pg_settings where name='rds.extensions' and setting like '%aurora_stat_utils%' \gset +\if :isaurora +SELECT CASE + WHEN '-1' = durability_lag_in_msec THEN 'Primary' + ELSE 'Secondary' + END AS global_role, + * + FROM aurora_global_db_status(); +\else + \if yes + \qecho 'This is not Aurora instance' + \endif +\endif +\qecho
+\qecho
[Top]

+ +-- +----------------------------------------------------------------------------+ +-- | - Aurora_wait_event_stat - | +-- +----------------------------------------------------------------------------+ + + +\qecho +\qecho Aurora wait event stat


+\qecho
+\qecho

Note:

+\qecho

aurora_stat_system_waits()function returns the cumulative number of waits and cumulative wait time for each wait event generated by the DB instance that you are currently connected to.

+\qecho

Statistics returned by this function are reset when a DB instance restarts.

+\qecho

waits :The number of times the wait event occurred.

+\qecho

wait_time : The total amount of time in microseconds spent waiting for this event.

+\qecho
+\qecho
+select count(*) > 0 isaurora from pg_settings where name='rds.extensions' and setting like '%aurora_stat_utils%' \gset +\if :isaurora +SELECT type_name as wait_event_type, + event_name as wait_event_name, + waits, + wait_time + FROM aurora_stat_system_waits() +NATURAL JOIN aurora_stat_wait_event() +NATURAL JOIN aurora_stat_wait_type() order by wait_time desc; +\else + \if yes + \qecho 'This is not Aurora instance' + \endif +\endif +\qecho
+\qecho
[Top]

+ + +-- +----------------------------------------------------------------------------+ +-- | - query_plan_management - | +-- +----------------------------------------------------------------------------+ + +\qecho +\qecho Query Plan Management (QPM)


+\qecho
+\qecho
+select count(*) > 0 isaurora from pg_settings where name='rds.extensions' and setting like '%aurora_stat_utils%' \gset +select count(*) > 0 is_apg_plan_mgmt_enabled FROM pg_catalog.pg_extension where extname = 'apg_plan_mgmt' \gset +select count(*) = 0 is_apg_plan_mgmt_not_enabled FROM pg_catalog.pg_extension where extname = 'apg_plan_mgmt' \gset +\if :isaurora + \if :is_apg_plan_mgmt_not_enabled + \qecho 'Query Plan Management (QPM) is not enabled' + \else + \if :is_apg_plan_mgmt_enabled + show rds.enable_plan_management ; + SELECT e.extname AS "Extension Name", e.extversion AS "Version", n.nspname AS "Schema",pg_get_userbyid(e.extowner) as Owner, c.description AS "Description" , e.extrelocatable as "relocatable to another schema", e.extconfig ,e.extcondition + FROM pg_catalog.pg_extension e LEFT JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace LEFT JOIN pg_catalog.pg_description c ON c.objoid = e.oid AND c.classoid = 'pg_catalog.pg_extension'::pg_catalog.regclass + where e.extname = 'apg_plan_mgmt'; + select * from pg_available_extensions where name='apg_plan_mgmt'; + SELECT name ,version ,installed FROM pg_available_extension_versions where name='apg_plan_mgmt' order by version; + select name,setting from pg_settings where name like 'apg_plan_mgmt%'; + with plans_stored_count as (select count(*) as cnt from apg_plan_mgmt.dba_plans) + select s.setting as max_plans, p.cnt as plans_stored_count, (p.cnt/s.setting::int)*100 as plans_stored_PCT_from_max_plans from pg_settings s, plans_stored_count p where s.name ='apg_plan_mgmt.max_plans'; + \endif + \endif +\else + \if yes + \qecho 'This is not Aurora instance' + \endif +\endif +\qecho
+\qecho
[Top]

+ +-- +----------------------------------------------------------------------------+ +-- | - Aurora_dml_activity - | +-- +----------------------------------------------------------------------------+ +\qecho +\qecho Aurora dml activity


+\qecho
+\qecho
+select count(*) > 0 isaurora from pg_settings where name='rds.extensions' and setting like '%aurora_stat_utils%' \gset +\if :isaurora +with dml_details as ( +SELECT db.datname AS datname, + NULLIF(BTRIM(SPLIT_PART(db.asdmla::TEXT, ',', 1), '()'),'') AS select_count, + NULLIF(BTRIM(SPLIT_PART(db.asdmla::TEXT, ',', 2), '()'),'') AS select_latency_microsecs, + NULLIF(BTRIM(SPLIT_PART(db.asdmla::TEXT, ',', 3), '()'),'') AS insert_count, + NULLIF(BTRIM(SPLIT_PART(db.asdmla::TEXT, ',', 4), '()'),'') AS insert_latency_microsecs, + NULLIF(BTRIM(SPLIT_PART(db.asdmla::TEXT, ',', 5), '()'),'') AS update_count, + NULLIF(BTRIM(SPLIT_PART(db.asdmla::TEXT, ',', 6), '()'),'') AS update_latency_microsecs, + NULLIF(BTRIM(SPLIT_PART(db.asdmla::TEXT, ',', 7), '()'),'') AS delete_count, + NULLIF(BTRIM(SPLIT_PART(db.asdmla::TEXT, ',', 8), '()'),'') AS delete_latency_microsecs + FROM (SELECT datname, + aurora_stat_dml_activity(oid) AS asdmla + FROM pg_database --where datname = 'rathoran_db' + ) AS db) +select datname as database_name , + select_count::numeric, + select_latency_microsecs::numeric, + TRUNC(select_latency_microsecs::numeric/NULLIF(select_count::numeric,0),3) select_latency_per_exec, + insert_count::numeric, + insert_latency_microsecs::numeric, + TRUNC(insert_latency_microsecs::numeric/NULLIF(insert_count::numeric,0),3) insert_latency_per_exec, + update_count::numeric, + update_latency_microsecs::numeric, + TRUNC(update_latency_microsecs::numeric/NULLIF(update_count::numeric,0),3) update_latency_per_exec, + delete_count::numeric, + delete_latency_microsecs::numeric, + TRUNC(delete_latency_microsecs::numeric/NULLIF(delete_count::numeric,0) ,3)delete_latency_per_exec + FROM dml_details + order by select_count desc; +\else + \if yes + \qecho 'This is not Aurora instance' + \endif +\endif +\qecho
+\qecho
[Top]

+-- +----------------------------------------------------------------------------+ +-- | - process_memory_context_usage - | +-- +----------------------------------------------------------------------------+ +\qecho +\qecho Process memory context usage


+\qecho
+\qecho
+select count(*) > 0 isaurora from pg_settings where name='rds.extensions' and setting like '%aurora_stat_utils%' \gset +\if :isaurora +\qecho
+\qecho

The allocated memory for each memory context across all the processes ordered by allocated memory:

+\qecho
+select +name, sum(allocated) as allocated_size_bytes, + pg_size_pretty(sum(allocated)) as allocated_size, + pg_size_pretty(sum(used)) as used_size, + trunc(sum(used)/sum(allocated)*100,2) as used_pct, + sum(instances) as instances_count + from aurora_stat_memctx_usage() + group by name + order by allocated_size_bytes desc; +\qecho
+\qecho

The top 50 porcess with the highest allocated memory:

+\qecho
+select +pid, sum(allocated) as allocated_size_bytes ,pg_size_pretty(sum(allocated)) as allocated_size , pg_size_pretty(sum(used)) as used_size, +trunc(sum(used)/sum(allocated)*100,2) as used_pct +from aurora_stat_memctx_usage() +group by pid order by allocated_size_bytes desc +limit 50; + +\qecho
+\qecho

The top 50 porcess with the highest allocated memory including process information in pg_stat_activity view:

+\qecho
+WITH memctx AS + (select +pid, sum(allocated) as allocated_size_bytes ,pg_size_pretty(sum(allocated)) as allocated_size , pg_size_pretty(sum(used)) as used_size, +trunc(sum(used)/sum(allocated)*100,2) as used_pct +from aurora_stat_memctx_usage() +group by pid order by allocated_size_bytes desc +limit 50 + ) +select * +from pg_stat_activity ps +INNER JOIN memctx ON ps.pid = memctx.pid +order by memctx.allocated_size_bytes desc ; +\qecho
+\qecho

The top 50 processes with the highest allocated memory and the breakdown of their memory usage of each memory context:

+\qecho
+WITH memctx as +(select +pid, sum(allocated) as allocated_size_bytes +from aurora_stat_memctx_usage() +group by pid order by allocated_size_bytes desc +limit 50 ) +select pid ,name , allocated as allocated_size_bytes ,pg_size_pretty(allocated) as allocated_size , pg_size_pretty(used) as used_size , instances as instances_count from aurora_stat_memctx_usage() where pid in +(select pid from memctx ) +order by PID, allocated_size_bytes desc; +\else + \if yes + \qecho 'This is not Aurora instance' + \endif +\endif +\qecho
+\qecho
[Top]

+-- +----------------------------------------------------------------------------+ +-- | - aurora_stat_statements - | +-- +----------------------------------------------------------------------------+ + + +\qecho +\qecho Aurora_stat_statements


+\qecho
+\qecho
+select count(*) > 0 isaurora from pg_settings where name='rds.extensions' and setting like '%aurora_stat_utils%' \gset +\if :isaurora +\qecho
+\qecho

Top 50 SQL order by total_time:

+\qecho
+\qecho
+select queryid,substring(query,1,60) as query , calls, +round(total_exec_time::numeric, 2) as total_time_Msec, +round((total_exec_time::numeric/1000), 2) as total_time_sec, +round(mean_exec_time::numeric,2) as avg_time_Msec, +round((mean_exec_time::numeric/1000),2) as avg_time_sec, +round(stddev_exec_time::numeric, 2) as standard_deviation_time_Msec, +round((stddev_exec_time::numeric/1000), 2) as standard_deviation_time_sec, +round(rows::numeric/calls,2) rows_per_exec, +round((100 * total_exec_time / sum(total_exec_time) over ())::numeric, 4) as percent +from aurora_stat_statements() +order by total_time_Msec desc limit 50; +\qecho
+\qecho
+\qecho

Top 50 SQL order by avg_time:

+\qecho
+\qecho
+select queryid,substring(query,1,60) as query , calls, +round(total_exec_time::numeric, 2) as total_time_Msec, +round((total_exec_time::numeric/1000), 2) as total_time_sec, +round(mean_exec_time::numeric,2) as avg_time_Msec, +round((mean_exec_time::numeric/1000),2) as avg_time_sec, +round(stddev_exec_time::numeric, 2) as standard_deviation_time_Msec, +round((stddev_exec_time::numeric/1000), 2) as standard_deviation_time_sec, +round(rows::numeric/calls,2) rows_per_exec, +round((100 * total_exec_time / sum(total_exec_time) over ())::numeric, 4) as percent +from aurora_stat_statements() +order by avg_time_Msec desc limit 50; +\qecho
+\qecho
+\qecho

Top 50 SQL order by percent of total DB time percent:

+\qecho
+\qecho
+select queryid,substring(query,1,60) as query , calls, +round(total_exec_time::numeric, 2) as total_time_Msec, +round((total_exec_time::numeric/1000), 2) as total_time_sec, +round(mean_exec_time::numeric,2) as avg_time_Msec, +round((mean_exec_time::numeric/1000),2) as avg_time_sec, +round(stddev_exec_time::numeric, 2) as standard_deviation_time_Msec, +round((stddev_exec_time::numeric/1000), 2) as standard_deviation_time_sec, +round(rows::numeric/calls,2) rows_per_exec, +round((100 * total_exec_time / sum(total_exec_time) over ())::numeric, 4) as percent +from aurora_stat_statements() +order by percent desc limit 50; +\qecho
+\qecho
+\qecho

Top 50 SQL order by number of execution (CALLs):

+\qecho
+\qecho
+select queryid,substring(query,1,60) as query , calls, +round(total_exec_time::numeric, 2) as total_time_Msec, +round((total_exec_time::numeric/1000), 2) as total_time_sec, +round(mean_exec_time::numeric,2) as avg_time_Msec, +round((mean_exec_time::numeric/1000),2) as avg_time_sec, +round(stddev_exec_time::numeric, 2) as standard_deviation_time_Msec, +round((stddev_exec_time::numeric/1000), 2) as standard_deviation_time_sec, +round(rows::numeric/calls,2) rows_per_exec, +round((100 * total_exec_time / sum(total_exec_time) over ())::numeric, 4) as percent +from aurora_stat_statements() +order by calls desc limit 50; +\qecho
+ +\qecho
+\qecho

Top 50 SQL order by shared blocks read (physical reads) from Aurora storage:

+\qecho
+\qecho
+select queryid, substring(query,1,60) as query , calls, +round(total_exec_time::numeric, 2) as total_time_Msec, +round((total_exec_time::numeric/1000), 2) as total_time_sec, +round(mean_exec_time::numeric,2) as avg_time_Msec, +round((mean_exec_time::numeric/1000),2) as avg_time_sec, +round(stddev_exec_time::numeric, 2) as standard_deviation_time_Msec, +round((stddev_exec_time::numeric/1000), 2) as standard_deviation_time_sec, +round(rows::numeric/calls,2) rows_per_exec, +round((100 * total_exec_time / sum(total_exec_time) over ())::numeric, 4) as percent, +shared_blks_read,orcache_blks_hit, +storage_blks_read +from aurora_stat_statements() +order by storage_blks_read desc limit 50; +\qecho
+\else + \if yes + \qecho 'This is not Aurora instance' + \endif +\endif +\qecho
+\qecho
[Top]

+ + +-- +----------------------------------------------------------------------------+ +-- | - aurora_stat_plans - | +-- +----------------------------------------------------------------------------+ + + +\qecho +\qecho Aurora_stat_plans


+\qecho
+\qecho
+select count(*) > 0 isaurora from pg_settings where name='rds.extensions' and setting like '%aurora_stat_utils%' \gset +\if :isaurora +\qecho
+\qecho

Top 50 Plan order by total_time:

+\qecho
+\qecho
+select queryid,substring(query,1,60) as query , +planid ,plan_type, plan_captured_time,explain_plan,calls, +round(total_exec_time::numeric, 2) as total_time_Msec, +round((total_exec_time::numeric/1000), 2) as total_time_sec, +round(mean_exec_time::numeric,2) as avg_time_Msec, +round((mean_exec_time::numeric/1000),2) as avg_time_sec, +round(stddev_exec_time::numeric, 2) as standard_deviation_time_Msec, +round((stddev_exec_time::numeric/1000), 2) as standard_deviation_time_sec, +round(rows::numeric/calls,2) rows_per_exec, +round((100 * total_exec_time / sum(total_exec_time) over ())::numeric, 4) as percent +from aurora_stat_plans() +where calls !=0 +order by total_time_Msec desc limit 50; +\qecho
+ +\qecho
+\qecho

Top 50 plan order by avg_time:

+\qecho
+\qecho
+select queryid,substring(query,1,60) as query , +planid ,plan_type, plan_captured_time,explain_plan, calls, +round(total_exec_time::numeric, 2) as total_time_Msec, +round((total_exec_time::numeric/1000), 2) as total_time_sec, +round(mean_exec_time::numeric,2) as avg_time_Msec, +round((mean_exec_time::numeric/1000),2) as avg_time_sec, +round(stddev_exec_time::numeric, 2) as standard_deviation_time_Msec, +round((stddev_exec_time::numeric/1000), 2) as standard_deviation_time_sec, +round(rows::numeric/calls,2) rows_per_exec, +round((100 * total_exec_time / sum(total_exec_time) over ())::numeric, 4) as percent +from aurora_stat_plans() +where calls !=0 +order by avg_time_Msec desc limit 50; +\qecho
+ +\qecho
+\qecho

Top 50 plan order by percent of total DB time percent:

+\qecho
+\qecho
+select queryid,substring(query,1,60) as query , +planid ,plan_type, plan_captured_time,explain_plan, calls, +round(total_exec_time::numeric, 2) as total_time_Msec, +round((total_exec_time::numeric/1000), 2) as total_time_sec, +round(mean_exec_time::numeric,2) as avg_time_Msec, +round((mean_exec_time::numeric/1000),2) as avg_time_sec, +round(stddev_exec_time::numeric, 2) as standard_deviation_time_Msec, +round((stddev_exec_time::numeric/1000), 2) as standard_deviation_time_sec, +round(rows::numeric/calls,2) rows_per_exec, +round((100 * total_exec_time / sum(total_exec_time) over ())::numeric, 4) as percent +from aurora_stat_plans() +where calls !=0 +order by percent desc limit 50; +\qecho
+ +\qecho
+\qecho

Top 50 plan order by number of execution (CALLs):

+\qecho
+\qecho
+select queryid,substring(query,1,60) as query , +planid ,plan_type, plan_captured_time,explain_plan, calls, +round(total_exec_time::numeric, 2) as total_time_Msec, +round((total_exec_time::numeric/1000), 2) as total_time_sec, +round(mean_exec_time::numeric,2) as avg_time_Msec, +round((mean_exec_time::numeric/1000),2) as avg_time_sec, +round(stddev_exec_time::numeric, 2) as standard_deviation_time_Msec, +round((stddev_exec_time::numeric/1000), 2) as standard_deviation_time_sec, +round(rows::numeric/calls,2) rows_per_exec, +round((100 * total_exec_time / sum(total_exec_time) over ())::numeric, 4) as percent +from aurora_stat_plans() +where calls !=0 +order by calls desc limit 50; +\qecho
+ +\qecho
+\qecho

Top 50 plan by shared blocks read (physical reads) from Aurora storage:

+\qecho
+\qecho
+select queryid, substring(query,1,60) as query , +planid ,plan_type, plan_captured_time,explain_plan, calls, +round(total_exec_time::numeric, 2) as total_time_Msec, +round((total_exec_time::numeric/1000), 2) as total_time_sec, +round(mean_exec_time::numeric,2) as avg_time_Msec, +round((mean_exec_time::numeric/1000),2) as avg_time_sec, +round(stddev_exec_time::numeric, 2) as standard_deviation_time_Msec, +round((stddev_exec_time::numeric/1000), 2) as standard_deviation_time_sec, +round(rows::numeric/calls,2) rows_per_exec, +round((100 * total_exec_time / sum(total_exec_time) over ())::numeric, 4) as percent, +shared_blks_read,orcache_blks_hit, +storage_blks_read +from aurora_stat_plans() +where calls !=0 +order by storage_blks_read desc limit 50; +\qecho
+\qecho
+\qecho

Listing Queries ID that have more then one plan (query id/plans_count) :

+\qecho
+\qecho
+-- calls !=0 >> to filter out canceled sqls +-- planid <> 0 >> to filter out non plannable statement (plan_type =no plan) +select dbid,queryid , count(planid) as plans_count +from aurora_stat_plans() +where planid <> 0 +group by dbid,queryid +having count(planid) > 1 +order by 3; +\qecho
+\qecho
+\qecho

Listing Queries with Multiple Execution Plans :

+\qecho
+\qecho
+-- calls !=0 >> to filter out canceled sqls +-- planid <> 0 >> to filter out non plannable statement (plan_type =no plan) +select dbid,queryid,substring(query,1,60) as query , +planid ,plan_type, plan_captured_time,explain_plan, calls, +round(total_exec_time::numeric, 2) as total_time_Msec, +round((total_exec_time::numeric/1000), 2) as total_time_sec, +round(mean_exec_time::numeric,2) as avg_time_Msec, +round((mean_exec_time::numeric/1000),2) as avg_time_sec, +round(stddev_exec_time::numeric, 2) as standard_deviation_time_Msec, +round((stddev_exec_time::numeric/1000), 2) as standard_deviation_time_sec, +round(rows::numeric/calls,2) rows_per_exec, +round((100 * total_exec_time / sum(total_exec_time) over ())::numeric, 4) as percent +from aurora_stat_plans() where queryid in (select queryid +from aurora_stat_plans() +where planid <> 0 +group by dbid,queryid +having count(planid) > 1 +) +and calls !=0 +order by queryid,dbid, total_time_Msec desc; +\qecho
+\else + \if yes + \qecho 'This is not Aurora instance' + \endif +\endif +\qecho
+\qecho
[Top]

+ +-- +----------------------------------------------------------------------------+ +-- | - logical_replication_write_through_cache - | +-- +----------------------------------------------------------------------------+ + + +\qecho +\qecho Logical replication write-through cache


+\qecho
+\qecho
+select count(*) > 0 isaurora from pg_settings where name='rds.extensions' and setting like '%aurora_stat_utils%' \gset +\if :isaurora +SELECT * FROM aurora_stat_logical_wal_cache(); +\else + \if yes + \qecho 'This is not Aurora instance' + \endif +\endif +\qecho
+ +\qecho
[Top]

+ +\echo Report Generated Successfully +\echo Report name and location: /tmp/pg_collector_:filename.html \q