├── pgwatch2 ├── config │ ├── dummy_config │ │ └── mydb_x.yml │ └── scalefield.template ├── metrics │ ├── change_events │ │ ├── 9.0 │ │ │ └── metric.sql │ │ └── README.md │ ├── pgbouncer_stats │ │ └── 0 │ │ │ └── metric.sql │ ├── pgbouncer_clients │ │ └── 0 │ │ │ └── metric.sql │ ├── stat_io │ │ ├── 16 │ │ │ └── metric.sql │ │ └── metric_attrs.yaml │ ├── archiver │ │ ├── metric_attrs.yaml │ │ ├── column_attrs.yaml │ │ └── 9.4 │ │ │ └── metric.sql │ ├── bgwriter │ │ ├── metric_attrs.yaml │ │ ├── 9.0 │ │ │ └── metric_master.sql │ │ └── 9.2 │ │ │ └── metric_master.sql │ ├── cpu_load │ │ ├── metric_attrs.yaml │ │ ├── column_attrs.yaml │ │ └── 9.0 │ │ │ └── metric.sql │ ├── psutil_cpu │ │ ├── metric_attrs.yaml │ │ ├── column_attrs.yaml │ │ └── 9.0 │ │ │ └── metric.sql │ ├── psutil_disk │ │ ├── metric_attrs.yaml │ │ ├── column_attrs.yaml │ │ └── 9.0 │ │ │ └── metric.sql │ ├── psutil_mem │ │ ├── metric_attrs.yaml │ │ ├── column_attrs.yaml │ │ └── 9.0 │ │ │ └── metric.sql │ ├── replication │ │ ├── metric_attrs.yaml │ │ ├── column_attrs.yaml │ │ └── 9.2 │ │ │ ├── metric.sql │ │ │ └── metric_su.sql │ ├── wal_receiver │ │ ├── 10 │ │ │ └── metric_standby.sql │ │ ├── metric_attrs.yaml │ │ ├── column_attrs.yaml │ │ └── 9.2 │ │ │ └── metric_standby.sql │ ├── wal_size │ │ ├── 10 │ │ │ ├── metric.sql │ │ │ └── metric_su.sql │ │ ├── metric_attrs.yaml │ │ ├── column_attrs.yaml │ │ └── 9.0 │ │ │ ├── metric.sql │ │ │ └── metric_su.sql │ ├── backup_age_walg │ │ ├── metric_attrs.yaml │ │ └── 9.1 │ │ │ └── metric.sql │ ├── buffercache_by_db │ │ ├── metric_attrs.yaml │ │ ├── column_attrs.yaml │ │ └── 9.2 │ │ │ └── metric.sql │ ├── db_size │ │ ├── metric_attrs.yaml │ │ ├── column_attrs.yaml │ │ └── 9.0 │ │ │ └── metric.sql │ ├── replication_slots │ │ ├── 10 │ │ │ └── metric_master.sql │ │ ├── metric_attrs.yaml │ │ ├── column_attrs.yaml │ │ └── 9.4 │ │ │ └── metric_master.sql │ ├── backup_age_pgbackrest │ │ ├── metric_attrs.yaml │ │ └── 9.1 │ │ │ └── metric.sql │ ├── buffercache_by_type │ │ ├── metric_attrs.yaml │ │ ├── column_attrs.yaml │ │ └── 9.2 │ │ │ └── metric.sql │ ├── db_size_approx │ │ ├── metric_attrs.yaml │ │ ├── column_attrs.yaml │ │ └── 9.1 │ │ │ └── metric.sql │ ├── db_stats_aurora │ │ ├── 10 │ │ │ └── metric.sql │ │ ├── metric_attrs.yaml │ │ ├── column_attrs.yaml │ │ └── 9.6 │ │ │ └── metric.sql │ ├── psutil_disk_io_total │ │ ├── metric_attrs.yaml │ │ └── 9.0 │ │ │ └── metric.sql │ ├── table_stats │ │ ├── metric_attrs.yaml │ │ └── column_attrs.yaml │ ├── table_stats_approx │ │ ├── metric_attrs.yaml │ │ └── column_attrs.yaml │ ├── logical_subscriptions │ │ ├── 10 │ │ │ └── metric.sql │ │ └── column_attrs.yaml │ ├── stat_statements │ │ └── metric_attrs.yaml │ ├── wal │ │ ├── 10 │ │ │ ├── metric.sql │ │ │ └── metric_su.sql │ │ ├── column_attrs.yaml │ │ ├── metric_attrs.yaml │ │ └── 9.2 │ │ │ └── metric.sql │ ├── locks │ │ ├── column_attrs.yaml │ │ └── 9.0 │ │ │ └── metric.sql │ ├── stat_ssl │ │ ├── column_attrs.yaml │ │ └── 9.5 │ │ │ ├── metric.sql │ │ │ └── metric_su.sql │ ├── stat_statements_calls │ │ ├── 13 │ │ │ └── metric.sql │ │ ├── metric_attrs.yaml │ │ └── 9.2 │ │ │ └── metric.sql │ ├── backends │ │ └── column_attrs.yaml │ ├── blocking_locks │ │ ├── column_attrs.yaml │ │ └── 9.2 │ │ │ └── metric.sql │ ├── locks_mode │ │ ├── column_attrs.yaml │ │ └── 9.0 │ │ │ └── metric.sql │ ├── recommendations │ │ └── 9.0 │ │ │ └── metric.sql │ ├── server_log_event_counts │ │ └── 9.0 │ │ │ └── metric.sql │ ├── table_bloat_approx_stattuple │ │ ├── column_attrs.yaml │ │ ├── metric_attrs.yaml │ │ └── 9.5 │ │ │ └── metric_master.sql │ ├── table_bloat_approx_summary │ │ ├── 10 │ │ │ └── metric.sql │ │ ├── column_attrs.yaml │ │ └── 9.5 │ │ │ ├── metric_master.sql │ │ │ └── metric_su.sql │ ├── table_bloat_approx_summary_sql │ │ ├── 12 │ │ │ └── metric.sql │ │ ├── column_attrs.yaml │ │ └── 9.0 │ │ │ └── metric.sql │ ├── stat_statements_no_query_text │ │ └── metric_attrs.yaml │ ├── db_stats │ │ ├── 10 │ │ │ ├── metric.sql │ │ │ └── metric_su.sql │ │ ├── 12 │ │ │ ├── metric.sql │ │ │ └── metric_su.sql │ │ ├── 14 │ │ │ └── metric.sql │ │ ├── 15 │ │ │ ├── metric.sql │ │ │ └── metric_su.sql │ │ ├── column_attrs.yaml │ │ ├── 9.0 │ │ │ └── metric.sql │ │ ├── 9.1 │ │ │ └── metric.sql │ │ ├── 9.2 │ │ │ └── metric.sql │ │ └── 9.3 │ │ │ └── metric.sql │ ├── kpi │ │ ├── column_attrs.yaml │ │ └── 9.0 │ │ │ ├── metric.sql │ │ │ └── metric_su.sql │ ├── smart_health_per_disk │ │ └── 9.1 │ │ │ └── metric.sql │ ├── configuration_hashes │ │ └── 9.0 │ │ │ └── metric.sql │ ├── subscription_stats │ │ └── 15 │ │ │ └── metric.sql │ ├── vmstat │ │ └── 9.1 │ │ │ └── metric.sql │ ├── pgpool_stats │ │ └── 3.0 │ │ │ └── metric.sql │ ├── reco_add_index_ext_qualstats_2.0 │ │ ├── metric_attrs.yaml │ │ └── 9.1 │ │ │ └── metric_master.sql │ ├── 00_helpers │ │ ├── get_sequences │ │ │ └── 10 │ │ │ │ └── metric.sql │ │ ├── get_wal_size │ │ │ ├── 10 │ │ │ │ └── metric.sql │ │ │ └── 9.0 │ │ │ │ └── metric.sql │ │ ├── get_stat_replication │ │ │ └── 9.2 │ │ │ │ └── metric.sql │ │ ├── get_stat_activity │ │ │ ├── 9.2 │ │ │ │ └── metric.sql │ │ │ └── 9.0 │ │ │ │ └── metric.sql │ │ ├── get_load_average │ │ │ └── 9.1 │ │ │ │ └── metric.sql │ │ ├── README.md │ │ ├── get_psutil_disk_io_total │ │ │ └── 9.1 │ │ │ │ └── metric.sql │ │ └── get_psutil_mem │ │ │ └── 9.1 │ │ │ └── metric.sql │ ├── database_conflicts │ │ └── 9.2 │ │ │ └── metric_standby.sql │ ├── instance_up │ │ └── 9.0 │ │ │ └── metric.sql │ ├── wal_stats │ │ └── 14 │ │ │ └── metric.sql │ ├── replication_slot_stats │ │ └── 14 │ │ │ └── metric.sql │ ├── sproc_hashes │ │ └── 9.0 │ │ │ └── metric.sql │ ├── stat_activity │ │ └── 10 │ │ │ ├── metric.sql │ │ │ └── metric_su.sql │ ├── reco_default_public_schema │ │ └── 9.0 │ │ │ └── metric_master.sql │ ├── sproc_stats │ │ └── 9.0 │ │ │ └── metric.sql │ ├── reco_add_index │ │ ├── metric_attrs.yaml │ │ └── 9.1 │ │ │ └── metric_master.sql │ ├── sequence_health │ │ └── 10 │ │ │ ├── metric.sql │ │ │ └── metric_su.sql │ ├── README_metrics.md │ ├── reco_disabled_triggers │ │ └── 9.0 │ │ │ └── metric_master.sql │ ├── index_hashes │ │ └── 9.0 │ │ │ └── metric.sql │ ├── wait_events │ │ └── 9.6 │ │ │ └── metric.sql │ ├── show_plans_realtime │ │ ├── 10 │ │ │ └── metric.sql │ │ └── 9.0 │ │ │ └── metric.sql │ ├── stat_activity_realtime │ │ ├── 10 │ │ │ └── metric.sql │ │ ├── 9.6 │ │ │ └── metric.sql │ │ ├── 9.2 │ │ │ └── metric.sql │ │ └── 9.0 │ │ │ └── metric.sql │ ├── reco_superusers │ │ └── 9.1 │ │ │ └── metric_master.sql │ ├── invalid_indexes │ │ └── 10 │ │ │ └── metric.sql │ ├── unused_indexes │ │ └── 10 │ │ │ └── metric.sql │ ├── reco_sprocs_wo_search_path │ │ └── 9.1 │ │ │ └── metric_master.sql │ ├── table_io_stats │ │ └── 9.0 │ │ │ └── metric.sql │ ├── reco_drop_index │ │ ├── 9.0 │ │ │ └── metric_master.sql │ │ └── 9.4 │ │ │ └── metric_master.sql │ └── reco_partial_index_candidates │ │ └── 9.0 │ │ └── metric.sql ├── bootstrap │ ├── create_db_pgwatch.sql │ ├── create_db_grafana.sql │ ├── create_db_metric_store.sql │ ├── grant_monitor_to_pgwatch2.sql │ ├── change_pw.sql │ ├── revoke_public_create.sql │ ├── supervisord_bootstrap_pg.sh │ ├── supervisord_bootstrap.sh │ ├── grafana_datasource.sql │ ├── grafana_datasource_pg.sql │ ├── insert_test_monitored_db_nonroot.sql │ ├── grafana_custom_config.ini │ ├── insert_test_monitored_db.sql │ ├── insert_test_monitored_db_influx.sql │ └── insert_test_monitored_db.sh ├── sql │ ├── config_store │ │ ├── migrations │ │ │ ├── v1.6.0-2_metric_column_attrs.sql │ │ │ ├── v1.5.1_metric_name_pattern.sql │ │ │ ├── v1.2.1_metric_version_to_numeric.sql │ │ │ ├── v1.4.0_group.sql │ │ │ ├── v1.3.0_monitored_db_dbtype.sql │ │ │ ├── v1.6.2_superuser_metrics.sql │ │ │ ├── v1.5.0-2_password_encryption.sql │ │ │ ├── v1.7.1-1_patroni_allow_multi.sql │ │ │ ├── v1.5.0-3_standby-only_metrics.sql │ │ │ ├── v1.3.7_stat_statements_calls.sql │ │ │ ├── v1.6.0-3_schema_version.sql │ │ │ ├── v1.6.1-1_patroni_cont_discovery.sql │ │ │ ├── v1.4.0_continuous_discovery.sql │ │ │ ├── v1.7.0_standby_metric_uniqueness.sql │ │ │ ├── v1.8.0-1_metric_name_allow_dot.sql │ │ │ ├── v1.6.0-1_master_only_and_patroni.sql │ │ │ ├── v1.8.0-2_pgpool_dbtype.sql │ │ │ ├── v1.8.2-1_patroni_namespace_discovery.sql │ │ │ ├── v1.5.0-1_ssl_certs.sql │ │ │ ├── v1.8.1-1_standby_config.sql │ │ │ └── v1.7.1-2_metric_attributes.sql │ │ └── remove_config_schema.sql │ └── metric_store │ │ ├── roll_out_metric.psql │ │ ├── roll_out_metric_time.psql │ │ ├── roll_out_metric_dbname_time.psql │ │ ├── roll_out_timescale.psql │ │ ├── custom │ │ └── metric_store_custom.sql │ │ ├── timescale │ │ └── change_chunk_interval.sql │ │ └── metric │ │ └── ensure_partition.sql ├── .gitignore ├── get_dependencies.sh ├── startup-scripts │ └── pgwatch2.service └── non-sql-metric-gathering-samples │ └── disk-smart-health │ └── smart-health.sh ├── docs ├── requirements.txt ├── kubernetes.rst ├── Makefile └── index.rst ├── .gitattributes ├── .vscode └── settings.json ├── grafana_dashboards ├── influxdb │ ├── v4 │ │ ├── db-overview │ │ │ └── title.txt │ │ ├── documentation │ │ │ └── title.txt │ │ ├── lock-details │ │ │ └── title.txt │ │ ├── replication │ │ │ └── title.txt │ │ ├── sproc-details │ │ │ └── title.txt │ │ ├── system-stats │ │ │ └── title.txt │ │ ├── table-details │ │ │ └── title.txt │ │ ├── pgbouncer-stats │ │ │ └── title.txt │ │ ├── aws-cloudwatch │ │ │ └── title.txt │ │ ├── global-db-overview │ │ │ └── title.txt │ │ ├── biggest-relations │ │ │ └── title.txt │ │ ├── change-events │ │ │ └── title.txt │ │ ├── single-query-details │ │ │ └── title.txt │ │ ├── stat-statements-top │ │ │ └── title.txt │ │ └── stat-statements-overview │ │ │ └── title.txt │ ├── v5 │ │ ├── db-overview │ │ │ └── title.txt │ │ ├── sprocs-top │ │ │ └── title.txt │ │ ├── tables-top │ │ │ └── title.txt │ │ ├── documentation │ │ │ └── title.txt │ │ ├── health-check │ │ │ └── title.txt │ │ ├── lock-details │ │ │ └── title.txt │ │ ├── replication │ │ │ └── title.txt │ │ ├── sproc-details │ │ │ └── title.txt │ │ ├── system-stats │ │ │ └── title.txt │ │ ├── table-details │ │ │ └── title.txt │ │ ├── index-overview │ │ │ └── title.txt │ │ ├── pgbouncer-stats │ │ │ └── title.txt │ │ ├── aws-cloudwatch │ │ │ └── title.txt │ │ ├── global-db-overview │ │ │ └── title.txt │ │ ├── biggest-relations │ │ │ └── title.txt │ │ ├── change-events │ │ │ └── title.txt │ │ ├── single-query-details │ │ │ └── title.txt │ │ ├── stat-statements-top │ │ │ └── title.txt │ │ ├── stat-statements-overview │ │ │ └── title.txt │ │ ├── db-overview-developer │ │ │ └── title.txt │ │ └── checkpointer-bgwriter-stats │ │ │ └── title.txt │ ├── v6 │ │ ├── db-overview │ │ │ └── title.txt │ │ ├── sprocs-top │ │ │ └── title.txt │ │ ├── tables-top │ │ │ └── title.txt │ │ ├── documentation │ │ │ └── title.txt │ │ ├── health-check │ │ │ └── title.txt │ │ ├── lock-details │ │ │ └── title.txt │ │ ├── pgpool-stats │ │ │ └── title.txt │ │ ├── replication │ │ │ └── title.txt │ │ ├── sproc-details │ │ │ └── title.txt │ │ ├── system-stats │ │ │ └── title.txt │ │ ├── table-details │ │ │ └── title.txt │ │ ├── index-overview │ │ │ └── title.txt │ │ ├── pgbouncer-stats │ │ │ └── title.txt │ │ ├── recommendations │ │ │ └── title.txt │ │ ├── aws-cloudwatch │ │ │ └── title.txt │ │ ├── global-db-overview │ │ │ └── title.txt │ │ ├── server-log-events │ │ │ └── title.txt │ │ ├── biggest-relations │ │ │ └── title.txt │ │ ├── change-events │ │ │ └── title.txt │ │ ├── single-query-details │ │ │ └── title.txt │ │ ├── stat-statements-top │ │ │ └── title.txt │ │ ├── stat-activity-realtime │ │ │ └── title.txt │ │ ├── postgres-version-overview │ │ │ └── title.txt │ │ ├── stat-statements-overview │ │ │ └── title.txt │ │ ├── stat-statements-sql-search │ │ │ └── title.txt │ │ ├── db-overview-developer │ │ │ └── title.txt │ │ └── checkpointer-bgwriter-stats │ │ │ └── title.txt │ ├── v7 │ │ ├── db-overview │ │ │ └── title.txt │ │ ├── sprocs-top │ │ │ └── title.txt │ │ ├── tables-top │ │ │ └── title.txt │ │ ├── documentation │ │ │ └── title.txt │ │ ├── health-check │ │ │ └── title.txt │ │ ├── lock-details │ │ │ └── title.txt │ │ ├── pgpool-stats │ │ │ └── title.txt │ │ ├── replication │ │ │ └── title.txt │ │ ├── sproc-details │ │ │ └── title.txt │ │ ├── system-stats │ │ │ └── title.txt │ │ ├── table-details │ │ │ └── title.txt │ │ ├── index-overview │ │ │ └── title.txt │ │ ├── pgbouncer-stats │ │ │ └── title.txt │ │ ├── recommendations │ │ │ └── title.txt │ │ ├── aws-cloudwatch │ │ │ └── title.txt │ │ ├── global-db-overview │ │ │ └── title.txt │ │ ├── server-log-events │ │ │ └── title.txt │ │ ├── biggest-relations │ │ │ └── title.txt │ │ ├── change-events │ │ │ └── title.txt │ │ ├── single-query-details │ │ │ └── title.txt │ │ ├── stat-statements-top │ │ │ └── title.txt │ │ ├── stat-activity-realtime │ │ │ └── title.txt │ │ ├── postgres-version-overview │ │ │ └── title.txt │ │ ├── stat-statements-overview │ │ │ └── title.txt │ │ ├── stat-statements-sql-search │ │ │ └── title.txt │ │ ├── db-overview-developer │ │ │ └── title.txt │ │ └── checkpointer-bgwriter-stats │ │ │ └── title.txt │ └── v8 │ │ ├── db-overview │ │ └── title.txt │ │ ├── sprocs-top │ │ └── title.txt │ │ ├── tables-top │ │ └── title.txt │ │ ├── documentation │ │ └── title.txt │ │ ├── health-check │ │ └── title.txt │ │ ├── lock-details │ │ └── title.txt │ │ ├── pgpool-stats │ │ └── title.txt │ │ ├── replication │ │ └── title.txt │ │ ├── sproc-details │ │ └── title.txt │ │ ├── system-stats │ │ └── title.txt │ │ ├── table-details │ │ └── title.txt │ │ ├── index-overview │ │ └── title.txt │ │ ├── pgbouncer-stats │ │ └── title.txt │ │ ├── recommendations │ │ └── title.txt │ │ ├── aws-cloudwatch │ │ └── title.txt │ │ ├── global-db-overview │ │ └── title.txt │ │ ├── server-log-events │ │ └── title.txt │ │ ├── biggest-relations │ │ └── title.txt │ │ ├── change-events │ │ └── title.txt │ │ ├── single-query-details │ │ └── title.txt │ │ ├── stat-statements-top │ │ └── title.txt │ │ ├── stat-activity-realtime │ │ └── title.txt │ │ ├── postgres-version-overview │ │ └── title.txt │ │ ├── stat-statements-overview │ │ └── title.txt │ │ ├── stat-statements-sql-search │ │ └── title.txt │ │ ├── db-overview-developer │ │ └── title.txt │ │ └── checkpointer-bgwriter-stats │ │ └── title.txt ├── postgres │ ├── v5 │ │ ├── db-overview │ │ │ └── title.txt │ │ ├── sprocs-top │ │ │ └── title.txt │ │ ├── tables-top │ │ │ └── title.txt │ │ ├── documentation │ │ │ └── title.txt │ │ ├── health-check │ │ │ └── title.txt │ │ ├── lock-details │ │ │ └── title.txt │ │ ├── replication │ │ │ └── title.txt │ │ ├── sproc-details │ │ │ └── title.txt │ │ ├── system-stats │ │ │ └── title.txt │ │ ├── table-details │ │ │ └── title.txt │ │ ├── index-overview │ │ │ └── title.txt │ │ ├── pgbouncer-stats │ │ │ └── title.txt │ │ ├── aws-cloudwatch │ │ │ └── title.txt │ │ ├── global-db-overview │ │ │ └── title.txt │ │ ├── biggest-relations │ │ │ └── title.txt │ │ ├── change-events │ │ │ └── title.txt │ │ ├── single-query-details │ │ │ └── title.txt │ │ ├── stat-statements-top │ │ │ └── title.txt │ │ ├── db-overview-developer │ │ │ └── title.txt │ │ └── checkpointer-bgwriter-stats │ │ │ └── title.txt │ ├── v6 │ │ ├── db-overview │ │ │ └── title.txt │ │ ├── sprocs-top │ │ │ └── title.txt │ │ ├── tables-top │ │ │ └── title.txt │ │ ├── documentation │ │ │ └── title.txt │ │ ├── global-health │ │ │ └── title.txt │ │ ├── health-check │ │ │ └── title.txt │ │ ├── lock-details │ │ │ └── title.txt │ │ ├── pgpool-stats │ │ │ └── title.txt │ │ ├── replication │ │ │ └── title.txt │ │ ├── sproc-details │ │ │ └── title.txt │ │ ├── system-stats │ │ │ └── title.txt │ │ ├── table-details │ │ │ └── title.txt │ │ ├── alert-template │ │ │ └── title.txt │ │ ├── index-overview │ │ │ └── title.txt │ │ ├── pgbouncer-stats │ │ │ └── title.txt │ │ ├── recommendations │ │ │ └── title.txt │ │ ├── aws-cloudwatch │ │ │ └── title.txt │ │ ├── global-db-overview │ │ │ └── title.txt │ │ ├── server-log-events │ │ │ └── title.txt │ │ ├── sessions-overview │ │ │ └── title.txt │ │ ├── biggest-relations │ │ │ └── title.txt │ │ ├── change-events │ │ │ └── title.txt │ │ ├── single-query-details │ │ │ └── title.txt │ │ ├── stat-statements-top │ │ │ └── title.txt │ │ ├── show-plans-realtime │ │ │ └── title.txt │ │ ├── stat-activity-realtime │ │ │ └── title.txt │ │ ├── db-overview-time-lag │ │ │ └── title.txt │ │ ├── postgres-version-overview │ │ │ └── title.txt │ │ ├── stat-statements-sql-search │ │ │ └── title.txt │ │ ├── stat-statements-top-fast │ │ │ └── title.txt │ │ ├── stat-statements-top-visual │ │ │ └── title.txt │ │ ├── system-stats-time-lag │ │ │ └── title.txt │ │ ├── table-details-time-lag │ │ │ └── title.txt │ │ ├── db-overview-developer │ │ │ └── title.txt │ │ └── checkpointer-bgwriter-stats │ │ │ └── title.txt │ ├── v7 │ │ ├── db-overview │ │ │ └── title.txt │ │ ├── sprocs-top │ │ │ └── title.txt │ │ ├── tables-top │ │ │ └── title.txt │ │ ├── documentation │ │ │ └── title.txt │ │ ├── global-health │ │ │ └── title.txt │ │ ├── health-check │ │ │ └── title.txt │ │ ├── lock-details │ │ │ └── title.txt │ │ ├── pgpool-stats │ │ │ └── title.txt │ │ ├── replication │ │ │ └── title.txt │ │ ├── sproc-details │ │ │ └── title.txt │ │ ├── system-stats │ │ │ └── title.txt │ │ ├── table-details │ │ │ └── title.txt │ │ ├── alert-template │ │ │ └── title.txt │ │ ├── index-overview │ │ │ └── title.txt │ │ ├── pgbouncer-stats │ │ │ └── title.txt │ │ ├── recommendations │ │ │ └── title.txt │ │ ├── aws-cloudwatch │ │ │ └── title.txt │ │ ├── global-db-overview │ │ │ └── title.txt │ │ ├── server-log-events │ │ │ └── title.txt │ │ ├── sessions-overview │ │ │ └── title.txt │ │ ├── biggest-relations │ │ │ └── title.txt │ │ ├── change-events │ │ │ └── title.txt │ │ ├── single-query-details │ │ │ └── title.txt │ │ ├── stat-statements-top │ │ │ └── title.txt │ │ ├── show-plans-realtime │ │ │ └── title.txt │ │ ├── stat-activity-realtime │ │ │ └── title.txt │ │ ├── db-overview-time-lag │ │ │ └── title.txt │ │ ├── postgres-version-overview │ │ │ └── title.txt │ │ ├── stat-statements-sql-search │ │ │ └── title.txt │ │ ├── stat-statements-top-fast │ │ │ └── title.txt │ │ ├── stat-statements-top-visual │ │ │ └── title.txt │ │ ├── system-stats-time-lag │ │ │ └── title.txt │ │ ├── table-details-time-lag │ │ │ └── title.txt │ │ ├── db-overview-developer │ │ │ └── title.txt │ │ └── checkpointer-bgwriter-stats │ │ │ └── title.txt │ └── v8 │ │ ├── db-overview │ │ └── title.txt │ │ ├── sprocs-top │ │ └── title.txt │ │ ├── tables-top │ │ └── title.txt │ │ ├── documentation │ │ └── title.txt │ │ ├── global-health │ │ └── title.txt │ │ ├── health-check │ │ └── title.txt │ │ ├── lock-details │ │ └── title.txt │ │ ├── pgpool-stats │ │ └── title.txt │ │ ├── replication │ │ └── title.txt │ │ ├── sproc-details │ │ └── title.txt │ │ ├── system-stats │ │ └── title.txt │ │ ├── table-details │ │ └── title.txt │ │ ├── alert-template │ │ └── title.txt │ │ ├── index-overview │ │ └── title.txt │ │ ├── pgbouncer-stats │ │ └── title.txt │ │ ├── recommendations │ │ └── title.txt │ │ ├── aws-cloudwatch │ │ └── title.txt │ │ ├── global-db-overview │ │ └── title.txt │ │ ├── server-log-events │ │ └── title.txt │ │ ├── sessions-overview │ │ └── title.txt │ │ ├── biggest-relations │ │ └── title.txt │ │ ├── change-events │ │ └── title.txt │ │ ├── single-query-details │ │ └── title.txt │ │ ├── stat-statements-top │ │ └── title.txt │ │ ├── show-plans-realtime │ │ └── title.txt │ │ ├── stat-activity-realtime │ │ └── title.txt │ │ ├── stat-activity │ │ └── title.txt │ │ ├── db-overview-time-lag │ │ └── title.txt │ │ ├── postgres-version-overview │ │ └── title.txt │ │ ├── stat-statements-sql-search │ │ └── title.txt │ │ ├── stat-statements-top-fast │ │ └── title.txt │ │ ├── stat-statements-top-visual │ │ └── title.txt │ │ ├── system-stats-time-lag │ │ └── title.txt │ │ ├── table-details-time-lag │ │ └── title.txt │ │ ├── db-overview-developer │ │ └── title.txt │ │ └── checkpointer-bgwriter-stats │ │ └── title.txt ├── prometheus │ ├── v7 │ │ ├── tables-top │ │ │ └── title.txt │ │ ├── db-overview │ │ │ └── title.txt │ │ ├── health-check │ │ │ └── title.txt │ │ ├── wait-events │ │ │ └── title.txt │ │ ├── query-details │ │ │ └── title.txt │ │ ├── table-details │ │ │ └── title.txt │ │ ├── sessions-overview │ │ │ └── title.txt │ │ └── postgres-version-overview │ │ │ └── title.txt │ ├── v8 │ │ ├── tables-top │ │ │ └── title.txt │ │ ├── db-overview │ │ │ └── title.txt │ │ ├── health-check │ │ │ └── title.txt │ │ ├── table-details │ │ │ └── title.txt │ │ └── sessions-overview │ │ │ └── title.txt │ └── v5 │ │ └── db-overview-developer │ │ └── title.txt ├── README.md └── delete_all_old_pw2_dashes.sh ├── webpy ├── static │ ├── custom.css │ └── logo.png ├── requirements_pg_metrics.txt ├── requirements_influx_metrics.txt ├── startup-scripts │ └── pgwatch2-webui.service ├── crypto.py ├── templates │ └── login.html └── utils.py ├── screenshots ├── overview.png ├── tables_top.png ├── web_ui_dbs.png ├── global_health.png ├── health_check.png ├── pgpool_status.png ├── alert_template.png ├── index_overview.png ├── pgbouncer_stats.png ├── recommendations.png ├── replication_lag.png ├── overview_developer.png ├── server_log_events.png ├── sessions_overview.png ├── checkpointer_bgwriter.png ├── overview_light_theme.png ├── pgwatch2_architecture.png ├── show_plans_realtime.png ├── stat_statements_top.png ├── system_stats_psutil.png ├── stat_activity_realtime.png ├── biggest_relations_treemap.png ├── pgwatch2_architecture_push.png ├── postres_versions_overview.png ├── stat_statements_sql_search.png ├── stat_statements_top_visual.png ├── aws_rds_cloudwatch_overview.png ├── db_overview_time_lag_comparison.png ├── pgwatch2_change_detected_dash.png └── web_ui_metrics_and_preset_configs.png ├── .secrets.yaml ├── docker ├── test │ ├── launch_all_pg_versions │ │ ├── get-all-pg-versions.sh │ │ ├── stop-all-pg-versions.sh │ │ ├── pause-all-pg-versions.sh │ │ ├── unpause-all-pg-versions.sh │ │ ├── purge-all-pg-versions.sh │ │ ├── pgbench_on_all.sh │ │ └── add_all_to_monitoring.sql │ └── smoke_test_all_latest_images.sh ├── influxdb │ ├── README.md │ └── non-root-user.patch ├── launch-wrapper-webui.sh ├── Dockerfile-daemon ├── grafana │ └── Dockerfile-grafana ├── Dockerfile-webui └── Dockerfile-scalefield ├── pg_hba.conf ├── .gitignore ├── .github ├── dependabot.yml └── workflows │ └── stale.yml ├── .readthedocs.yaml ├── postgresql.conf ├── postgresql_timescale.conf ├── postgresql_22.04.conf └── docker-launcher-scalefield.sh /pgwatch2/config/dummy_config/mydb_x.yml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | Sphinx 2 | sphinx_rtd_theme -------------------------------------------------------------------------------- /pgwatch2/metrics/change_events/9.0/metric.sql: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sql linguist-language=PLpgSQL 2 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.eol": "\n" 3 | } -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v4/db-overview/title.txt: -------------------------------------------------------------------------------- 1 | DB overview -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v5/db-overview/title.txt: -------------------------------------------------------------------------------- 1 | DB overview -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v5/sprocs-top/title.txt: -------------------------------------------------------------------------------- 1 | Sprocs Top -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v5/tables-top/title.txt: -------------------------------------------------------------------------------- 1 | Tables top -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v6/db-overview/title.txt: -------------------------------------------------------------------------------- 1 | DB overview -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v6/sprocs-top/title.txt: -------------------------------------------------------------------------------- 1 | Sprocs Top -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v6/tables-top/title.txt: -------------------------------------------------------------------------------- 1 | Tables top -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v7/db-overview/title.txt: -------------------------------------------------------------------------------- 1 | DB overview -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v7/sprocs-top/title.txt: -------------------------------------------------------------------------------- 1 | Sprocs Top -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v7/tables-top/title.txt: -------------------------------------------------------------------------------- 1 | Tables top -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v8/db-overview/title.txt: -------------------------------------------------------------------------------- 1 | DB overview -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v8/sprocs-top/title.txt: -------------------------------------------------------------------------------- 1 | Sprocs Top -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v8/tables-top/title.txt: -------------------------------------------------------------------------------- 1 | Tables top -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v5/db-overview/title.txt: -------------------------------------------------------------------------------- 1 | DB overview -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v5/sprocs-top/title.txt: -------------------------------------------------------------------------------- 1 | Sprocs Top -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v5/tables-top/title.txt: -------------------------------------------------------------------------------- 1 | Tables top -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/db-overview/title.txt: -------------------------------------------------------------------------------- 1 | DB overview -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/sprocs-top/title.txt: -------------------------------------------------------------------------------- 1 | Sprocs Top -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/tables-top/title.txt: -------------------------------------------------------------------------------- 1 | Tables top -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/db-overview/title.txt: -------------------------------------------------------------------------------- 1 | DB overview -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/sprocs-top/title.txt: -------------------------------------------------------------------------------- 1 | Sprocs Top -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/tables-top/title.txt: -------------------------------------------------------------------------------- 1 | Tables top -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/db-overview/title.txt: -------------------------------------------------------------------------------- 1 | DB overview -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/sprocs-top/title.txt: -------------------------------------------------------------------------------- 1 | Sprocs Top -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/tables-top/title.txt: -------------------------------------------------------------------------------- 1 | Tables top -------------------------------------------------------------------------------- /grafana_dashboards/prometheus/v7/tables-top/title.txt: -------------------------------------------------------------------------------- 1 | Tables Top -------------------------------------------------------------------------------- /grafana_dashboards/prometheus/v8/tables-top/title.txt: -------------------------------------------------------------------------------- 1 | Tables Top -------------------------------------------------------------------------------- /pgwatch2/metrics/pgbouncer_stats/0/metric.sql: -------------------------------------------------------------------------------- 1 | show stats; 2 | -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v4/documentation/title.txt: -------------------------------------------------------------------------------- 1 | Documentation -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v4/lock-details/title.txt: -------------------------------------------------------------------------------- 1 | Lock details -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v4/replication/title.txt: -------------------------------------------------------------------------------- 1 | Replication lag -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v4/sproc-details/title.txt: -------------------------------------------------------------------------------- 1 | Sproc details -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v4/system-stats/title.txt: -------------------------------------------------------------------------------- 1 | System Stats -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v4/table-details/title.txt: -------------------------------------------------------------------------------- 1 | Table details -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v5/documentation/title.txt: -------------------------------------------------------------------------------- 1 | Documentation -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v5/health-check/title.txt: -------------------------------------------------------------------------------- 1 | Health-check -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v5/lock-details/title.txt: -------------------------------------------------------------------------------- 1 | Lock details -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v5/replication/title.txt: -------------------------------------------------------------------------------- 1 | Replication lag -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v5/sproc-details/title.txt: -------------------------------------------------------------------------------- 1 | Sproc details -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v5/system-stats/title.txt: -------------------------------------------------------------------------------- 1 | System Stats -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v5/table-details/title.txt: -------------------------------------------------------------------------------- 1 | Table details -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v6/documentation/title.txt: -------------------------------------------------------------------------------- 1 | Documentation -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v6/health-check/title.txt: -------------------------------------------------------------------------------- 1 | Health-check -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v6/lock-details/title.txt: -------------------------------------------------------------------------------- 1 | Lock details -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v6/pgpool-stats/title.txt: -------------------------------------------------------------------------------- 1 | Pgpool Stats -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v6/replication/title.txt: -------------------------------------------------------------------------------- 1 | Replication lag -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v6/sproc-details/title.txt: -------------------------------------------------------------------------------- 1 | Sproc details -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v6/system-stats/title.txt: -------------------------------------------------------------------------------- 1 | System Stats -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v6/table-details/title.txt: -------------------------------------------------------------------------------- 1 | Table details -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v7/documentation/title.txt: -------------------------------------------------------------------------------- 1 | Documentation -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v7/health-check/title.txt: -------------------------------------------------------------------------------- 1 | Health-check -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v7/lock-details/title.txt: -------------------------------------------------------------------------------- 1 | Lock details -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v7/pgpool-stats/title.txt: -------------------------------------------------------------------------------- 1 | Pgpool Stats -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v7/replication/title.txt: -------------------------------------------------------------------------------- 1 | Replication lag -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v7/sproc-details/title.txt: -------------------------------------------------------------------------------- 1 | Sproc details -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v7/system-stats/title.txt: -------------------------------------------------------------------------------- 1 | System Stats -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v7/table-details/title.txt: -------------------------------------------------------------------------------- 1 | Table details -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v8/documentation/title.txt: -------------------------------------------------------------------------------- 1 | Documentation -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v8/health-check/title.txt: -------------------------------------------------------------------------------- 1 | Health-check -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v8/lock-details/title.txt: -------------------------------------------------------------------------------- 1 | Lock details -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v8/pgpool-stats/title.txt: -------------------------------------------------------------------------------- 1 | Pgpool Stats -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v8/replication/title.txt: -------------------------------------------------------------------------------- 1 | Replication lag -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v8/sproc-details/title.txt: -------------------------------------------------------------------------------- 1 | Sproc details -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v8/system-stats/title.txt: -------------------------------------------------------------------------------- 1 | System Stats -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v8/table-details/title.txt: -------------------------------------------------------------------------------- 1 | Table details -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v5/documentation/title.txt: -------------------------------------------------------------------------------- 1 | Documentation -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v5/health-check/title.txt: -------------------------------------------------------------------------------- 1 | Health-check -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v5/lock-details/title.txt: -------------------------------------------------------------------------------- 1 | Lock details -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v5/replication/title.txt: -------------------------------------------------------------------------------- 1 | Replication lag -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v5/sproc-details/title.txt: -------------------------------------------------------------------------------- 1 | Sproc details -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v5/system-stats/title.txt: -------------------------------------------------------------------------------- 1 | System Stats -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v5/table-details/title.txt: -------------------------------------------------------------------------------- 1 | Table details -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/documentation/title.txt: -------------------------------------------------------------------------------- 1 | Documentation -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/global-health/title.txt: -------------------------------------------------------------------------------- 1 | Global Health -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/health-check/title.txt: -------------------------------------------------------------------------------- 1 | Health-check -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/lock-details/title.txt: -------------------------------------------------------------------------------- 1 | Lock details -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/pgpool-stats/title.txt: -------------------------------------------------------------------------------- 1 | Pgpool Stats -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/replication/title.txt: -------------------------------------------------------------------------------- 1 | Replication lag -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/sproc-details/title.txt: -------------------------------------------------------------------------------- 1 | Sproc details -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/system-stats/title.txt: -------------------------------------------------------------------------------- 1 | System Stats -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/table-details/title.txt: -------------------------------------------------------------------------------- 1 | Table details -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/documentation/title.txt: -------------------------------------------------------------------------------- 1 | Documentation -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/global-health/title.txt: -------------------------------------------------------------------------------- 1 | Global Health -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/health-check/title.txt: -------------------------------------------------------------------------------- 1 | Health-check -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/lock-details/title.txt: -------------------------------------------------------------------------------- 1 | Lock details -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/pgpool-stats/title.txt: -------------------------------------------------------------------------------- 1 | Pgpool Stats -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/replication/title.txt: -------------------------------------------------------------------------------- 1 | Replication lag -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/sproc-details/title.txt: -------------------------------------------------------------------------------- 1 | Sproc details -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/system-stats/title.txt: -------------------------------------------------------------------------------- 1 | System Stats -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/table-details/title.txt: -------------------------------------------------------------------------------- 1 | Table details -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/documentation/title.txt: -------------------------------------------------------------------------------- 1 | Documentation -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/global-health/title.txt: -------------------------------------------------------------------------------- 1 | Global Health -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/health-check/title.txt: -------------------------------------------------------------------------------- 1 | Health-check -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/lock-details/title.txt: -------------------------------------------------------------------------------- 1 | Lock details -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/pgpool-stats/title.txt: -------------------------------------------------------------------------------- 1 | Pgpool Stats -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/replication/title.txt: -------------------------------------------------------------------------------- 1 | Replication lag -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/sproc-details/title.txt: -------------------------------------------------------------------------------- 1 | Sproc details -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/system-stats/title.txt: -------------------------------------------------------------------------------- 1 | System Stats -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/table-details/title.txt: -------------------------------------------------------------------------------- 1 | Table details -------------------------------------------------------------------------------- /grafana_dashboards/prometheus/v7/db-overview/title.txt: -------------------------------------------------------------------------------- 1 | DB overview -------------------------------------------------------------------------------- /grafana_dashboards/prometheus/v7/health-check/title.txt: -------------------------------------------------------------------------------- 1 | Health-check -------------------------------------------------------------------------------- /grafana_dashboards/prometheus/v7/wait-events/title.txt: -------------------------------------------------------------------------------- 1 | Wait Events -------------------------------------------------------------------------------- /grafana_dashboards/prometheus/v8/db-overview/title.txt: -------------------------------------------------------------------------------- 1 | DB overview -------------------------------------------------------------------------------- /grafana_dashboards/prometheus/v8/health-check/title.txt: -------------------------------------------------------------------------------- 1 | Health-check -------------------------------------------------------------------------------- /pgwatch2/metrics/pgbouncer_clients/0/metric.sql: -------------------------------------------------------------------------------- 1 | show clients; 2 | -------------------------------------------------------------------------------- /webpy/static/custom.css: -------------------------------------------------------------------------------- 1 | textarea { 2 | resize: both; 3 | } 4 | -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v4/pgbouncer-stats/title.txt: -------------------------------------------------------------------------------- 1 | PgBouncer stats -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v5/index-overview/title.txt: -------------------------------------------------------------------------------- 1 | Index overview -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v5/pgbouncer-stats/title.txt: -------------------------------------------------------------------------------- 1 | PgBouncer stats -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v6/index-overview/title.txt: -------------------------------------------------------------------------------- 1 | Index overview -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v6/pgbouncer-stats/title.txt: -------------------------------------------------------------------------------- 1 | PgBouncer stats -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v6/recommendations/title.txt: -------------------------------------------------------------------------------- 1 | Recommendations -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v7/index-overview/title.txt: -------------------------------------------------------------------------------- 1 | Index overview -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v7/pgbouncer-stats/title.txt: -------------------------------------------------------------------------------- 1 | PgBouncer stats -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v7/recommendations/title.txt: -------------------------------------------------------------------------------- 1 | Recommendations -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v8/index-overview/title.txt: -------------------------------------------------------------------------------- 1 | Index overview -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v8/pgbouncer-stats/title.txt: -------------------------------------------------------------------------------- 1 | PgBouncer stats -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v8/recommendations/title.txt: -------------------------------------------------------------------------------- 1 | Recommendations -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v5/index-overview/title.txt: -------------------------------------------------------------------------------- 1 | Index overview -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v5/pgbouncer-stats/title.txt: -------------------------------------------------------------------------------- 1 | PgBouncer stats -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/alert-template/title.txt: -------------------------------------------------------------------------------- 1 | Alert template -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/index-overview/title.txt: -------------------------------------------------------------------------------- 1 | Index overview -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/pgbouncer-stats/title.txt: -------------------------------------------------------------------------------- 1 | PgBouncer stats -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/recommendations/title.txt: -------------------------------------------------------------------------------- 1 | Recommendations -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/alert-template/title.txt: -------------------------------------------------------------------------------- 1 | Alert template -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/index-overview/title.txt: -------------------------------------------------------------------------------- 1 | Index overview -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/pgbouncer-stats/title.txt: -------------------------------------------------------------------------------- 1 | PgBouncer stats -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/recommendations/title.txt: -------------------------------------------------------------------------------- 1 | Recommendations -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/alert-template/title.txt: -------------------------------------------------------------------------------- 1 | Alert template -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/index-overview/title.txt: -------------------------------------------------------------------------------- 1 | Index overview -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/pgbouncer-stats/title.txt: -------------------------------------------------------------------------------- 1 | PgBouncer stats -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/recommendations/title.txt: -------------------------------------------------------------------------------- 1 | Recommendations -------------------------------------------------------------------------------- /grafana_dashboards/prometheus/v7/query-details/title.txt: -------------------------------------------------------------------------------- 1 | Query details -------------------------------------------------------------------------------- /grafana_dashboards/prometheus/v7/table-details/title.txt: -------------------------------------------------------------------------------- 1 | Table details -------------------------------------------------------------------------------- /grafana_dashboards/prometheus/v8/table-details/title.txt: -------------------------------------------------------------------------------- 1 | Table details -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v4/aws-cloudwatch/title.txt: -------------------------------------------------------------------------------- 1 | AWS CloudWatch overview -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v4/global-db-overview/title.txt: -------------------------------------------------------------------------------- 1 | Global DB Overview -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v5/aws-cloudwatch/title.txt: -------------------------------------------------------------------------------- 1 | AWS CloudWatch overview -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v5/global-db-overview/title.txt: -------------------------------------------------------------------------------- 1 | Global DB Overview -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v6/aws-cloudwatch/title.txt: -------------------------------------------------------------------------------- 1 | AWS CloudWatch overview -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v6/global-db-overview/title.txt: -------------------------------------------------------------------------------- 1 | Global DB Overview -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v6/server-log-events/title.txt: -------------------------------------------------------------------------------- 1 | Server Log Events -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v7/aws-cloudwatch/title.txt: -------------------------------------------------------------------------------- 1 | AWS CloudWatch overview -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v7/global-db-overview/title.txt: -------------------------------------------------------------------------------- 1 | Global DB Overview -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v7/server-log-events/title.txt: -------------------------------------------------------------------------------- 1 | Server Log Events -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v8/aws-cloudwatch/title.txt: -------------------------------------------------------------------------------- 1 | AWS CloudWatch overview -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v8/global-db-overview/title.txt: -------------------------------------------------------------------------------- 1 | Global DB Overview -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v8/server-log-events/title.txt: -------------------------------------------------------------------------------- 1 | Server Log Events -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v5/aws-cloudwatch/title.txt: -------------------------------------------------------------------------------- 1 | AWS CloudWatch overview -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v5/global-db-overview/title.txt: -------------------------------------------------------------------------------- 1 | Global DB Overview -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/aws-cloudwatch/title.txt: -------------------------------------------------------------------------------- 1 | AWS CloudWatch overview -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/global-db-overview/title.txt: -------------------------------------------------------------------------------- 1 | Global DB Overview -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/server-log-events/title.txt: -------------------------------------------------------------------------------- 1 | Server Log Events -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/sessions-overview/title.txt: -------------------------------------------------------------------------------- 1 | Sessions overview -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/aws-cloudwatch/title.txt: -------------------------------------------------------------------------------- 1 | AWS CloudWatch overview -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/global-db-overview/title.txt: -------------------------------------------------------------------------------- 1 | Global DB Overview -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/server-log-events/title.txt: -------------------------------------------------------------------------------- 1 | Server Log Events -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/sessions-overview/title.txt: -------------------------------------------------------------------------------- 1 | Sessions overview -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/aws-cloudwatch/title.txt: -------------------------------------------------------------------------------- 1 | AWS CloudWatch overview -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/global-db-overview/title.txt: -------------------------------------------------------------------------------- 1 | Global DB Overview -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/server-log-events/title.txt: -------------------------------------------------------------------------------- 1 | Server Log Events -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/sessions-overview/title.txt: -------------------------------------------------------------------------------- 1 | Sessions overview -------------------------------------------------------------------------------- /grafana_dashboards/prometheus/v7/sessions-overview/title.txt: -------------------------------------------------------------------------------- 1 | Sessions overview -------------------------------------------------------------------------------- /grafana_dashboards/prometheus/v8/sessions-overview/title.txt: -------------------------------------------------------------------------------- 1 | Sessions overview -------------------------------------------------------------------------------- /pgwatch2/metrics/stat_io/metric_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | is_instance_level: true 3 | -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v4/biggest-relations/title.txt: -------------------------------------------------------------------------------- 1 | Biggest relations treemap -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v4/change-events/title.txt: -------------------------------------------------------------------------------- 1 | DDL / Config change events -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v4/single-query-details/title.txt: -------------------------------------------------------------------------------- 1 | Single query details -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v4/stat-statements-top/title.txt: -------------------------------------------------------------------------------- 1 | Stat statements Top -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v5/biggest-relations/title.txt: -------------------------------------------------------------------------------- 1 | Biggest relations treemap -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v5/change-events/title.txt: -------------------------------------------------------------------------------- 1 | DDL / Config change events -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v5/single-query-details/title.txt: -------------------------------------------------------------------------------- 1 | Single query details -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v5/stat-statements-top/title.txt: -------------------------------------------------------------------------------- 1 | Stat statements Top -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v6/biggest-relations/title.txt: -------------------------------------------------------------------------------- 1 | Biggest relations treemap -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v6/change-events/title.txt: -------------------------------------------------------------------------------- 1 | DDL / Config change events -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v6/single-query-details/title.txt: -------------------------------------------------------------------------------- 1 | Single query details -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v6/stat-statements-top/title.txt: -------------------------------------------------------------------------------- 1 | Stat statements Top -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v7/biggest-relations/title.txt: -------------------------------------------------------------------------------- 1 | Biggest relations treemap -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v7/change-events/title.txt: -------------------------------------------------------------------------------- 1 | DDL / Config change events -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v7/single-query-details/title.txt: -------------------------------------------------------------------------------- 1 | Single query details -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v7/stat-statements-top/title.txt: -------------------------------------------------------------------------------- 1 | Stat statements Top -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v8/biggest-relations/title.txt: -------------------------------------------------------------------------------- 1 | Biggest relations treemap -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v8/change-events/title.txt: -------------------------------------------------------------------------------- 1 | DDL / Config change events -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v8/single-query-details/title.txt: -------------------------------------------------------------------------------- 1 | Single query details -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v8/stat-statements-top/title.txt: -------------------------------------------------------------------------------- 1 | Stat statements Top -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v5/biggest-relations/title.txt: -------------------------------------------------------------------------------- 1 | Biggest relations treemap -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v5/change-events/title.txt: -------------------------------------------------------------------------------- 1 | DDL / Config change events -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v5/single-query-details/title.txt: -------------------------------------------------------------------------------- 1 | Single query details -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v5/stat-statements-top/title.txt: -------------------------------------------------------------------------------- 1 | Stat statements Top -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/biggest-relations/title.txt: -------------------------------------------------------------------------------- 1 | Biggest relations treemap -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/change-events/title.txt: -------------------------------------------------------------------------------- 1 | DDL / Config change events -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/single-query-details/title.txt: -------------------------------------------------------------------------------- 1 | Single query details -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/stat-statements-top/title.txt: -------------------------------------------------------------------------------- 1 | Stat statements Top -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/biggest-relations/title.txt: -------------------------------------------------------------------------------- 1 | Biggest relations treemap -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/change-events/title.txt: -------------------------------------------------------------------------------- 1 | DDL / Config change events -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/single-query-details/title.txt: -------------------------------------------------------------------------------- 1 | Single query details -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/stat-statements-top/title.txt: -------------------------------------------------------------------------------- 1 | Stat statements Top -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/biggest-relations/title.txt: -------------------------------------------------------------------------------- 1 | Biggest relations treemap -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/change-events/title.txt: -------------------------------------------------------------------------------- 1 | DDL / Config change events -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/single-query-details/title.txt: -------------------------------------------------------------------------------- 1 | Single query details -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/stat-statements-top/title.txt: -------------------------------------------------------------------------------- 1 | Stat statements Top -------------------------------------------------------------------------------- /pgwatch2/metrics/archiver/metric_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | is_instance_level: true 3 | -------------------------------------------------------------------------------- /pgwatch2/metrics/bgwriter/metric_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | is_instance_level: true 3 | -------------------------------------------------------------------------------- /pgwatch2/metrics/cpu_load/metric_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | is_instance_level: true 3 | -------------------------------------------------------------------------------- /pgwatch2/metrics/psutil_cpu/metric_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | is_instance_level: true 3 | -------------------------------------------------------------------------------- /pgwatch2/metrics/psutil_disk/metric_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | is_instance_level: true 3 | -------------------------------------------------------------------------------- /pgwatch2/metrics/psutil_mem/metric_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | is_instance_level: true 3 | -------------------------------------------------------------------------------- /pgwatch2/metrics/replication/metric_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | is_instance_level: true 3 | -------------------------------------------------------------------------------- /pgwatch2/metrics/wal_receiver/metric_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | is_instance_level: true 3 | -------------------------------------------------------------------------------- /pgwatch2/metrics/wal_size/metric_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | is_instance_level: true 3 | -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v6/stat-activity-realtime/title.txt: -------------------------------------------------------------------------------- 1 | Stat Activity Realtime -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v7/stat-activity-realtime/title.txt: -------------------------------------------------------------------------------- 1 | Stat Activity Realtime -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v8/stat-activity-realtime/title.txt: -------------------------------------------------------------------------------- 1 | Stat Activity Realtime -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/show-plans-realtime/title.txt: -------------------------------------------------------------------------------- 1 | Show EXPLAIN plans realtime -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/stat-activity-realtime/title.txt: -------------------------------------------------------------------------------- 1 | Stat Activity Realtime -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/show-plans-realtime/title.txt: -------------------------------------------------------------------------------- 1 | Show EXPLAIN plans realtime -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/stat-activity-realtime/title.txt: -------------------------------------------------------------------------------- 1 | Stat Activity Realtime -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/show-plans-realtime/title.txt: -------------------------------------------------------------------------------- 1 | Show EXPLAIN plans realtime -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/stat-activity-realtime/title.txt: -------------------------------------------------------------------------------- 1 | Stat Activity Realtime -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/stat-activity/title.txt: -------------------------------------------------------------------------------- 1 | Grid control Stat Activity 2 | -------------------------------------------------------------------------------- /pgwatch2/bootstrap/create_db_pgwatch.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE pgwatch2 OWNER pgwatch2; 2 | -------------------------------------------------------------------------------- /pgwatch2/metrics/backup_age_walg/metric_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | is_instance_level: true 3 | -------------------------------------------------------------------------------- /pgwatch2/metrics/buffercache_by_db/metric_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | is_instance_level: true 3 | -------------------------------------------------------------------------------- /pgwatch2/metrics/db_size/metric_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | statement_timeout_seconds: 300 3 | -------------------------------------------------------------------------------- /pgwatch2/metrics/replication_slots/metric_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | is_instance_level: true 3 | -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v4/stat-statements-overview/title.txt: -------------------------------------------------------------------------------- 1 | Stat statements overview -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v5/stat-statements-overview/title.txt: -------------------------------------------------------------------------------- 1 | Stat statements overview -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v6/postgres-version-overview/title.txt: -------------------------------------------------------------------------------- 1 | Postgres Version Overview -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v6/stat-statements-overview/title.txt: -------------------------------------------------------------------------------- 1 | Stat statements overview -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v6/stat-statements-sql-search/title.txt: -------------------------------------------------------------------------------- 1 | Stat Statements SQL Search -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v7/postgres-version-overview/title.txt: -------------------------------------------------------------------------------- 1 | Postgres Version Overview -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v7/stat-statements-overview/title.txt: -------------------------------------------------------------------------------- 1 | Stat statements overview -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v7/stat-statements-sql-search/title.txt: -------------------------------------------------------------------------------- 1 | Stat Statements SQL Search -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v8/postgres-version-overview/title.txt: -------------------------------------------------------------------------------- 1 | Postgres Version Overview -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v8/stat-statements-overview/title.txt: -------------------------------------------------------------------------------- 1 | Stat statements overview -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v8/stat-statements-sql-search/title.txt: -------------------------------------------------------------------------------- 1 | Stat Statements SQL Search -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/db-overview-time-lag/title.txt: -------------------------------------------------------------------------------- 1 | DB overview time lag comparison -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/postgres-version-overview/title.txt: -------------------------------------------------------------------------------- 1 | Postgres Version Overview -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/stat-statements-sql-search/title.txt: -------------------------------------------------------------------------------- 1 | Stat Statements SQL Search -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/stat-statements-top-fast/title.txt: -------------------------------------------------------------------------------- 1 | Stat statements Top (Fast) -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/db-overview-time-lag/title.txt: -------------------------------------------------------------------------------- 1 | DB overview time lag comparison -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/postgres-version-overview/title.txt: -------------------------------------------------------------------------------- 1 | Postgres Version Overview -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/stat-statements-sql-search/title.txt: -------------------------------------------------------------------------------- 1 | Stat Statements SQL Search -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/stat-statements-top-fast/title.txt: -------------------------------------------------------------------------------- 1 | Stat statements Top (Fast) -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/db-overview-time-lag/title.txt: -------------------------------------------------------------------------------- 1 | DB overview time lag comparison -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/postgres-version-overview/title.txt: -------------------------------------------------------------------------------- 1 | Postgres Version Overview -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/stat-statements-sql-search/title.txt: -------------------------------------------------------------------------------- 1 | Stat Statements SQL Search -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/stat-statements-top-fast/title.txt: -------------------------------------------------------------------------------- 1 | Stat statements Top (Fast) -------------------------------------------------------------------------------- /grafana_dashboards/prometheus/v7/postgres-version-overview/title.txt: -------------------------------------------------------------------------------- 1 | Postgres version overview -------------------------------------------------------------------------------- /pgwatch2/bootstrap/create_db_grafana.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE pgwatch2_grafana OWNER pgwatch2; 2 | -------------------------------------------------------------------------------- /pgwatch2/metrics/backup_age_pgbackrest/metric_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | is_instance_level: true 3 | -------------------------------------------------------------------------------- /pgwatch2/metrics/buffercache_by_type/metric_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | is_instance_level: true 3 | -------------------------------------------------------------------------------- /pgwatch2/metrics/db_size_approx/metric_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | metric_storage_name: db_size 3 | -------------------------------------------------------------------------------- /pgwatch2/metrics/db_stats_aurora/metric_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | metric_storage_name: db_stats 3 | -------------------------------------------------------------------------------- /pgwatch2/metrics/psutil_disk_io_total/metric_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | is_instance_level: true 3 | -------------------------------------------------------------------------------- /pgwatch2/metrics/table_stats/metric_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | statement_timeout_seconds: 300 3 | -------------------------------------------------------------------------------- /pgwatch2/metrics/table_stats_approx/metric_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | metric_storage_name: table_stats -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/stat-statements-top-visual/title.txt: -------------------------------------------------------------------------------- 1 | Stat Statements Top (Visual) -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/system-stats-time-lag/title.txt: -------------------------------------------------------------------------------- 1 | Systems stats time lag comparison -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/table-details-time-lag/title.txt: -------------------------------------------------------------------------------- 1 | Table details time lag comparison -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/stat-statements-top-visual/title.txt: -------------------------------------------------------------------------------- 1 | Stat Statements Top (Visual) -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/system-stats-time-lag/title.txt: -------------------------------------------------------------------------------- 1 | Systems stats time lag comparison -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/table-details-time-lag/title.txt: -------------------------------------------------------------------------------- 1 | Table details time lag comparison -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/stat-statements-top-visual/title.txt: -------------------------------------------------------------------------------- 1 | Stat Statements Top (Visual) -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/system-stats-time-lag/title.txt: -------------------------------------------------------------------------------- 1 | Systems stats time lag comparison -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/table-details-time-lag/title.txt: -------------------------------------------------------------------------------- 1 | Table details time lag comparison -------------------------------------------------------------------------------- /pgwatch2/bootstrap/create_db_metric_store.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE pgwatch2_metrics OWNER pgwatch2; 2 | -------------------------------------------------------------------------------- /pgwatch2/metrics/db_size_approx/column_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prometheus_all_gauge_columns: true 3 | -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v5/db-overview-developer/title.txt: -------------------------------------------------------------------------------- 1 | DB overview Unprivileged / Developer mode -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v6/db-overview-developer/title.txt: -------------------------------------------------------------------------------- 1 | DB overview Unprivileged / Developer mode -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v7/db-overview-developer/title.txt: -------------------------------------------------------------------------------- 1 | DB overview Unprivileged / Developer mode -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v8/db-overview-developer/title.txt: -------------------------------------------------------------------------------- 1 | DB overview Unprivileged / Developer mode -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v5/db-overview-developer/title.txt: -------------------------------------------------------------------------------- 1 | DB overview Unprivileged / Developer mode -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/db-overview-developer/title.txt: -------------------------------------------------------------------------------- 1 | DB overview Unprivileged / Developer mode -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/db-overview-developer/title.txt: -------------------------------------------------------------------------------- 1 | DB overview Unprivileged / Developer mode -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/db-overview-developer/title.txt: -------------------------------------------------------------------------------- 1 | DB overview Unprivileged / Developer mode -------------------------------------------------------------------------------- /pgwatch2/metrics/logical_subscriptions/column_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prometheus_all_gauge_columns: true 3 | -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v5/checkpointer-bgwriter-stats/title.txt: -------------------------------------------------------------------------------- 1 | Checkpointer / Bgwriter / Block IO Stats -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v6/checkpointer-bgwriter-stats/title.txt: -------------------------------------------------------------------------------- 1 | Checkpointer / Bgwriter / Block IO Stats -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v7/checkpointer-bgwriter-stats/title.txt: -------------------------------------------------------------------------------- 1 | Checkpointer / Bgwriter / Block IO Stats -------------------------------------------------------------------------------- /grafana_dashboards/influxdb/v8/checkpointer-bgwriter-stats/title.txt: -------------------------------------------------------------------------------- 1 | Checkpointer / Bgwriter / Block IO Stats -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v5/checkpointer-bgwriter-stats/title.txt: -------------------------------------------------------------------------------- 1 | Checkpointer / Bgwriter / Block IO Stats -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v6/checkpointer-bgwriter-stats/title.txt: -------------------------------------------------------------------------------- 1 | Checkpointer / Bgwriter / Block IO Stats -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v7/checkpointer-bgwriter-stats/title.txt: -------------------------------------------------------------------------------- 1 | Checkpointer / Bgwriter / Block IO Stats -------------------------------------------------------------------------------- /grafana_dashboards/postgres/v8/checkpointer-bgwriter-stats/title.txt: -------------------------------------------------------------------------------- 1 | Checkpointer / Bgwriter / Block IO Stats -------------------------------------------------------------------------------- /grafana_dashboards/prometheus/v5/db-overview-developer/title.txt: -------------------------------------------------------------------------------- 1 | DB overview Unprivileged / Developer mode -------------------------------------------------------------------------------- /pgwatch2/bootstrap/grant_monitor_to_pgwatch2.sql: -------------------------------------------------------------------------------- 1 | GRANT pg_monitor TO pgwatch2; -- will fail for PG < v10 2 | -------------------------------------------------------------------------------- /webpy/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/webpy/static/logo.png -------------------------------------------------------------------------------- /screenshots/overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/overview.png -------------------------------------------------------------------------------- /.secrets.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | exclude_files: 3 | - "README.md" 4 | - "docker/Dockerfile-postgres" 5 | - "docs/*" 6 | -------------------------------------------------------------------------------- /pgwatch2/metrics/stat_statements/metric_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prerequisite_extensions: 3 | - pg_stat_statements 4 | -------------------------------------------------------------------------------- /pgwatch2/metrics/wal/column_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prometheus_all_gauge_columns: true 3 | prometheus_ignored_columns: 4 | -------------------------------------------------------------------------------- /screenshots/tables_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/tables_top.png -------------------------------------------------------------------------------- /screenshots/web_ui_dbs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/web_ui_dbs.png -------------------------------------------------------------------------------- /pgwatch2/metrics/cpu_load/column_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prometheus_all_gauge_columns: true 3 | prometheus_ignored_columns: 4 | -------------------------------------------------------------------------------- /pgwatch2/metrics/db_size/column_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prometheus_all_gauge_columns: true 3 | prometheus_ignored_columns: 4 | -------------------------------------------------------------------------------- /pgwatch2/metrics/locks/column_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prometheus_all_gauge_columns: true 3 | prometheus_ignored_columns: 4 | -------------------------------------------------------------------------------- /pgwatch2/metrics/stat_ssl/column_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prometheus_all_gauge_columns: true 3 | prometheus_ignored_columns: 4 | -------------------------------------------------------------------------------- /pgwatch2/metrics/stat_statements_calls/metric_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prerequisite_extensions: 3 | - pg_stat_statements 4 | -------------------------------------------------------------------------------- /pgwatch2/metrics/wal_size/column_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prometheus_all_gauge_columns: true 3 | prometheus_ignored_columns: 4 | -------------------------------------------------------------------------------- /screenshots/global_health.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/global_health.png -------------------------------------------------------------------------------- /screenshots/health_check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/health_check.png -------------------------------------------------------------------------------- /screenshots/pgpool_status.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/pgpool_status.png -------------------------------------------------------------------------------- /webpy/requirements_pg_metrics.txt: -------------------------------------------------------------------------------- 1 | cherrypy 2 | jinja2 3 | psycopg2-binary 4 | decorator 5 | cryptography 6 | requests 7 | -------------------------------------------------------------------------------- /pgwatch2/metrics/backends/column_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prometheus_all_gauge_columns: true 3 | prometheus_ignored_columns: 4 | -------------------------------------------------------------------------------- /pgwatch2/metrics/blocking_locks/column_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prometheus_all_gauge_columns: true 3 | prometheus_ignored_columns: 4 | -------------------------------------------------------------------------------- /pgwatch2/metrics/locks_mode/column_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prometheus_all_gauge_columns: true 3 | prometheus_ignored_columns: 4 | -------------------------------------------------------------------------------- /pgwatch2/metrics/psutil_cpu/column_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prometheus_all_gauge_columns: true 3 | prometheus_ignored_columns: 4 | -------------------------------------------------------------------------------- /pgwatch2/metrics/psutil_disk/column_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prometheus_all_gauge_columns: true 3 | prometheus_ignored_columns: 4 | -------------------------------------------------------------------------------- /pgwatch2/metrics/psutil_mem/column_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prometheus_all_gauge_columns: true 3 | prometheus_ignored_columns: 4 | -------------------------------------------------------------------------------- /pgwatch2/metrics/replication/column_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prometheus_all_gauge_columns: true 3 | prometheus_ignored_columns: 4 | -------------------------------------------------------------------------------- /pgwatch2/metrics/wal_receiver/column_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prometheus_all_gauge_columns: true 3 | prometheus_ignored_columns: 4 | -------------------------------------------------------------------------------- /screenshots/alert_template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/alert_template.png -------------------------------------------------------------------------------- /screenshots/index_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/index_overview.png -------------------------------------------------------------------------------- /screenshots/pgbouncer_stats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/pgbouncer_stats.png -------------------------------------------------------------------------------- /screenshots/recommendations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/recommendations.png -------------------------------------------------------------------------------- /screenshots/replication_lag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/replication_lag.png -------------------------------------------------------------------------------- /pgwatch2/bootstrap/change_pw.sql: -------------------------------------------------------------------------------- 1 | CREATE ROLE pgwatch2 WITH LOGIN PASSWORD 'pgwatch2admin'; -- NB! change the pw for production 2 | -------------------------------------------------------------------------------- /pgwatch2/bootstrap/revoke_public_create.sql: -------------------------------------------------------------------------------- 1 | REVOKE CREATE ON SCHEMA public FROM PUBLIC; 2 | GRANT ALL ON SCHEMA public TO pgwatch2; -------------------------------------------------------------------------------- /pgwatch2/metrics/buffercache_by_db/column_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prometheus_all_gauge_columns: true 3 | prometheus_ignored_columns: 4 | -------------------------------------------------------------------------------- /pgwatch2/metrics/buffercache_by_type/column_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prometheus_all_gauge_columns: true 3 | prometheus_ignored_columns: 4 | -------------------------------------------------------------------------------- /pgwatch2/metrics/replication_slots/column_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prometheus_all_gauge_columns: true 3 | prometheus_ignored_columns: 4 | -------------------------------------------------------------------------------- /pgwatch2/sql/config_store/migrations/v1.6.0-2_metric_column_attrs.sql: -------------------------------------------------------------------------------- 1 | alter table pgwatch2.metric 2 | add m_column_attrs jsonb; 3 | -------------------------------------------------------------------------------- /screenshots/overview_developer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/overview_developer.png -------------------------------------------------------------------------------- /screenshots/server_log_events.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/server_log_events.png -------------------------------------------------------------------------------- /screenshots/sessions_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/sessions_overview.png -------------------------------------------------------------------------------- /pgwatch2/metrics/recommendations/9.0/metric.sql: -------------------------------------------------------------------------------- 1 | /* dummy placeholder - special handling in code to collect other metrics named reco_* */ -------------------------------------------------------------------------------- /screenshots/checkpointer_bgwriter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/checkpointer_bgwriter.png -------------------------------------------------------------------------------- /screenshots/overview_light_theme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/overview_light_theme.png -------------------------------------------------------------------------------- /screenshots/pgwatch2_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/pgwatch2_architecture.png -------------------------------------------------------------------------------- /screenshots/show_plans_realtime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/show_plans_realtime.png -------------------------------------------------------------------------------- /screenshots/stat_statements_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/stat_statements_top.png -------------------------------------------------------------------------------- /screenshots/system_stats_psutil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/system_stats_psutil.png -------------------------------------------------------------------------------- /webpy/requirements_influx_metrics.txt: -------------------------------------------------------------------------------- 1 | cherrypy 2 | jinja2 3 | psycopg2-binary 4 | influxdb 5 | decorator 6 | cryptography 7 | requests 8 | -------------------------------------------------------------------------------- /pgwatch2/metrics/server_log_event_counts/9.0/metric.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Dummy placeholder - special handling in gatherer code for log parsing 3 | */ -------------------------------------------------------------------------------- /pgwatch2/metrics/table_bloat_approx_stattuple/column_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prometheus_all_gauge_columns: true 3 | prometheus_ignored_columns: 4 | -------------------------------------------------------------------------------- /pgwatch2/metrics/table_bloat_approx_summary/column_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prometheus_all_gauge_columns: true 3 | prometheus_ignored_columns: 4 | -------------------------------------------------------------------------------- /screenshots/stat_activity_realtime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/stat_activity_realtime.png -------------------------------------------------------------------------------- /pgwatch2/metrics/change_events/README.md: -------------------------------------------------------------------------------- 1 | ### A dummy "master metric" definition that uses other *_hashes metrics internally in the daemon code 2 | -------------------------------------------------------------------------------- /pgwatch2/metrics/table_bloat_approx_summary_sql/column_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prometheus_all_gauge_columns: true 3 | prometheus_ignored_columns: 4 | -------------------------------------------------------------------------------- /screenshots/biggest_relations_treemap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/biggest_relations_treemap.png -------------------------------------------------------------------------------- /screenshots/pgwatch2_architecture_push.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/pgwatch2_architecture_push.png -------------------------------------------------------------------------------- /screenshots/postres_versions_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/postres_versions_overview.png -------------------------------------------------------------------------------- /screenshots/stat_statements_sql_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/stat_statements_sql_search.png -------------------------------------------------------------------------------- /screenshots/stat_statements_top_visual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/stat_statements_top_visual.png -------------------------------------------------------------------------------- /screenshots/aws_rds_cloudwatch_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/aws_rds_cloudwatch_overview.png -------------------------------------------------------------------------------- /docker/test/launch_all_pg_versions/get-all-pg-versions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for ver in 9.{0..6} {10..12} ; do 4 | docker pull postgres:$ver 5 | done 6 | -------------------------------------------------------------------------------- /screenshots/db_overview_time_lag_comparison.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/db_overview_time_lag_comparison.png -------------------------------------------------------------------------------- /screenshots/pgwatch2_change_detected_dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/pgwatch2_change_detected_dash.png -------------------------------------------------------------------------------- /pg_hba.conf: -------------------------------------------------------------------------------- 1 | local all all trust 2 | host all all 127.0.0.1/32 trust 3 | host all all ::1/128 trust 4 | host all all 0.0.0.0/0 md5 5 | host all all ::0/0 md5 6 | -------------------------------------------------------------------------------- /screenshots/web_ui_metrics_and_preset_configs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybertec-postgresql/pgwatch2/HEAD/screenshots/web_ui_metrics_and_preset_configs.png -------------------------------------------------------------------------------- /pgwatch2/metrics/archiver/column_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prometheus_gauge_columns: 3 | - is_failing_int 4 | - seconds_since_last_failure 5 | prometheus_ignored_columns: 6 | -------------------------------------------------------------------------------- /pgwatch2/metrics/stat_statements_no_query_text/metric_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | metric_storage_name: stat_statements 3 | prerequisite_extensions: 4 | - pg_stat_statements 5 | -------------------------------------------------------------------------------- /pgwatch2/metrics/table_bloat_approx_stattuple/metric_attrs.yaml: -------------------------------------------------------------------------------- 1 | #disabled_days: "1-5" 2 | #disabled_times: # NB! in timezone of pgwatch2 server 3 | # - "09:00-18:00" 4 | -------------------------------------------------------------------------------- /pgwatch2/metrics/wal_size/10/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | get_wal_size() as wal_size_b; 4 | -------------------------------------------------------------------------------- /pgwatch2/metrics/wal_size/9.0/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | get_wal_size() as wal_size_b; 4 | -------------------------------------------------------------------------------- /pgwatch2/sql/config_store/migrations/v1.5.1_metric_name_pattern.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE metric 2 | ADD CONSTRAINT metric_m_name_check CHECK ((m_name ~ '^[a-z0-9_]+$'::text)); 3 | -------------------------------------------------------------------------------- /pgwatch2/sql/config_store/migrations/v1.2.1_metric_version_to_numeric.sql: -------------------------------------------------------------------------------- 1 | begin; 2 | 3 | alter table pgwatch2.metric alter column m_pg_version_from type numeric; 4 | 5 | end; 6 | -------------------------------------------------------------------------------- /pgwatch2/metrics/db_stats/column_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prometheus_gauge_columns: 3 | - numbackends 4 | - postmaster_uptime_s 5 | - checksum_last_failure_s 6 | prometheus_ignored_columns: 7 | -------------------------------------------------------------------------------- /pgwatch2/metrics/kpi/column_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prometheus_gauge_columns: 3 | - numbackends 4 | - active_backends 5 | - blocked_backends 6 | - kpi_oldest_tx_s 7 | prometheus_ignored_columns: 8 | -------------------------------------------------------------------------------- /pgwatch2/sql/config_store/migrations/v1.4.0_group.sql: -------------------------------------------------------------------------------- 1 | begin; 2 | alter table pgwatch2.monitored_db 3 | add md_group text not null default 'default' check (md_group ~ E'\\w+'); 4 | commit; 5 | 6 | -------------------------------------------------------------------------------- /pgwatch2/sql/config_store/migrations/v1.3.0_monitored_db_dbtype.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE pgwatch2.monitored_db 2 | ADD md_dbtype text NOT NULL DEFAULT 'postgres' 3 | CHECK (md_dbtype in ('postgres', 'pgbouncer')); 4 | -------------------------------------------------------------------------------- /docker/test/launch_all_pg_versions/stop-all-pg-versions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for ver in 9{0..6} {10..12} ; do 4 | echo "stopping PG $ver ..." 5 | docker stop "pg${ver}" 6 | docker stop "pg${ver}-repl" 7 | done 8 | -------------------------------------------------------------------------------- /pgwatch2/sql/metric_store/roll_out_metric.psql: -------------------------------------------------------------------------------- 1 | begin; 2 | 3 | \i 00_schema_base.sql 4 | \i 01_old_metrics_cleanup_procedure.sql 5 | \i metric/ensure_partition.sql 6 | \i metric/metric_store_simple.sql 7 | 8 | commit; 9 | -------------------------------------------------------------------------------- /docker/test/launch_all_pg_versions/pause-all-pg-versions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for ver in 9{0..6} {10..12} ; do 4 | echo "pausing PG $ver ..." 5 | docker pause "pg${ver}" 6 | docker pause "pg${ver}-repl" 7 | done 8 | -------------------------------------------------------------------------------- /pgwatch2/metrics/db_stats_aurora/column_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prometheus_gauge_columns: 3 | - numbackends 4 | - postmaster_uptime_s 5 | - backup_duration_s 6 | - checksum_last_failure_s 7 | prometheus_ignored_columns: 8 | -------------------------------------------------------------------------------- /docker/test/launch_all_pg_versions/unpause-all-pg-versions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for ver in 9{0..6} {10..12} ; do 4 | echo "unpausing PG $ver ..." 5 | docker unpause "pg${ver}" 6 | docker unpause "pg${ver}-repl" 7 | done 8 | -------------------------------------------------------------------------------- /pgwatch2/sql/config_store/migrations/v1.6.2_superuser_metrics.sql: -------------------------------------------------------------------------------- 1 | begin; 2 | 3 | alter table pgwatch2.metric add m_sql_su text default ''; 4 | 5 | insert into pgwatch2.schema_version (sv_tag) values ('1.6.2'); 6 | 7 | commit; 8 | -------------------------------------------------------------------------------- /pgwatch2/metrics/backup_age_walg/9.1/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | retcode, 4 | backup_age_seconds, 5 | message 6 | from 7 | get_backup_age_walg() 8 | ; -------------------------------------------------------------------------------- /pgwatch2/metrics/smart_health_per_disk/9.1/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | device as tag_device, 4 | retcode 5 | from 6 | get_smart_health_per_device(); 7 | -------------------------------------------------------------------------------- /pgwatch2/metrics/configuration_hashes/9.0/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | name as tag_setting, 4 | coalesce(reset_val, '') as value 5 | from 6 | pg_settings; 7 | -------------------------------------------------------------------------------- /pgwatch2/metrics/backup_age_pgbackrest/9.1/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | retcode, 4 | backup_age_seconds, 5 | message 6 | from 7 | get_backup_age_pgbackrest() 8 | ; -------------------------------------------------------------------------------- /pgwatch2/sql/metric_store/roll_out_metric_time.psql: -------------------------------------------------------------------------------- 1 | begin; 2 | 3 | \i 00_schema_base.sql 4 | \i 01_old_metrics_cleanup_procedure.sql 5 | \i metric-time/ensure_partition_metric_time.sql 6 | \i metric-time/metric_store_part_time.sql 7 | 8 | commit; 9 | -------------------------------------------------------------------------------- /pgwatch2/metrics/wal_size/9.0/metric_su.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | sum((pg_stat_file('pg_xlog/'||f)).size)::int8 as wal_size_b from (select pg_ls_dir('pg_xlog') f) ls 4 | ; 5 | -------------------------------------------------------------------------------- /pgwatch2/sql/config_store/migrations/v1.5.0-2_password_encryption.sql: -------------------------------------------------------------------------------- 1 | begin; 2 | 3 | alter table monitored_db 4 | add md_password_type text not null default 'plain-text' 5 | CHECK (md_password_type in ('plain-text', 'aes-gcm-256')); 6 | 7 | commit; 8 | -------------------------------------------------------------------------------- /pgwatch2/bootstrap/supervisord_bootstrap_pg.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for prog in postgres grafana grafana_dashboard_setup pgwatch2 webpy ; do 4 | echo "supervisorctl start $prog ..." 5 | supervisorctl start $prog 6 | echo "sleep 5" 7 | sleep 5 8 | done 9 | -------------------------------------------------------------------------------- /pgwatch2/bootstrap/supervisord_bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for prog in postgres grafana grafana_dashboard_setup influxdb pgwatch2 webpy ; do 4 | echo "supervisorctl start $prog ..." 5 | supervisorctl start $prog 6 | echo "sleep 5" 7 | sleep 5 8 | done 9 | -------------------------------------------------------------------------------- /pgwatch2/metrics/subscription_stats/15/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | subname::text as tag_subname, 4 | apply_error_count, 5 | sync_error_count 6 | from 7 | pg_stat_subscription_stats; 8 | -------------------------------------------------------------------------------- /pgwatch2/metrics/psutil_disk_io_total/9.0/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | read_count, 4 | write_count, 5 | read_bytes, 6 | write_bytes 7 | from 8 | get_psutil_disk_io_total() 9 | ; 10 | -------------------------------------------------------------------------------- /pgwatch2/metrics/wal/metric_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | is_instance_level: true 3 | #statement_timeout_seconds: 15 4 | #disabled_days: "1-5" 5 | #disabled_times: # NB! in timezone of pgwatch2 server if not TZ specified after the time range 6 | # - "09:00-21:00" 7 | # - "22:00-05:00" 8 | -------------------------------------------------------------------------------- /docker/test/smoke_test_all_latest_images.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | ./smoke_test_docker_image.sh influx cybertec/pgwatch2:latest 6 | ./smoke_test_docker_image.sh influx cybertec/pgwatch2-nonroot:latest 7 | ./smoke_test_docker_image.sh pg cybertec/pgwatch2-postgres:latest 8 | -------------------------------------------------------------------------------- /pgwatch2/metrics/psutil_disk/9.0/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | dir_or_tablespace as tag_dir_or_tablespace, 4 | path as tag_path, 5 | total, used, free, percent 6 | from 7 | get_psutil_disk() 8 | ; 9 | -------------------------------------------------------------------------------- /pgwatch2/metrics/psutil_mem/9.0/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | total, used, free, buff_cache, available, percent, 4 | swap_total, swap_used, swap_free, swap_percent 5 | from 6 | get_psutil_mem() 7 | ; 8 | -------------------------------------------------------------------------------- /pgwatch2/sql/metric_store/roll_out_metric_dbname_time.psql: -------------------------------------------------------------------------------- 1 | begin; 2 | 3 | \i 00_schema_base.sql 4 | \i 01_old_metrics_cleanup_procedure.sql 5 | \i metric-dbname-time/ensure_partition_metric_dbname_time.sql 6 | \i metric-dbname-time/metric_store_part_dbname_time.sql 7 | 8 | commit; 9 | -------------------------------------------------------------------------------- /pgwatch2/sql/config_store/migrations/v1.7.1-1_patroni_allow_multi.sql: -------------------------------------------------------------------------------- 1 | begin; 2 | 3 | drop index monitored_db_md_hostname_md_port_md_dbname_md_is_enabled_idx; 4 | 5 | create unique index on monitored_db(md_hostname, md_port, md_dbname, md_is_enabled) where not md_dbtype ~ 'patroni'; 6 | 7 | end; 8 | -------------------------------------------------------------------------------- /pgwatch2/metrics/table_stats/column_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prometheus_gauge_columns: 3 | - table_size_b 4 | - total_relation_size_b 5 | - toast_size_b 6 | - seconds_since_last_vacuum 7 | - seconds_since_last_analyze 8 | - n_live_tup 9 | - n_dead_tup 10 | prometheus_ignored_columns: 11 | -------------------------------------------------------------------------------- /pgwatch2/metrics/vmstat/9.1/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | r, b, swpd, free, buff, cache, si, so, bi, bo, "in", cs, us, sy, id, wa, st, cpu_count, load_1m, load_5m, load_15m, total_memory 4 | from 5 | get_vmstat(); 6 | 7 | -------------------------------------------------------------------------------- /pgwatch2/metrics/table_stats_approx/column_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prometheus_gauge_columns: 3 | - table_size_b 4 | - total_relation_size_b 5 | - toast_size_b 6 | - seconds_since_last_vacuum 7 | - seconds_since_last_analyze 8 | - n_live_tup 9 | - n_dead_tup 10 | prometheus_ignored_columns: 11 | -------------------------------------------------------------------------------- /pgwatch2/sql/config_store/remove_config_schema.sql: -------------------------------------------------------------------------------- 1 | begin; 2 | drop table if exists pgwatch2.monitored_db; 3 | drop table if exists pgwatch2.metric_attribute; 4 | drop table if exists pgwatch2.metric; 5 | drop table if exists pgwatch2.preset_config; 6 | drop table if exists pgwatch2.schema_version; 7 | commit; 8 | -------------------------------------------------------------------------------- /pgwatch2/metrics/pgpool_stats/3.0/metric.sql: -------------------------------------------------------------------------------- 1 | /* SHOW POOL_NODES expected to be 1st "command" */ 2 | SHOW POOL_NODES; 3 | /* special handling in code - when below SHOW POOL_PROCESSES line is defined pgpool_stats will have additional summary columns: 4 | processes_total, processes_active */ 5 | SHOW POOL_PROCESSES; 6 | -------------------------------------------------------------------------------- /pgwatch2/metrics/reco_add_index_ext_qualstats_2.0/metric_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # not an "independent" regular metric, but only used for overriding SQL definitions of other metrics, "pointing" to the private ones 3 | # via the "extension_version_based_overrides.target_metric_name" attribute currently 4 | is_private: true 5 | -------------------------------------------------------------------------------- /pgwatch2/metrics/wal_receiver/10/metric_standby.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | pg_wal_lsn_diff(pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn())::int8 as replay_lag_b, 4 | extract(epoch from (now() - pg_last_xact_replay_timestamp()))::int8 as last_replay_s; 5 | -------------------------------------------------------------------------------- /pgwatch2/sql/config_store/migrations/v1.5.0-3_standby-only_metrics.sql: -------------------------------------------------------------------------------- 1 | begin; 2 | 3 | alter table metric 4 | add m_master_only bool default false, 5 | add m_standby_only bool default false; 6 | 7 | alter table metric 8 | add constraint metric_check check (not (m_master_only and m_standby_only)); 9 | 10 | commit; 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Packages ouput folder 15 | dist 16 | 17 | # delve debugger file 18 | debug 19 | -------------------------------------------------------------------------------- /pgwatch2/metrics/00_helpers/get_sequences/10/metric.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION get_sequences() RETURNS SETOF pg_sequences AS 2 | $$ 3 | select * from pg_sequences 4 | $$ LANGUAGE sql VOLATILE SECURITY DEFINER; 5 | 6 | GRANT EXECUTE ON FUNCTION get_sequences() TO pgwatch2; 7 | COMMENT ON FUNCTION get_sequences() IS 'created for pgwatch2'; 8 | -------------------------------------------------------------------------------- /pgwatch2/bootstrap/grafana_datasource.sql: -------------------------------------------------------------------------------- 1 | insert into data_source (org_id, version, type, name, access, url, 2 | password, "user", database, basic_auth, is_default, json_data, created, updated 3 | ) values ( 4 | 1, 0, 'influxdb', 'Influx', 'proxy', 'http://localhost:8086', 5 | 'root', 'root', 'pgwatch2', 'f', 't', '{}', now(), now() 6 | ); 7 | -------------------------------------------------------------------------------- /pgwatch2/metrics/wal_receiver/9.2/metric_standby.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | pg_xlog_location_diff(pg_last_xlog_receive_location(), pg_last_xlog_replay_location())::int8 as replay_lag_b, 4 | extract(epoch from (now() - pg_last_xact_replay_timestamp()))::int8 as last_replay_s; 5 | -------------------------------------------------------------------------------- /pgwatch2/metrics/bgwriter/9.0/metric_master.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | checkpoints_timed, 4 | checkpoints_req, 5 | buffers_checkpoint, 6 | buffers_clean, 7 | maxwritten_clean, 8 | buffers_backend, 9 | buffers_alloc 10 | from 11 | pg_stat_bgwriter; 12 | -------------------------------------------------------------------------------- /pgwatch2/metrics/database_conflicts/9.2/metric_standby.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | confl_tablespace, 4 | confl_lock, 5 | confl_snapshot, 6 | confl_bufferpin, 7 | confl_deadlock 8 | FROM 9 | pg_stat_database_conflicts 10 | WHERE 11 | datname = current_database(); 12 | -------------------------------------------------------------------------------- /pgwatch2/metrics/buffercache_by_db/9.2/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | datname as tag_database, 4 | count(*) * (current_setting('block_size')::int8) as size_b 5 | FROM 6 | pg_buffercache AS b, 7 | pg_database AS d 8 | WHERE 9 | d.oid = b.reldatabase 10 | GROUP BY 11 | datname; 12 | -------------------------------------------------------------------------------- /pgwatch2/metrics/00_helpers/get_wal_size/10/metric.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION get_wal_size() RETURNS int8 AS 2 | $$ 3 | select (sum((pg_stat_file('pg_wal/' || name)).size))::int8 from pg_ls_waldir() 4 | $$ LANGUAGE sql VOLATILE SECURITY DEFINER; 5 | 6 | GRANT EXECUTE ON FUNCTION get_wal_size() TO pgwatch2; 7 | COMMENT ON FUNCTION get_wal_size() IS 'created for pgwatch2'; 8 | -------------------------------------------------------------------------------- /pgwatch2/metrics/instance_up/9.0/metric.sql: -------------------------------------------------------------------------------- 1 | /* NB! This metric has some special handling attached to it - it will store a 0 value if the DB is not accessible. 2 | Thus it can be used to for example calculate some percentual "uptime" indicator. 3 | */ 4 | select /* pgwatch2_generated */ 5 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 6 | 1::int as is_up 7 | ; 8 | -------------------------------------------------------------------------------- /pgwatch2/metrics/00_helpers/get_stat_replication/9.2/metric.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION get_stat_replication() RETURNS SETOF pg_stat_replication AS 2 | $$ 3 | select * from pg_stat_replication 4 | $$ LANGUAGE sql VOLATILE SECURITY DEFINER; 5 | 6 | GRANT EXECUTE ON FUNCTION get_stat_replication() TO pgwatch2; 7 | COMMENT ON FUNCTION get_stat_replication() IS 'created for pgwatch2'; 8 | -------------------------------------------------------------------------------- /pgwatch2/metrics/db_size/9.0/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | pg_database_size(current_database()) as size_b, 4 | (select sum(pg_total_relation_size(c.oid))::int8 5 | from pg_class c join pg_namespace n on n.oid = c.relnamespace 6 | where nspname = 'pg_catalog' and relkind = 'r' 7 | ) as catalog_size_b; 8 | -------------------------------------------------------------------------------- /pgwatch2/metrics/table_bloat_approx_summary/9.5/metric_master.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | approx_free_percent, 4 | approx_free_space as approx_free_space_b, 5 | dead_tuple_percent, 6 | dead_tuple_len as dead_tuple_len_b 7 | from 8 | get_table_bloat_approx() 9 | where 10 | approx_free_space > 0; 11 | -------------------------------------------------------------------------------- /pgwatch2/metrics/wal_stats/14/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | wal_records, 4 | wal_fpi, 5 | (wal_bytes / 1024)::int8 as wal_bytes_kb, 6 | wal_buffers_full, 7 | wal_write, 8 | wal_sync, 9 | wal_write_time::int8, 10 | wal_sync_time::int8 11 | from 12 | pg_stat_wal; 13 | -------------------------------------------------------------------------------- /pgwatch2/metrics/00_helpers/get_wal_size/9.0/metric.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION get_wal_size() RETURNS int8 AS 2 | $$ 3 | select sum((pg_stat_file('pg_xlog/'||f)).size)::int8 from (select pg_ls_dir('pg_xlog') f) ls 4 | $$ LANGUAGE sql VOLATILE SECURITY DEFINER; 5 | 6 | GRANT EXECUTE ON FUNCTION get_wal_size() TO pgwatch2; 7 | COMMENT ON FUNCTION get_wal_size() IS 'created for pgwatch2'; 8 | -------------------------------------------------------------------------------- /pgwatch2/metrics/replication_slot_stats/14/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | slot_name::text as tag_slot_name, 4 | spill_txns, 5 | spill_count, 6 | spill_bytes, 7 | stream_txns, 8 | stream_count, 9 | stream_bytes, 10 | total_txns, 11 | total_bytes 12 | from 13 | pg_stat_replication_slots; 14 | -------------------------------------------------------------------------------- /pgwatch2/metrics/cpu_load/9.0/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | round(load_1min::numeric, 2)::float as load_1min, 4 | round(load_5min::numeric, 2)::float as load_5min, 5 | round(load_15min::numeric, 2)::float as load_15min 6 | from 7 | get_load_average(); -- needs the plpythonu proc from "metric_fetching_helpers" folder 8 | -------------------------------------------------------------------------------- /pgwatch2/metrics/stat_statements_calls/9.2/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | coalesce(sum(calls), 0)::int8 as calls, 4 | coalesce(round(sum(total_time)::numeric, 3), 0)::float8 as total_time 5 | from 6 | pg_stat_statements 7 | where 8 | dbid = (select oid from pg_database where datname = current_database()) 9 | ; 10 | -------------------------------------------------------------------------------- /pgwatch2/sql/config_store/migrations/v1.3.7_stat_statements_calls.sql: -------------------------------------------------------------------------------- 1 | UPDATE pgwatch2.metric 2 | SET m_sql = 3 | $sql$ 4 | select 5 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 6 | sum(calls) as calls, 7 | sum(total_time) as total_time 8 | from 9 | public.get_stat_statements(); 10 | $sql$ 11 | WHERE 12 | m_name = 'stat_statements_calls' 13 | AND m_pg_version_from = 9.2 14 | ; 15 | -------------------------------------------------------------------------------- /pgwatch2/metrics/wal_size/10/metric_su.sql: -------------------------------------------------------------------------------- 1 | /* NB! If using not a real superuser but a role with "pg_monitor" grant then below execute grant is needed: 2 | GRANT EXECUTE ON FUNCTION pg_stat_file(text) to pgwatch2; 3 | */ 4 | select /* pgwatch2_generated */ 5 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 6 | (sum((pg_stat_file('pg_wal/' || name)).size))::int8 as wal_size_b 7 | from pg_ls_waldir(); 8 | -------------------------------------------------------------------------------- /pgwatch2/metrics/sproc_hashes/9.0/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | p.oid::text as tag_oid, 4 | quote_ident(nspname)||'.'||quote_ident(proname) as tag_sproc, 5 | md5(prosrc) 6 | from 7 | pg_proc p 8 | join 9 | pg_namespace n on n.oid = pronamespace 10 | where 11 | not nspname like any(array[E'pg\\_%', 'information_schema']); 12 | -------------------------------------------------------------------------------- /pgwatch2/.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | pg_timetable 3 | *.exe 4 | *.exe~ 5 | *.dll 6 | *.so 7 | *.dylib 8 | 9 | # Test binary, build with `go test -c` 10 | *.test 11 | 12 | # Output of the go coverage tool, specifically when used with LiteIDE 13 | *.out 14 | 15 | # Visual Studio Code internal folder 16 | .vscode 17 | 18 | # Packages ouput folder 19 | dist 20 | 21 | # delve debugger file 22 | debug -------------------------------------------------------------------------------- /pgwatch2/bootstrap/grafana_datasource_pg.sql: -------------------------------------------------------------------------------- 1 | insert into data_source (org_id, version, type, name, access, url, 2 | password, "user", database, basic_auth, is_default, json_data, created, updated 3 | ) values ( 4 | 1, 0, 'postgres', 'pg-metrics', 'proxy', 'localhost:5432', 5 | 'pgwatch2admin', 'pgwatch2', 'pgwatch2_metrics', 'f', 't', '{"postgresVersion":1000,"sslmode":"disable","timescaledb":false}', now(), now() 6 | ); 7 | -------------------------------------------------------------------------------- /pgwatch2/metrics/00_helpers/get_stat_activity/9.2/metric.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION get_stat_activity() RETURNS SETOF pg_stat_activity AS 2 | $$ 3 | select * from pg_stat_activity where datname = current_database() and pid != pg_backend_pid() 4 | $$ LANGUAGE sql VOLATILE SECURITY DEFINER; 5 | 6 | GRANT EXECUTE ON FUNCTION get_stat_activity() TO pgwatch2; 7 | COMMENT ON FUNCTION get_stat_activity() IS 'created for pgwatch2'; 8 | -------------------------------------------------------------------------------- /pgwatch2/metrics/00_helpers/get_stat_activity/9.0/metric.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION get_stat_activity() RETURNS SETOF pg_stat_activity AS 2 | $$ 3 | select * from pg_stat_activity where datname = current_database() and procpid != pg_backend_pid() 4 | $$ LANGUAGE sql VOLATILE SECURITY DEFINER; 5 | 6 | GRANT EXECUTE ON FUNCTION get_stat_activity() TO pgwatch2; 7 | COMMENT ON FUNCTION get_stat_activity() IS 'created for pgwatch2'; 8 | -------------------------------------------------------------------------------- /pgwatch2/metrics/bgwriter/9.2/metric_master.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | checkpoints_timed, 4 | checkpoints_req, 5 | checkpoint_write_time, 6 | checkpoint_sync_time, 7 | buffers_checkpoint, 8 | buffers_clean, 9 | maxwritten_clean, 10 | buffers_backend, 11 | buffers_backend_fsync, 12 | buffers_alloc 13 | from 14 | pg_stat_bgwriter; 15 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | 4 | # Maintain dependencies for Go modules 5 | - package-ecosystem: gomod 6 | directory: "pgwatch2/" 7 | schedule: 8 | interval: daily 9 | time: "04:00" 10 | open-pull-requests-limit: 10 11 | 12 | # Maintain dependencies for GitHub Actions 13 | - package-ecosystem: "github-actions" 14 | directory: "/" 15 | schedule: 16 | interval: "daily" 17 | -------------------------------------------------------------------------------- /pgwatch2/metrics/stat_activity/10/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | s.query as query, 4 | count(*) as count 5 | from get_stat_activity() s 6 | where s.datname = current_database() 7 | and s.state = 'active' 8 | and s.backend_type = 'client backend' 9 | and s.pid != pg_backend_pid() 10 | and now() - s.query_start > '100ms'::interval 11 | group by s.query; 12 | -------------------------------------------------------------------------------- /pgwatch2/metrics/stat_activity/10/metric_su.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | s.query as query, 4 | count(*) as count 5 | from pg_stat_activity s 6 | where s.datname = current_database() 7 | and s.state = 'active' 8 | and s.backend_type = 'client backend' 9 | and s.pid != pg_backend_pid() 10 | and now() - s.query_start > '100ms'::interval 11 | group by s.query; 12 | -------------------------------------------------------------------------------- /pgwatch2/metrics/table_bloat_approx_summary_sql/12/metric.sql: -------------------------------------------------------------------------------- 1 | WITH q_bloat AS ( 2 | select * from get_table_bloat_approx_sql() 3 | ) 4 | select /* pgwatch2_generated */ 5 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 6 | (select sum(approx_bloat_bytes) from q_bloat) as approx_table_bloat_b, 7 | ((select sum(approx_bloat_bytes) from q_bloat) * 100 / pg_database_size(current_database()))::int8 as approx_bloat_percentage 8 | ; 9 | -------------------------------------------------------------------------------- /pgwatch2/metrics/table_bloat_approx_summary_sql/9.0/metric.sql: -------------------------------------------------------------------------------- 1 | WITH q_bloat AS ( 2 | select * from get_table_bloat_approx_sql() 3 | ) 4 | select /* pgwatch2_generated */ 5 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 6 | (select sum(approx_bloat_bytes) from q_bloat) as approx_table_bloat_b, 7 | ((select sum(approx_bloat_bytes) from q_bloat) * 100 / pg_database_size(current_database()))::int8 as approx_bloat_percentage 8 | ; 9 | -------------------------------------------------------------------------------- /pgwatch2/sql/config_store/migrations/v1.6.0-3_schema_version.sql: -------------------------------------------------------------------------------- 1 | begin; 2 | 3 | set role to pgwatch2; 4 | 5 | /* this will allow auto-rollout of schema changes for future 1.6+ releases */ 6 | create table schema_version ( 7 | sv_tag text primary key, 8 | sv_created_on timestamptz not null default now() 9 | ); 10 | 11 | insert into pgwatch2.schema_version (sv_tag) values ('1.6.0'); 12 | 13 | end; 14 | 15 | reset role; 16 | -------------------------------------------------------------------------------- /docker/test/launch_all_pg_versions/purge-all-pg-versions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for ver in 9{0..6} {10..12} ; do 4 | echo "stopping PG $ver ..." 5 | docker stop "pg${ver}" 6 | docker stop "pg${ver}-repl" 7 | 8 | echo "removing PG $ver ..." 9 | docker rm "pg${ver}" 10 | docker rm "pg${ver}-repl" 11 | 12 | echo "removing volumes for PG $ver ..." 13 | docker volume rm "pg${ver}" 14 | docker volume rm "pg${ver}-repl" 15 | done 16 | -------------------------------------------------------------------------------- /pgwatch2/sql/metric_store/roll_out_timescale.psql: -------------------------------------------------------------------------------- 1 | begin; 2 | 3 | create extension if not exists timescaledb cascade; 4 | 5 | \i 00_schema_base.sql 6 | \i 01_old_metrics_cleanup_procedure.sql 7 | \i timescale/ensure_partition_timescale.sql 8 | \i timescale/change_compression_interval.sql 9 | \i timescale/metric_store_timescale.sql 10 | \i timescale/change_chunk_interval.sql 11 | \i metric-time/ensure_partition_metric_time.sql 12 | 13 | commit; 14 | -------------------------------------------------------------------------------- /pgwatch2/sql/config_store/migrations/v1.6.1-1_patroni_cont_discovery.sql: -------------------------------------------------------------------------------- 1 | begin; 2 | 3 | alter table pgwatch2.monitored_db 4 | drop constraint monitored_db_md_dbtype_check, 5 | add constraint monitored_db_md_dbtype_check 6 | check (md_dbtype in ('postgres', 'pgbouncer', 'postgres-continuous-discovery', 'patroni', 'patroni-continuous-discovery')); 7 | 8 | insert into pgwatch2.schema_version (sv_tag) values ('1.6.1'); 9 | 10 | commit; 11 | -------------------------------------------------------------------------------- /pgwatch2/bootstrap/insert_test_monitored_db_nonroot.sql: -------------------------------------------------------------------------------- 1 | insert into pgwatch2.monitored_db (md_unique_name, md_preset_config_name, md_config, md_hostname, md_port, md_dbname, md_user, md_password) 2 | select 'test', 'exhaustive', null, 'localhost', '5432', 'pgwatch2', 'pgwatch2', 'pgwatch2admin' 3 | where not exists ( 4 | select * from pgwatch2.monitored_db where (md_unique_name, md_hostname, md_dbname) = ('test', 'localhost', 'pgwatch2') 5 | ) 6 | ; 7 | -------------------------------------------------------------------------------- /pgwatch2/sql/config_store/migrations/v1.4.0_continuous_discovery.sql: -------------------------------------------------------------------------------- 1 | begin; 2 | 3 | alter table pgwatch2.monitored_db 4 | add md_include_pattern text, 5 | add md_exclude_pattern text, 6 | drop constraint monitored_db_md_dbtype_check, 7 | add constraint monitored_db_md_dbtype_check check (md_dbtype in ('postgres', 'pgbouncer', 'postgres-continuous-discovery')); 8 | 9 | alter table pgwatch2.monitored_db 10 | add md_custom_tags jsonb; 11 | 12 | commit; 13 | -------------------------------------------------------------------------------- /pgwatch2/sql/config_store/migrations/v1.7.0_standby_metric_uniqueness.sql: -------------------------------------------------------------------------------- 1 | begin; 2 | 3 | alter table pgwatch2.metric 4 | alter column m_master_only set not null, 5 | alter column m_standby_only set not null, 6 | drop constraint metric_m_name_m_pg_version_from_key, 7 | add constraint metric_m_name_m_pg_version_from_ke UNIQUE (m_name, m_pg_version_from, m_standby_only); 8 | 9 | insert into pgwatch2.schema_version (sv_tag) values ('1.7.0'); 10 | 11 | commit; 12 | -------------------------------------------------------------------------------- /pgwatch2/sql/config_store/migrations/v1.8.0-1_metric_name_allow_dot.sql: -------------------------------------------------------------------------------- 1 | begin; 2 | 3 | alter table pgwatch2.metric 4 | drop constraint metric_m_name_check, 5 | add constraint metric_m_name_check check (m_name ~ E'^[a-z0-9_\\.]+$'); 6 | 7 | alter table pgwatch2.metric_attribute 8 | drop constraint metric_attribute_ma_metric_name_check, 9 | add constraint metric_attribute_ma_metric_name_check check (ma_metric_name ~ E'^[a-z0-9_\\.]+$'); 10 | 11 | commit; 12 | -------------------------------------------------------------------------------- /docker/test/launch_all_pg_versions/pgbench_on_all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DURATION=7200 4 | RATE=0.1 5 | SCALE=1 6 | DB=postgres 7 | 8 | for ver in 9{0..6} {10..12} ; do 9 | 10 | echo "doing pgbench init for ${ver} ..." 11 | pgbench -h localhost -U postgres -p "543${ver}" -i $DB 12 | 13 | echo "launching pgbench for ${ver} ..." 14 | pgbench -h localhost -U postgres -p "543${ver}" -T $DURATION -R $RATE $DB & 15 | 16 | done 17 | 18 | echo "done. pgbench duration: $DURATION" -------------------------------------------------------------------------------- /pgwatch2/metrics/stat_statements_calls/13/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | coalesce(sum(calls), 0)::int8 as calls, 4 | coalesce(round(sum(total_exec_time)::numeric, 3), 0)::float8 as total_time, 5 | round(sum(total_plan_time)::numeric, 3)::double precision as total_plan_time 6 | from 7 | pg_stat_statements 8 | where 9 | dbid = (select oid from pg_database where datname = current_database()) 10 | ; 11 | -------------------------------------------------------------------------------- /pgwatch2/sql/config_store/migrations/v1.6.0-1_master_only_and_patroni.sql: -------------------------------------------------------------------------------- 1 | begin; 2 | 3 | alter table pgwatch2.monitored_db 4 | add md_host_config jsonb, 5 | drop constraint monitored_db_md_dbtype_check, 6 | add constraint monitored_db_md_dbtype_check 7 | check (md_dbtype in ('postgres', 'pgbouncer', 'postgres-continuous-discovery', 'patroni')); 8 | 9 | alter table pgwatch2.monitored_db 10 | add md_only_if_master bool not null default false; 11 | 12 | commit; 13 | -------------------------------------------------------------------------------- /pgwatch2/sql/config_store/migrations/v1.8.0-2_pgpool_dbtype.sql: -------------------------------------------------------------------------------- 1 | begin; 2 | 3 | alter table pgwatch2.monitored_db 4 | drop constraint monitored_db_md_dbtype_check, 5 | add constraint monitored_db_md_dbtype_check 6 | check (md_dbtype in ('postgres', 'pgbouncer', 'postgres-continuous-discovery', 'patroni', 'patroni-continuous-discovery', 'pgpool')); 7 | 8 | insert into pgwatch2.schema_version (sv_tag) values ('1.8.0') 9 | on conflict do nothing; 10 | 11 | commit; 12 | -------------------------------------------------------------------------------- /pgwatch2/metrics/stat_ssl/9.5/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | count(*) as total, 4 | count(*) FILTER (WHERE ssl) as "on", 5 | count(*) FILTER (WHERE NOT ssl) as "off" 6 | FROM 7 | pg_stat_ssl AS s, 8 | get_stat_activity() AS a 9 | WHERE 10 | a.pid = s.pid 11 | AND a.datname = current_database() 12 | AND a.pid <> pg_backend_pid() 13 | AND NOT (a.client_addr = '127.0.0.1' OR client_port = -1) 14 | ; 15 | -------------------------------------------------------------------------------- /pgwatch2/metrics/stat_ssl/9.5/metric_su.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | count(*) as total, 4 | count(*) FILTER (WHERE ssl) as "on", 5 | count(*) FILTER (WHERE NOT ssl) as "off" 6 | FROM 7 | pg_stat_ssl AS s, 8 | pg_stat_activity AS a 9 | WHERE 10 | a.pid = s.pid 11 | AND a.datname = current_database() 12 | AND a.pid <> pg_backend_pid() 13 | AND NOT (a.client_addr = '127.0.0.1' OR client_port = -1) 14 | ; 15 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # Read the Docs configuration file 2 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 3 | 4 | # Required 5 | version: 2 6 | 7 | # Set the version of Python and other tools we might need 8 | build: 9 | os: ubuntu-22.04 10 | tools: 11 | python: "3.11" 12 | 13 | # Build documentation in the docs/ directory with Sphinx 14 | sphinx: 15 | configuration: docs/conf.py 16 | 17 | python: 18 | install: 19 | - requirements: docs/requirements.txt -------------------------------------------------------------------------------- /pgwatch2/sql/config_store/migrations/v1.8.2-1_patroni_namespace_discovery.sql: -------------------------------------------------------------------------------- 1 | begin; 2 | 3 | alter table pgwatch2.monitored_db 4 | drop constraint monitored_db_md_dbtype_check, 5 | add constraint monitored_db_md_dbtype_check 6 | check (md_dbtype in ('postgres', 'pgbouncer', 'postgres-continuous-discovery', 'patroni', 'patroni-continuous-discovery', 'patroni-namespace-discovery', 'pgpool')); 7 | 8 | insert into pgwatch2.schema_version (sv_tag) values ('1.8.2'); 9 | 10 | commit; 11 | -------------------------------------------------------------------------------- /pgwatch2/sql/config_store/migrations/v1.5.0-1_ssl_certs.sql: -------------------------------------------------------------------------------- 1 | begin; 2 | 3 | alter table monitored_db 4 | add md_root_ca_path text not null default '', 5 | add md_client_cert_path text not null default '', 6 | add md_client_key_path text not null default ''; 7 | 8 | alter table monitored_db 9 | drop constraint monitored_db_md_sslmode_check, 10 | add constraint monitored_db_md_sslmode_check 11 | CHECK (md_sslmode in ('disable', 'require', 'verify-ca', 'verify-full')); 12 | 13 | commit; 14 | -------------------------------------------------------------------------------- /pgwatch2/bootstrap/grafana_custom_config.ini: -------------------------------------------------------------------------------- 1 | [server] 2 | protocol = http 3 | cert_file = /pgwatch2/persistent-config/self-signed-ssl.pem 4 | cert_key = /pgwatch2/persistent-config/self-signed-ssl.key 5 | 6 | [database] 7 | type = postgres 8 | host = 127.0.0.1:5432 9 | name = pgwatch2_grafana 10 | user = pgwatch2 11 | password = pgwatch2admin 12 | 13 | [security] 14 | admin_user = admin 15 | admin_password = pgwatch2admin 16 | 17 | [auth.anonymous] 18 | enabled = true 19 | 20 | [metrics] 21 | enabled = false 22 | -------------------------------------------------------------------------------- /pgwatch2/metrics/replication_slots/9.4/metric_master.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | slot_name::text as tag_slot_name, 4 | coalesce(plugin, 'physical')::text as tag_plugin, 5 | active, 6 | case when active then 0 else 1 end as non_active_int, 7 | pg_xlog_location_diff(pg_current_xlog_location(), restart_lsn)::int8 as restart_lsn_lag_b, 8 | greatest(age(xmin), age(catalog_xmin))::int8 as xmin_age_tx 9 | from 10 | pg_replication_slots; 11 | -------------------------------------------------------------------------------- /pgwatch2/metrics/archiver/9.4/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | archived_count, 4 | failed_count, 5 | case when coalesce(last_failed_time, '1970-01-01'::timestamptz) > coalesce(last_archived_time, '1970-01-01'::timestamptz) then 1 else 0 end as is_failing_int, 6 | extract(epoch from now() - last_failed_time)::int8 as seconds_since_last_failure 7 | from 8 | pg_stat_archiver 9 | where 10 | current_setting('archive_mode') in ('on', 'always'); 11 | -------------------------------------------------------------------------------- /pgwatch2/metrics/00_helpers/get_load_average/9.1/metric.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | 3 | CREATE EXTENSION IF NOT EXISTS plpython3u; 4 | 5 | CREATE OR REPLACE FUNCTION get_load_average(OUT load_1min float, OUT load_5min float, OUT load_15min float) AS 6 | $$ 7 | from os import getloadavg 8 | la = getloadavg() 9 | return [la[0], la[1], la[2]] 10 | $$ LANGUAGE plpython3u VOLATILE; 11 | 12 | GRANT EXECUTE ON FUNCTION get_load_average() TO pgwatch2; 13 | 14 | COMMENT ON FUNCTION get_load_average() is 'created for pgwatch2'; 15 | 16 | COMMIT; 17 | -------------------------------------------------------------------------------- /pgwatch2/metrics/reco_default_public_schema/9.0/metric_master.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | 'default_public_schema_privs'::text as tag_reco_topic, 4 | nspname::text as tag_object_name, 5 | 'REVOKE CREATE ON SCHEMA public FROM PUBLIC;'::text as recommendation, 6 | 'only authorized users should be allowed to create new objects'::text as extra_info 7 | from 8 | pg_namespace 9 | where 10 | nspname = 'public' 11 | and nspacl::text ~ E'[,\\{]+=U?C/' 12 | ; -------------------------------------------------------------------------------- /postgresql.conf: -------------------------------------------------------------------------------- 1 | listen_addresses='*' 2 | shared_buffers='32MB' 3 | work_mem='2MB' 4 | checkpoint_timeout='1h' 5 | ssl=true 6 | track_io_timing=on 7 | shared_preload_libraries = 'pg_stat_statements' 8 | track_functions='pl' 9 | wal_compression=on 10 | log_destination=csvlog 11 | logging_collector=on 12 | log_directory='/var/log/postgresql' 13 | log_filename='postgresql-%a.log' 14 | log_truncate_on_rotation=on 15 | autovacuum_freeze_max_age=2000000000 16 | wal_level=archive 17 | archive_mode=on 18 | archive_command='/bin/true' 19 | -------------------------------------------------------------------------------- /pgwatch2/metrics/wal/9.2/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | case 4 | when pg_is_in_recovery() = false then 5 | pg_xlog_location_diff(pg_current_xlog_location(), '0/0')::int8 6 | else 7 | pg_xlog_location_diff(pg_last_xlog_replay_location(), '0/0')::int8 8 | end as xlog_location_b, 9 | case when pg_is_in_recovery() then 1 else 0 end as in_recovery_int, 10 | extract(epoch from (now() - pg_postmaster_start_time()))::int8 as postmaster_uptime_s; 11 | -------------------------------------------------------------------------------- /pgwatch2/bootstrap/insert_test_monitored_db.sql: -------------------------------------------------------------------------------- 1 | /* NB! using superuser for monitoring is not a best practice and should be done only if pgwatch2 is also running locally */ 2 | insert into pgwatch2.monitored_db (md_unique_name, md_preset_config_name, md_config, md_hostname, md_port, md_dbname, md_user, md_password) 3 | select 'test', 'full', null, 'localhost', '5432', 'pgwatch2', 'postgres', '' 4 | where not exists ( 5 | select * from pgwatch2.monitored_db where (md_unique_name, md_hostname, md_dbname) = ('test', 'localhost', 'pgwatch2') 6 | ) 7 | ; 8 | -------------------------------------------------------------------------------- /pgwatch2/sql/config_store/migrations/v1.8.1-1_standby_config.sql: -------------------------------------------------------------------------------- 1 | begin; 2 | 3 | alter table pgwatch2.monitored_db 4 | add md_preset_config_name_standby text references pgwatch2.preset_config(pc_name), 5 | add md_config_standby jsonb; 6 | 7 | alter table pgwatch2.monitored_db 8 | add constraint preset_or_custom_config_standby check 9 | (not (md_preset_config_name_standby is not null and md_config_standby is not null)); 10 | 11 | insert into pgwatch2.schema_version (sv_tag) values ('1.8.1') 12 | on conflict do nothing; 13 | 14 | commit; 15 | -------------------------------------------------------------------------------- /pgwatch2/metrics/sproc_stats/9.0/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | schemaname::text AS tag_schema, 4 | funcname::text AS tag_function_name, 5 | quote_ident(schemaname)||'.'||quote_ident(funcname) as tag_function_full_name, 6 | p.oid::text as tag_oid, -- for overloaded funcs 7 | calls as sp_calls, 8 | self_time, 9 | total_time 10 | FROM 11 | pg_stat_user_functions f 12 | JOIN 13 | pg_proc p ON p.oid = f.funcid 14 | ORDER BY 15 | total_time DESC 16 | LIMIT 17 | 300; 18 | -------------------------------------------------------------------------------- /pgwatch2/bootstrap/insert_test_monitored_db_influx.sql: -------------------------------------------------------------------------------- 1 | /* NB! using superuser for monitoring is not a best practice and should be done only if pgwatch2 is also running locally */ 2 | insert into pgwatch2.monitored_db (md_unique_name, md_preset_config_name, md_config, md_hostname, md_port, md_dbname, md_user, md_password) 3 | select 'test', 'full_influx', null, 'localhost', '5432', 'pgwatch2', 'postgres', '' 4 | where not exists ( 5 | select * from pgwatch2.monitored_db where (md_unique_name, md_hostname, md_dbname) = ('test', 'localhost', 'pgwatch2') 6 | ) 7 | ; 8 | -------------------------------------------------------------------------------- /pgwatch2/metrics/db_stats/9.0/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | numbackends, 4 | xact_commit, 5 | xact_rollback, 6 | blks_read, 7 | blks_hit, 8 | tup_returned, 9 | tup_fetched, 10 | tup_inserted, 11 | tup_updated, 12 | tup_deleted, 13 | extract(epoch from (now() - pg_postmaster_start_time()))::int8 as postmaster_uptime_s, 14 | case when pg_is_in_recovery() then 1 else 0 end as in_recovery_int 15 | from 16 | pg_stat_database 17 | where 18 | datname = current_database(); 19 | -------------------------------------------------------------------------------- /pgwatch2/metrics/reco_add_index/metric_attrs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # override the SQL definition from $metric_name if below defined extensions and versions match 3 | # SQL config equivalent: 4 | # {"extension_version_based_overrides": [{"target_metric": "reco_add_index_ext_qualstats_2.0", "expected_extension_versions": [{"ext_name": "pg_qualstats", "ext_min_version": "2.0"}] }]} 5 | extension_version_based_overrides: 6 | - target_metric: reco_add_index_ext_qualstats_2.0 7 | expected_extension_versions: 8 | - ext_min_version: 2.0 9 | ext_name: pg_qualstats 10 | -------------------------------------------------------------------------------- /pgwatch2/metrics/sequence_health/10/metric.sql: -------------------------------------------------------------------------------- 1 | with q_seq_data as ( 2 | select * from get_sequences() 3 | ) 4 | select /* pgwatch2_generated */ 5 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 6 | (select round(100.0 * coalesce(max(last_value::numeric / max_value), 0), 2)::float from q_seq_data where not cycle) as max_used_pct, 7 | (select count(*) from q_seq_data where not cycle and last_value::numeric / max_value > 0.5) as p50_used_seq_count, 8 | (select count(*) from q_seq_data where not cycle and last_value::numeric / max_value > 0.75) as p75_used_seq_count; 9 | -------------------------------------------------------------------------------- /pgwatch2/metrics/reco_add_index/9.1/metric_master.sql: -------------------------------------------------------------------------------- 1 | /* assumes the pg_qualstats extension and superuser or select grants on pg_qualstats_indexes_ddl view */ 2 | select /* pgwatch2_generated */ 3 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 4 | 'create_index'::text as tag_reco_topic, 5 | quote_ident(nspname::text)||'.'||quote_ident(relid::text) as tag_object_name, 6 | ddl as recommendation, 7 | ('qual execution count: '|| execution_count)::text as extra_info 8 | from 9 | pg_qualstats_indexes_ddl 10 | order by 11 | execution_count desc 12 | limit 25; 13 | -------------------------------------------------------------------------------- /pgwatch2/metrics/sequence_health/10/metric_su.sql: -------------------------------------------------------------------------------- 1 | with q_seq_data as ( 2 | select * from pg_sequences 3 | ) 4 | select /* pgwatch2_generated */ 5 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 6 | (select round(100.0 * coalesce(max(last_value::numeric / max_value), 0), 2)::float from q_seq_data where not cycle) as max_used_pct, 7 | (select count(*) from q_seq_data where not cycle and last_value::numeric / max_value > 0.5) as p50_used_seq_count, 8 | (select count(*) from q_seq_data where not cycle and last_value::numeric / max_value > 0.75) as p75_used_seq_count; 9 | -------------------------------------------------------------------------------- /pgwatch2/metrics/db_stats/9.1/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | numbackends, 4 | xact_commit, 5 | xact_rollback, 6 | blks_read, 7 | blks_hit, 8 | tup_returned, 9 | tup_fetched, 10 | tup_inserted, 11 | tup_updated, 12 | tup_deleted, 13 | conflicts, 14 | extract(epoch from (now() - pg_postmaster_start_time()))::int8 as postmaster_uptime_s, 15 | case when pg_is_in_recovery() then 1 else 0 end as in_recovery_int 16 | from 17 | pg_stat_database 18 | where 19 | datname = current_database(); 20 | -------------------------------------------------------------------------------- /postgresql_timescale.conf: -------------------------------------------------------------------------------- 1 | listen_addresses='*' 2 | shared_buffers='64MB' 3 | work_mem='16MB' 4 | checkpoint_timeout='1h' 5 | ssl=true 6 | track_io_timing=on 7 | shared_preload_libraries = 'pg_stat_statements,timescaledb,pg_qualstats' 8 | track_functions='pl' 9 | wal_compression=zstd 10 | log_destination=csvlog 11 | logging_collector=on 12 | log_directory='/var/log/postgresql' 13 | log_filename='postgresql-%a.log' 14 | log_truncate_on_rotation=on 15 | wal_level=archive 16 | archive_mode=on 17 | archive_command='/bin/true' 18 | max_worker_processes=16 19 | max_locks_per_transaction=128 20 | -------------------------------------------------------------------------------- /pgwatch2/metrics/buffercache_by_type/9.2/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | CASE 4 | WHEN relkind = 'r' THEN 'Table' -- TODO all relkinds covered? 5 | WHEN relkind = 'i' THEN 'Index' 6 | WHEN relkind = 't' THEN 'Toast' 7 | WHEN relkind = 'm' THEN 'Materialized view' 8 | ELSE 'Other' 9 | END as tag_relkind, 10 | count(*) * (current_setting('block_size')::int8) size_b 11 | FROM 12 | pg_buffercache AS b, 13 | pg_class AS d 14 | WHERE 15 | d.oid = b.relfilenode 16 | GROUP BY 17 | relkind; 18 | -------------------------------------------------------------------------------- /pgwatch2/metrics/locks/9.0/metric.sql: -------------------------------------------------------------------------------- 1 | WITH q_locks AS ( 2 | select 3 | * 4 | from 5 | pg_locks 6 | where 7 | pid != pg_backend_pid() 8 | and database = (select oid from pg_database where datname = current_database()) 9 | ) 10 | select /* pgwatch2_generated */ 11 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 12 | locktypes AS tag_locktype, 13 | coalesce((select count(*) FROM q_locks WHERE locktype = locktypes), 0) AS count 14 | FROM 15 | unnest('{relation, extend, page, tuple, transactionid, virtualxid, object, userlock, advisory}'::text[]) locktypes; 16 | -------------------------------------------------------------------------------- /docker/influxdb/README.md: -------------------------------------------------------------------------------- 1 | ## Build instructions 2 | 3 | 1. Get the latest official InfluxDB Docker sources 4 | ``` 5 | git clone https://github.com/influxdata/influxdata-docker 6 | ``` 7 | 8 | 1. Point an env variable to the pgwatch2 source folder 9 | ``` 10 | PGWATCH2_SOURCE_DIR=~/git/pgwatch2 11 | ``` 12 | 13 | 1. Apply the "non-root user" patch, change the Influx version as needed, and build the image as usual 14 | ``` 15 | cd influxdata-docker/influxdb/1.4 16 | git apply $PGWATCH2_SOURCE_DIR/docker/influxdb/non-root-user.patch 17 | Docker build . 18 | ``` 19 | -------------------------------------------------------------------------------- /postgresql_22.04.conf: -------------------------------------------------------------------------------- 1 | listen_addresses='*' 2 | shared_buffers='32MB' 3 | work_mem='2MB' 4 | checkpoint_timeout='1h' 5 | ssl=true 6 | track_io_timing=on 7 | shared_preload_libraries = 'pg_stat_statements,pg_qualstats' 8 | track_functions='pl' 9 | wal_compression=on 10 | log_destination=csvlog 11 | logging_collector=on 12 | log_directory='/var/log/postgresql' 13 | log_filename='postgresql-%a.log' 14 | log_truncate_on_rotation=on 15 | autovacuum_freeze_max_age=2000000000 16 | wal_level=archive 17 | archive_mode=on 18 | archive_command='/bin/true' 19 | pg_qualstats.sample_rate = 0.1 20 | pg_qualstats.track_constants = off 21 | -------------------------------------------------------------------------------- /docs/kubernetes.rst: -------------------------------------------------------------------------------- 1 | Kubernetes 2 | ========== 3 | 4 | A basic Helm chart is available for installing pgwatch2 to a Kubernetes cluster. The corresponding setup can be found in `pgwatch2-charts repository `_, whereas installation is done via the following commands: 5 | 6 | :: 7 | 8 | cd openshift_k8s 9 | helm install -f chart-values.yml pgwatch2 ./helm-chart 10 | 11 | Please have a look at `openshift_k8s/helm-chart/values.yaml `_ to get additional information of configurable options. 12 | -------------------------------------------------------------------------------- /pgwatch2/metrics/locks_mode/9.0/metric.sql: -------------------------------------------------------------------------------- 1 | WITH q_locks AS ( 2 | select 3 | * 4 | from 5 | pg_locks 6 | where 7 | pid != pg_backend_pid() 8 | and database = (select oid from pg_database where datname = current_database()) 9 | ) 10 | select /* pgwatch2_generated */ 11 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 12 | lockmodes AS tag_lockmode, 13 | coalesce((select count(*) FROM q_locks WHERE mode = lockmodes), 0) AS count 14 | FROM 15 | unnest('{AccessShareLock, ExclusiveLock, RowShareLock, RowExclusiveLock, ShareLock, ShareRowExclusiveLock, AccessExclusiveLock, ShareUpdateExclusiveLock}'::text[]) lockmodes; 16 | -------------------------------------------------------------------------------- /docker/launch-wrapper-webui.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ### 4 | ### For building the pgwatch2-webui docker image 5 | ### 6 | 7 | # currently only checks if SSL is enabled and if so generates new cert on the first run 8 | if [ ! -f /pgwatch2/persistent-config/self-signed-ssl.key -o ! -f /pgwatch2/persistent-config/self-signed-ssl.pem ] ; then 9 | openssl req -x509 -newkey rsa:4096 -keyout /pgwatch2/persistent-config/self-signed-ssl.key -out /pgwatch2/persistent-config/self-signed-ssl.pem -days 3650 -nodes -sha256 -subj '/CN=pw2' 10 | chmod 0600 /pgwatch2/persistent-config/self-signed-ssl.* 11 | fi 12 | 13 | exec /pgwatch2/webpy/web.py "$@" 14 | -------------------------------------------------------------------------------- /pgwatch2/metrics/README_metrics.md: -------------------------------------------------------------------------------- 1 | # Metrics folder structure 2 | 3 | Following folder structure is expected when adding new metrics: 4 | 5 | * Top level metrics folder 6 | * Metric name (will be stored "as is") 7 | * Postgres version (the minimal / "from" version where the current query works) 8 | * metric.sql | metric_su.sql | metric_master.sql | metric_standby.sql ("attributes" can also be combined!) 9 | 10 | Helpers ("00_helpers") follows the same pattern and also filename defining "preset configs" must be "preset-configs.yaml". 11 | The top level metrics folder can be located anywhere and must be pointed to via -m /--metrics-folder gatherer params. 12 | -------------------------------------------------------------------------------- /pgwatch2/metrics/reco_disabled_triggers/9.0/metric_master.sql: -------------------------------------------------------------------------------- 1 | /* "temporarily" disabled triggers might be forgotten about... */ 2 | select /* pgwatch2_generated */ 3 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 4 | 'disabled_triggers'::text as tag_reco_topic, 5 | quote_ident(nspname)||'.'||quote_ident(relname) as tag_object_name, 6 | 'review usage of trigger and consider dropping it if not needed anymore'::text as recommendation, 7 | ''::text as extra_info 8 | from 9 | pg_trigger t 10 | join 11 | pg_class c on c.oid = t.tgrelid 12 | join 13 | pg_namespace n on n.oid = c.relnamespace 14 | where 15 | tgenabled = 'D' 16 | ; 17 | -------------------------------------------------------------------------------- /pgwatch2/metrics/db_stats/9.2/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | numbackends, 4 | xact_commit, 5 | xact_rollback, 6 | blks_read, 7 | blks_hit, 8 | tup_returned, 9 | tup_fetched, 10 | tup_inserted, 11 | tup_updated, 12 | tup_deleted, 13 | conflicts, 14 | temp_files, 15 | temp_bytes, 16 | deadlocks, 17 | blk_read_time, 18 | blk_write_time, 19 | extract(epoch from (now() - pg_postmaster_start_time()))::int8 as postmaster_uptime_s, 20 | case when pg_is_in_recovery() then 1 else 0 end as in_recovery_int 21 | from 22 | pg_stat_database 23 | where 24 | datname = current_database(); 25 | -------------------------------------------------------------------------------- /pgwatch2/metrics/db_stats_aurora/9.6/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | numbackends, 4 | xact_commit, 5 | xact_rollback, 6 | blks_read, 7 | blks_hit, 8 | tup_returned, 9 | tup_fetched, 10 | tup_inserted, 11 | tup_updated, 12 | tup_deleted, 13 | conflicts, 14 | temp_files, 15 | temp_bytes, 16 | deadlocks, 17 | blk_read_time, 18 | blk_write_time, 19 | extract(epoch from (now() - pg_postmaster_start_time()))::int8 as postmaster_uptime_s, 20 | case when pg_is_in_recovery() then 1 else 0 end as in_recovery_int 21 | from 22 | pg_stat_database 23 | where 24 | datname = current_database(); 25 | -------------------------------------------------------------------------------- /grafana_dashboards/README.md: -------------------------------------------------------------------------------- 1 | ## Conventions for adding dashboards 2 | 3 | 1. For a new dashboard create a new subdirectory in "grafana_dashboards". Folder name will serve as a Grafana "slug" 4 | i.e. URL identifier so use hyphens for multi-word names. 5 | 2. Put the dashboard source in a file called dashboard.json (without the internal __inputs and __requires keys, i.e. exactly 6 | as displayed after clicking on the "View JSON" button from Dashboard settings). 7 | 3. Put the title on a single line into a file called "title.txt". NB! Special characters like apostrophes need to be doubled (SQL syntax rules) 8 | 9 | Stored dashboards will be installed when container starts up for the first time. 10 | -------------------------------------------------------------------------------- /pgwatch2/metrics/index_hashes/9.0/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | quote_ident(nspname)||'.'||quote_ident(c.relname) as tag_index, 4 | quote_ident(nspname)||'.'||quote_ident(r.relname) as "table", 5 | i.indisvalid::text as is_valid, 6 | coalesce(md5(pg_get_indexdef(i.indexrelid)), random()::text) as md5 7 | from 8 | pg_index i 9 | join 10 | pg_class c on c.oid = i.indexrelid 11 | join 12 | pg_class r on r.oid = i.indrelid 13 | join 14 | pg_namespace n on n.oid = c.relnamespace 15 | where 16 | c.relnamespace not in (select oid from pg_namespace where nspname like any(array[E'pg\\_%', 'information_schema'])); 17 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /pgwatch2/metrics/psutil_cpu/9.0/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | round(cpu_utilization::numeric, 2)::float as cpu_utilization, 4 | round(load_1m_norm::numeric, 2)::float as load_1m_norm, 5 | round(load_1m::numeric, 2)::float as load_1m, 6 | round(load_5m_norm::numeric, 2)::float as load_5m_norm, 7 | round(load_5m::numeric, 2)::float as load_5m, 8 | round("user"::numeric, 2)::float as "user", 9 | round(system::numeric, 2)::float as system, 10 | round(idle::numeric, 2)::float as idle, 11 | round(iowait::numeric, 2)::float as iowait, 12 | round(irqs::numeric, 2)::float as irqs, 13 | round(other::numeric, 2)::float as other 14 | from 15 | get_psutil_cpu() 16 | ; 17 | -------------------------------------------------------------------------------- /pgwatch2/metrics/db_stats_aurora/10/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | numbackends, 4 | xact_commit, 5 | xact_rollback, 6 | blks_read, 7 | blks_hit, 8 | tup_returned, 9 | tup_fetched, 10 | tup_inserted, 11 | tup_updated, 12 | tup_deleted, 13 | conflicts, 14 | temp_files, 15 | temp_bytes, 16 | deadlocks, 17 | blk_read_time, 18 | blk_write_time, 19 | extract(epoch from (now() - pg_postmaster_start_time()))::int8 as postmaster_uptime_s, 20 | case when pg_is_in_recovery() then 1 else 0 end as in_recovery_int, 21 | system_identifier::text as tag_sys_id 22 | from 23 | pg_stat_database, pg_control_system() 24 | where 25 | datname = current_database(); 26 | -------------------------------------------------------------------------------- /pgwatch2/metrics/db_stats/9.3/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | numbackends, 4 | xact_commit, 5 | xact_rollback, 6 | blks_read, 7 | blks_hit, 8 | tup_returned, 9 | tup_fetched, 10 | tup_inserted, 11 | tup_updated, 12 | tup_deleted, 13 | conflicts, 14 | temp_files, 15 | temp_bytes, 16 | deadlocks, 17 | blk_read_time, 18 | blk_write_time, 19 | extract(epoch from (now() - pg_postmaster_start_time()))::int8 as postmaster_uptime_s, 20 | extract(epoch from (now() - pg_backup_start_time()))::int8 as backup_duration_s, 21 | case when pg_is_in_recovery() then 1 else 0 end as in_recovery_int 22 | from 23 | pg_stat_database 24 | where 25 | datname = current_database(); 26 | -------------------------------------------------------------------------------- /pgwatch2/metrics/wait_events/9.6/metric.sql: -------------------------------------------------------------------------------- 1 | with q_sa as ( 2 | select * from pg_stat_activity where datname = current_database() and pid <> pg_backend_pid() 3 | ) 4 | select /* pgwatch2_generated */ 5 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 6 | wait_event_type as tag_wait_event_type, 7 | wait_event as tag_wait_event, 8 | count(*), 9 | avg(abs(1e6* extract(epoch from now() - query_start)))::int8 as avg_query_duration_us, 10 | max(abs(1e6* extract(epoch from now() - query_start)))::int8 as max_query_duration_us, 11 | (select count(*) from q_sa where state = 'active') as total_active 12 | from 13 | q_sa 14 | where 15 | state = 'active' 16 | and wait_event_type is not null 17 | and wait_event_type <> 'Timeout' 18 | group by 19 | 1, 2, 3; 20 | -------------------------------------------------------------------------------- /pgwatch2/metrics/logical_subscriptions/10/metric.sql: -------------------------------------------------------------------------------- 1 | with q_sr as ( 2 | select * from pg_subscription_rel 3 | ) 4 | select /* pgwatch2_generated */ 5 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 6 | subname::text as tag_subname, 7 | subenabled, 8 | (select count(*) from q_sr where srsubid = oid) as relcount, 9 | (select count(*) from q_sr where srsubid = oid and srsubstate = 'i') as state_i, 10 | (select count(*) from q_sr where srsubid = oid and srsubstate = 'd') as state_d, 11 | (select count(*) from q_sr where srsubid = oid and srsubstate = 's') as state_s, 12 | (select count(*) from q_sr where srsubid = oid and srsubstate = 'r') as state_r 13 | from 14 | pg_subscription 15 | where 16 | subdbid = (select oid from pg_database where datname = current_database()) 17 | ; -------------------------------------------------------------------------------- /pgwatch2/metrics/show_plans_realtime/9.0/metric.sql: -------------------------------------------------------------------------------- 1 | /* assumes pg_show_plans extension */ 2 | select /* pgwatch2_generated */ 3 | max((extract(epoch from now()) * 1e9)::int8) as epoch_ns, 4 | max(extract(epoch from now() - query_start))::int as max_s, 5 | avg(extract(epoch from now() - query_start))::int as avg_s, 6 | count(*), 7 | array_to_string(array_agg(distinct usename order by usename), ',') as "users", 8 | max(md5(plan)) as tag_hash, /* needed for influx */ 9 | plan, 10 | max(query) as query 11 | from 12 | pg_show_plans p 13 | join 14 | pg_stat_activity a 15 | using (pid) 16 | where 17 | p.pid != pg_backend_pid() 18 | and datname = current_database() 19 | and now() - query_start > '1s'::interval 20 | group by 21 | plan 22 | order by 23 | max_s desc 24 | limit 25 | 10 26 | ; -------------------------------------------------------------------------------- /pgwatch2/get_dependencies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | go_on_path=$(which go) 4 | if [ -z "$go_on_path" ] ; then 5 | export PATH=$PATH:/usr/local/go/bin 6 | fi 7 | 8 | echo "getting project dependencies..." 9 | go get -u github.com/lib/pq 10 | go get -u github.com/op/go-logging 11 | go get -u github.com/jmoiron/sqlx 12 | go get -u github.com/influxdata/influxdb1-client 13 | go get -u github.com/jessevdk/go-flags 14 | go get -u github.com/marpaia/graphite-golang 15 | go get -u github.com/shopspring/decimal 16 | go get -u gopkg.in/yaml.v2 17 | go get -u github.com/coreos/go-systemd/daemon 18 | go get -u golang.org/x/crypto/pbkdf2 19 | go get -u github.com/prometheus/client_golang/prometheus 20 | go get -u github.com/samuel/go-zookeeper/zk 21 | go get -u go.etcd.io/etcd/client 22 | go get -u github.com/hashicorp/consul/api 23 | -------------------------------------------------------------------------------- /pgwatch2/metrics/stat_activity_realtime/9.6/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | pid as tag_pid, 4 | usename::text AS user, 5 | application_name AS appname, 6 | coalesce(client_addr::text, 'local') AS ip, 7 | extract(epoch FROM (now() - query_start))::int AS duration_s, 8 | (wait_event_type IS NOT NULL)::int AS waiting, 9 | array_to_string(pg_blocking_pids(pid), ',') as blocking_pids, 10 | ltrim(regexp_replace(query, E'[ \\t\\n\\r]+' , ' ', 'g'))::varchar(300) AS query 11 | FROM 12 | pg_stat_activity 13 | WHERE 14 | state != 'idle' 15 | AND pid != pg_backend_pid() 16 | AND datname = current_database() 17 | AND now() - query_start > '500ms'::interval 18 | ORDER BY 19 | now() - query_start DESC 20 | LIMIT 25; 21 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. meta:: 2 | :http-equiv=Refresh: 0; url='https://pgwat.ch/' 3 | 4 | Welcome to pgwatch2's documentation! 5 | ==================================== 6 | 7 | .. toctree:: 8 | :maxdepth: 2 9 | :caption: Contents: 10 | 11 | README 12 | project_background 13 | features 14 | advanced_features 15 | components 16 | installation_options 17 | docker_installation 18 | custom_installation 19 | preparing_databases 20 | using_managed_services 21 | metric_definitions 22 | web_ui 23 | dashboarding_alerting 24 | sizing_recommendations 25 | technical_details 26 | security 27 | long_term_installations 28 | upgrading 29 | kubernetes 30 | 31 | 32 | Indices and tables 33 | ================== 34 | 35 | * :ref:`genindex` 36 | * :ref:`modindex` 37 | * :ref:`search` 38 | -------------------------------------------------------------------------------- /pgwatch2/metrics/00_helpers/README.md: -------------------------------------------------------------------------------- 1 | Metric fetching helpers are normal PL/pgSQL or PL/Python stored procedures that provide OS level information which is not 2 | available at all with normal PostgreSQL means (as Postgres is pretty much oblivious on OS details) or some security 3 | sensitive internal statistics (like query texts) that are superuser restricted. For the latter case though, starting from 4 | version 10, there's a special "pg_monitor" system grant available to be used for exactly such monitoring purposes. Then 5 | helpers are only needed for OS-level metrics. 6 | 7 | To make rolling out helpers easier there's a small Python script provided - rollout_helper.py, that contacts the config DB 8 | to find all "DBs to be monitored" and then tries to roll out all helpers (if master) and gives execute grants to the monitoring user. 9 | -------------------------------------------------------------------------------- /pgwatch2/metrics/replication_slots/10/metric_master.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | slot_name::text as tag_slot_name, 4 | coalesce(plugin, 'physical')::text as tag_plugin, 5 | active, 6 | case when active then 0 else 1 end as non_active_int, 7 | case when not pg_is_in_recovery() then pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)::int8 8 | else pg_wal_lsn_diff(pg_last_wal_replay_lsn(), restart_lsn)::int8 end as restart_lsn_lag_b, 9 | case when not pg_is_in_recovery() then pg_wal_lsn_diff(pg_current_wal_lsn(), confirmed_flush_lsn)::int8 10 | else pg_wal_lsn_diff(pg_last_wal_replay_lsn(), confirmed_flush_lsn)::int8 end as confirmed_flush_lsn_lag_b, 11 | greatest(age(xmin), age(catalog_xmin))::int8 as xmin_age_tx 12 | from 13 | pg_replication_slots; 14 | -------------------------------------------------------------------------------- /docker/Dockerfile-daemon: -------------------------------------------------------------------------------- 1 | FROM golang:1.22 2 | 3 | # For showing Git version via 'pgwatch2 --version' 4 | ARG GIT_HASH 5 | ARG GIT_TIME 6 | ENV GIT_HASH=${GIT_HASH} 7 | ENV GIT_TIME=${GIT_TIME} 8 | 9 | ADD pgwatch2 /pgwatch2 10 | RUN cd /pgwatch2 && bash build_gatherer.sh 11 | 12 | 13 | FROM ubuntu:22.04 14 | 15 | RUN apt-get -q update && apt-get -qy install wget git && apt autoremove -y && mkdir /pgwatch2 16 | 17 | ADD pgwatch2/metrics /pgwatch2/metrics 18 | ADD pgwatch2/config /pgwatch2/config 19 | 20 | # Copy over the compiled gatherer 21 | COPY --from=0 /pgwatch2/pgwatch2 /pgwatch2 22 | 23 | RUN chgrp -R 0 /pgwatch2 \ 24 | && chmod -R g=u /pgwatch2 25 | 26 | # pgwatch2 internal status endpoint 27 | EXPOSE 8081 28 | # Prometheus metrics scraping port 29 | EXPOSE 9187 30 | 31 | USER 10001 32 | 33 | ENTRYPOINT ["/pgwatch2/pgwatch2"] 34 | -------------------------------------------------------------------------------- /pgwatch2/metrics/wal/10/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | case 4 | when pg_is_in_recovery() = false then 5 | pg_wal_lsn_diff(pg_current_wal_lsn(), '0/0')::int8 6 | else 7 | pg_wal_lsn_diff(pg_last_wal_replay_lsn(), '0/0')::int8 8 | end as xlog_location_b, 9 | case when pg_is_in_recovery() then 1 else 0 end as in_recovery_int, 10 | extract(epoch from (now() - pg_postmaster_start_time()))::int8 as postmaster_uptime_s, 11 | system_identifier::text as tag_sys_id, 12 | case 13 | when pg_is_in_recovery() = false then 14 | ('x'||substr(pg_walfile_name(pg_current_wal_lsn()), 1, 8))::bit(32)::int 15 | else 16 | (select min_recovery_end_timeline::int from pg_control_recovery()) 17 | end as timeline 18 | from pg_control_system(); 19 | -------------------------------------------------------------------------------- /pgwatch2/metrics/show_plans_realtime/10/metric.sql: -------------------------------------------------------------------------------- 1 | /* assumes pg_show_plans extension */ 2 | select /* pgwatch2_generated */ 3 | max((extract(epoch from now()) * 1e9)::int8) as epoch_ns, 4 | max(extract(epoch from now() - query_start))::int as max_s, 5 | avg(extract(epoch from now() - query_start))::int as avg_s, 6 | count(*), 7 | array_to_string(array_agg(distinct usename order by usename), ',') as "users", 8 | max(md5(plan)) as tag_hash, /* needed for influx */ 9 | plan, 10 | max(query) as query 11 | from 12 | pg_show_plans p 13 | join 14 | pg_stat_activity a 15 | using (pid) 16 | where 17 | p.pid != pg_backend_pid() 18 | and datname = current_database() 19 | and now() - query_start > '1s'::interval 20 | and backend_type = 'client backend' 21 | group by 22 | plan 23 | order by 24 | max_s desc 25 | limit 26 | 10 27 | ; -------------------------------------------------------------------------------- /pgwatch2/metrics/reco_superusers/9.1/metric_master.sql: -------------------------------------------------------------------------------- 1 | /* reco_* metrics have special handling - all results are stored actually under one 'recommendations' metric and 2 | following text columns are expected: reco_topic, object_name, recommendation, extra_info. 3 | */ 4 | with q_su as ( 5 | select count(*) from pg_roles where rolcanlogin and rolsuper 6 | ), 7 | q_total as ( 8 | select count(*) from pg_roles where rolcanlogin 9 | ) 10 | select /* pgwatch2_generated */ 11 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 12 | 'superuser_count'::text as tag_reco_topic, 13 | '-'::text as tag_object_name, 14 | 'too many superusers detected - review recommended'::text as recommendation, 15 | format('%s active superusers, %s total active users', q_su.count, q_total.count) as extra_info 16 | from 17 | q_su, q_total 18 | where 19 | q_su.count >= 10 20 | ; -------------------------------------------------------------------------------- /pgwatch2/metrics/db_size_approx/9.1/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | current_setting('block_size')::int8 * ( 4 | select sum(relpages) from pg_class c 5 | join pg_namespace n on n.oid = c.relnamespace 6 | where c.relpersistence != 't' 7 | ) as size_b, 8 | current_setting('block_size')::int8 * ( 9 | select sum(c.relpages + coalesce(ct.relpages, 0) + coalesce(cti.relpages, 0)) 10 | from pg_class c 11 | join pg_namespace n on n.oid = c.relnamespace 12 | left join pg_class ct on ct.oid = c.reltoastrelid 13 | left join pg_index ti on ti.indrelid = ct.oid 14 | left join pg_class cti on cti.oid = ti.indexrelid 15 | where nspname = 'pg_catalog' 16 | and (c.relkind = 'r' 17 | or c.relkind = 'i' and not c.relname ~ '^pg_toast') 18 | ) as catalog_size_b; 19 | -------------------------------------------------------------------------------- /docker/influxdb/non-root-user.patch: -------------------------------------------------------------------------------- 1 | diff --git a/influxdb/1.4/Dockerfile b/influxdb/1.4/Dockerfile 2 | index 3b4bcfe..77d65ec 100644 3 | --- a/influxdb/1.4/Dockerfile 4 | +++ b/influxdb/1.4/Dockerfile 5 | @@ -23,6 +23,10 @@ RUN ARCH= && dpkgArch="$(dpkg --print-architecture)" && \ 6 | gpg --batch --verify influxdb_${INFLUXDB_VERSION}_${ARCH}.deb.asc influxdb_${INFLUXDB_VERSION}_${ARCH}.deb && \ 7 | dpkg -i influxdb_${INFLUXDB_VERSION}_${ARCH}.deb && \ 8 | rm -f influxdb_${INFLUXDB_VERSION}_${ARCH}.deb* 9 | + 10 | +RUN chgrp -R 0 /var/lib/influxdb /etc/influxdb \ 11 | + && chmod -R g=u /var/lib/influxdb /etc/influxdb 12 | + 13 | COPY influxdb.conf /etc/influxdb/influxdb.conf 14 | 15 | EXPOSE 8086 16 | @@ -33,3 +37,4 @@ COPY entrypoint.sh /entrypoint.sh 17 | COPY init-influxdb.sh /init-influxdb.sh 18 | ENTRYPOINT ["/entrypoint.sh"] 19 | CMD ["influxd"] 20 | +USER 10001 21 | -------------------------------------------------------------------------------- /pgwatch2/metrics/00_helpers/get_psutil_disk_io_total/9.1/metric.sql: -------------------------------------------------------------------------------- 1 | /* Pre-requisites: PL/Pythonu and "psutil" Python package (e.g. pip install psutil) */ 2 | CREATE EXTENSION IF NOT EXISTS plpython3u; /* NB! "plpython3u" might need changing to "plpythonu" (Python 2) everywhere for older OS-es */ 3 | 4 | CREATE OR REPLACE FUNCTION get_psutil_disk_io_total( 5 | OUT read_count float8, OUT write_count float8, OUT read_bytes float8, OUT write_bytes float8 6 | ) 7 | LANGUAGE plpython3u 8 | AS $FUNCTION$ 9 | from psutil import disk_io_counters 10 | dc = disk_io_counters(perdisk=False) 11 | if dc: 12 | return dc.read_count, dc.write_count, dc.read_bytes, dc.write_bytes 13 | else: 14 | return None, None, None, None 15 | $FUNCTION$; 16 | 17 | GRANT EXECUTE ON FUNCTION get_psutil_disk_io_total() TO pgwatch2; 18 | COMMENT ON FUNCTION get_psutil_disk_io_total() IS 'created for pgwatch2'; 19 | -------------------------------------------------------------------------------- /pgwatch2/metrics/wal/10/metric_su.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | case 4 | when pg_is_in_recovery() = false then 5 | pg_wal_lsn_diff(pg_current_wal_lsn(), '0/0')::int8 6 | else 7 | pg_wal_lsn_diff(pg_last_wal_replay_lsn(), '0/0')::int8 8 | end as xlog_location_b, 9 | case when pg_is_in_recovery() then 1 else 0 end as in_recovery_int, 10 | extract(epoch from (now() - coalesce((pg_stat_file('postmaster.pid', true)).modification, pg_postmaster_start_time())))::int8 as postmaster_uptime_s, 11 | system_identifier::text as tag_sys_id, 12 | case 13 | when pg_is_in_recovery() = false then 14 | ('x'||substr(pg_walfile_name(pg_current_wal_lsn()), 1, 8))::bit(32)::int 15 | else 16 | (select min_recovery_end_timeline::int from pg_control_recovery()) 17 | end as timeline 18 | from pg_control_system(); 19 | -------------------------------------------------------------------------------- /docker/grafana/Dockerfile-grafana: -------------------------------------------------------------------------------- 1 | FROM debian:jessie 2 | 3 | ### 4 | # non-root adjustments to https://github.com/grafana/grafana-docker 5 | ### 6 | 7 | ARG DOWNLOAD_URL 8 | 9 | RUN apt-get update && \ 10 | apt-get -y --no-install-recommends install libfontconfig curl ca-certificates && \ 11 | apt-get clean && \ 12 | curl ${DOWNLOAD_URL} > /tmp/grafana.deb && \ 13 | dpkg -i /tmp/grafana.deb && \ 14 | rm /tmp/grafana.deb && \ 15 | apt-get autoremove -y && \ 16 | rm -rf /var/lib/apt/lists/* 17 | 18 | RUN chgrp -R 0 /var/lib/grafana /var/log/grafana /etc/grafana /usr/share/grafana /etc/passwd \ 19 | && chmod -R g=u /var/lib/grafana /var/log/grafana /etc/grafana/ /usr/share/grafana /etc/passwd 20 | 21 | VOLUME ["/var/lib/grafana", "/var/log/grafana", "/etc/grafana"] 22 | 23 | EXPOSE 3000 24 | 25 | COPY grafana-run.sh /run.sh 26 | 27 | USER 10001 28 | 29 | ENTRYPOINT ["/run.sh"] 30 | -------------------------------------------------------------------------------- /pgwatch2/metrics/table_bloat_approx_stattuple/9.5/metric_master.sql: -------------------------------------------------------------------------------- 1 | /* NB! accessing pgstattuple_approx directly requires superuser or pg_stat_scan_tables/pg_monitor builtin roles */ 2 | select /* pgwatch2_generated */ 3 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 4 | quote_ident(n.nspname)||'.'||quote_ident(c.relname) as tag_full_table_name, 5 | approx_free_percent, 6 | approx_free_space as approx_free_space_b, 7 | approx_tuple_count, 8 | dead_tuple_percent, 9 | dead_tuple_len as dead_tuple_len_b 10 | from 11 | pg_class c 12 | join lateral pgstattuple_approx(c.oid) st on (c.oid not in (select relation from pg_locks where mode = 'AccessExclusiveLock')) -- skip locked tables, 13 | join pg_namespace n on n.oid = c.relnamespace 14 | where 15 | relkind in ('r', 'm') 16 | and c.relpages >= 128 -- tables > 1mb 17 | and not n.nspname like any (array[E'pg\\_%', 'information_schema']); 18 | -------------------------------------------------------------------------------- /pgwatch2/metrics/stat_activity_realtime/10/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | pid as tag_pid, 4 | usename::text AS user, 5 | application_name AS appname, 6 | coalesce(client_addr::text, 'local') AS ip, 7 | extract(epoch FROM (now() - query_start))::int AS duration_s, 8 | (coalesce(wait_event_type, '') IN ('LWLockNamed', 'Lock', 'BufferPin'))::int AS waiting, 9 | array_to_string(pg_blocking_pids(pid), ',') as blocking_pids, 10 | ltrim(regexp_replace(query, E'[ \\t\\n\\r]+' , ' ', 'g'))::varchar(300) AS query 11 | FROM 12 | pg_stat_activity 13 | WHERE 14 | state != 'idle' 15 | AND backend_type IN ('client backend', 'autovacuum worker') 16 | AND pid != pg_backend_pid() 17 | AND datname = current_database() 18 | AND now() - query_start > '500ms'::interval 19 | ORDER BY 20 | now() - query_start DESC 21 | LIMIT 25; 22 | -------------------------------------------------------------------------------- /pgwatch2/sql/config_store/migrations/v1.7.1-2_metric_attributes.sql: -------------------------------------------------------------------------------- 1 | begin; 2 | 3 | set role to pgwatch2; 4 | 5 | create table pgwatch2.metric_attribute ( 6 | ma_metric_name text not null primary key, 7 | ma_last_modified_on timestamptz not null default now(), 8 | ma_metric_attrs jsonb not null, 9 | 10 | check (ma_metric_name ~ '^[a-z0-9_]+$') 11 | ); 12 | 13 | insert into pgwatch2.metric_attribute (ma_metric_name, ma_metric_attrs) 14 | select m, '{"is_instance_level": true}' 15 | from unnest( 16 | array['archiver', 'backup_age_pgbackrest', 'backup_age_walg', 'bgwriter', 'buffercache_by_db', 'buffercache_by_type', 17 | 'cpu_load', 'psutil_cpu', 'psutil_disk', 'psutil_disk_io_total', 'psutil_mem', 'replication', 'replication_slots', 18 | 'smart_health_per_disk', 'wal', 'wal_receiver', 'wal_size'] 19 | ) m; 20 | 21 | insert into pgwatch2.schema_version (sv_tag) values ('1.7.1'); 22 | 23 | end; 24 | -------------------------------------------------------------------------------- /pgwatch2/metrics/invalid_indexes/10/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | format('%I.%I', n.nspname , ci.relname) as tag_index_full_name, 4 | coalesce(pg_relation_size(indexrelid), 0) as index_size_b 5 | from 6 | pg_index i 7 | join pg_class ci on ci.oid = i.indexrelid 8 | join pg_class cr on cr.oid = i.indrelid 9 | join pg_namespace n on n.oid = ci.relnamespace 10 | where not n.nspname like E'pg\\_temp%' 11 | and not indisvalid 12 | and not exists ( /* leave out ones that are being actively rebuilt */ 13 | select * from pg_locks l 14 | join pg_stat_activity a using (pid) 15 | where l.relation = i.indexrelid 16 | and a.state = 'active' 17 | and a.query ~* 'concurrently' 18 | ) 19 | and not exists (select * from pg_locks where relation = indexrelid and mode = 'AccessExclusiveLock') /* can't get size then */ 20 | order by index_size_b desc 21 | limit 100; 22 | -------------------------------------------------------------------------------- /pgwatch2/startup-scripts/pgwatch2.service: -------------------------------------------------------------------------------- 1 | # This is an example of a systemD config file for pgwatch2. 2 | # You can copy it to "/etc/systemd/system/pgwatch2.service", adjust as necessary and then call 3 | # systemctl daemon-reload && systemctl start pgwatch2 && systemctl enable pgwatch2 4 | # to start and also enable auto-start after reboot. 5 | 6 | [Unit] 7 | Description=Pgwatch2 Gathering Daemon 8 | After=network-online.target 9 | # If you're using the config DB approach and when on the same machine then it's a good idea to launch after Postgres 10 | #After=postgresql@12-main.service 11 | 12 | [Service] 13 | User=pgwatch2 14 | Type=notify 15 | ExecStart=/usr/bin/pgwatch2-daemon -c /etc/pgwatch2/config/instances.yaml -m /etc/pgwatch2/metrics --datastore=postgres --pg-metric-store-conn-str=postgresql://pgwatch2@localhost:5432/pgwatch2_metrics 16 | Restart=on-failure 17 | TimeoutStartSec=0 18 | 19 | [Install] 20 | WantedBy=multi-user.target 21 | -------------------------------------------------------------------------------- /pgwatch2/metrics/unused_indexes/10/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | * 4 | from ( 5 | select 6 | format('%I.%I', sui.schemaname, sui.indexrelname) as tag_index_full_name, 7 | sui.idx_scan, 8 | coalesce(pg_relation_size(sui.indexrelid), 0) as index_size_b, 9 | system_identifier::text as tag_sys_id /* to easily check also all replicas as could be still used there */ 10 | from 11 | pg_stat_user_indexes sui 12 | join pg_index i on i.indexrelid = sui.indexrelid 13 | join pg_control_system() on true 14 | where not sui.schemaname like E'pg\\_temp%' 15 | and idx_scan = 0 16 | and not (indisprimary or indisunique or indisexclusion) 17 | and not exists (select * from pg_locks where relation = sui.relid and mode = 'AccessExclusiveLock') 18 | ) x 19 | where index_size_b > 100*1024^2 /* list >100MB only */ 20 | order by index_size_b desc 21 | limit 25; 22 | -------------------------------------------------------------------------------- /docker/Dockerfile-webui: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | 3 | RUN apt-get -q update && apt-get -qy install python3-pip \ 4 | && mkdir /pgwatch2 5 | 6 | ### 7 | ### add webpy source and configure installed components 8 | ### 9 | 10 | ADD webpy /pgwatch2/webpy 11 | ADD docker/launch-wrapper-webui.sh /pgwatch2 12 | 13 | # Get Web UI requirements 14 | RUN pip3 install -U "pip < 21.0" && pip3 install -r /pgwatch2/webpy/requirements_pg_metrics.txt \ 15 | && pip3 install -r /pgwatch2/webpy/requirements_pg_metrics.txt \ 16 | && chgrp -R 0 /pgwatch2 \ 17 | && chmod -R g=u /pgwatch2 \ 18 | && mkdir /pgwatch2/persistent-config \ 19 | && chgrp -R 0 /pgwatch2/webpy /pgwatch2/persistent-config \ 20 | && chmod -R g=u /pgwatch2/webpy /pgwatch2/persistent-config 21 | 22 | 23 | # Admin UI for configuring servers to be monitored 24 | EXPOSE 8080 25 | 26 | VOLUME /pgwatch2/persistent-config 27 | 28 | USER 10001 29 | 30 | ENTRYPOINT ["/pgwatch2/launch-wrapper-webui.sh"] 31 | -------------------------------------------------------------------------------- /pgwatch2/metrics/00_helpers/get_psutil_mem/9.1/metric.sql: -------------------------------------------------------------------------------- 1 | /* Pre-requisites: PL/Pythonu and "psutil" Python package (e.g. pip install psutil) */ 2 | CREATE EXTENSION IF NOT EXISTS plpython3u; /* NB! "plpython3u" might need changing to "plpythonu" (Python 2 everywhere for new OS-es */ 3 | 4 | CREATE OR REPLACE FUNCTION get_psutil_mem( 5 | OUT total float8, OUT used float8, OUT free float8, OUT buff_cache float8, OUT available float8, OUT percent float8, 6 | OUT swap_total float8, OUT swap_used float8, OUT swap_free float8, OUT swap_percent float8 7 | ) 8 | LANGUAGE plpython3u 9 | AS $FUNCTION$ 10 | from psutil import virtual_memory, swap_memory 11 | vm = virtual_memory() 12 | sw = swap_memory() 13 | return vm.total, vm.used, vm.free, vm.buffers + vm.cached, vm.available, vm.percent, sw.total, sw.used, sw.free, sw.percent 14 | $FUNCTION$; 15 | 16 | GRANT EXECUTE ON FUNCTION get_psutil_mem() TO pgwatch2; 17 | COMMENT ON FUNCTION get_psutil_mem() IS 'created for pgwatch2'; 18 | -------------------------------------------------------------------------------- /grafana_dashboards/delete_all_old_pw2_dashes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ## for testing purposes mostly 4 | 5 | export PGHOST=localhost 6 | export PGPORT=5432 7 | export PGUSER=postgres 8 | export PGDATABASE=pgwatch2_grafana 9 | 10 | DRY_RUN=1 11 | PW2_DASHBOARD_NAMES=`ls -1 postgres/v8/` 12 | 13 | if [ "$?" -ne 0 ]; then 14 | echo "could not list dashboards...are you in the dashboards root dir?" 15 | exit 1 16 | fi 17 | 18 | if [ -n "$1" ]; then 19 | DRY_RUN=0 20 | echo "deleting ALL pw2 dashboards!" 21 | echo "hit ctl+c now if not sure" 22 | echo "sleeping 5s..." 23 | sleep 5 24 | else 25 | echo "--dry-run on ALL pw2 dashboards. add any parameter to script call to really delete" 26 | fi 27 | 28 | for slug in $PW2_DASHBOARD_NAMES ; do 29 | if [ "$DRY_RUN" -eq 0 ]; then 30 | SQL="delete from dashboard where slug = '$slug'" 31 | echo "$SQL" 32 | echo "$SQL" | psql -Xq 33 | else 34 | echo "would delete '$slug' ..." 35 | fi 36 | done 37 | 38 | echo "done" 39 | -------------------------------------------------------------------------------- /pgwatch2/metrics/reco_sprocs_wo_search_path/9.1/metric_master.sql: -------------------------------------------------------------------------------- 1 | with q_sprocs as ( 2 | select /* pgwatch2_generated */ 3 | format('%s.%s', quote_ident(nspname), quote_ident(proname)) as sproc_name, 4 | 'alter function ' || proname || '(' || pg_get_function_arguments(p.oid) || ') set search_path = X;' as fix_sql 5 | from 6 | pg_proc p 7 | join pg_namespace n on n.oid = p.pronamespace 8 | where prosecdef and not 'search_path' = ANY(coalesce(proconfig, '{}'::text[])) 9 | and not pg_catalog.obj_description(p.oid, 'pg_proc') ~ 'pgwatch2' 10 | ) 11 | select /* pgwatch2_generated */ 12 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 13 | 'sprocs_wo_search_path'::text as tag_reco_topic, 14 | sproc_name::text as tag_object_name, 15 | fix_sql::text as recommendation, 16 | 'functions without fixed search_path can be potentially abused by malicious users if used objects are not fully qualified'::text as extra_info 17 | from 18 | q_sprocs 19 | order by 20 | tag_object_name, extra_info; 21 | -------------------------------------------------------------------------------- /pgwatch2/metrics/table_io_stats/9.0/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | schemaname::text as tag_schema, 4 | relname::text as tag_table_name, 5 | quote_ident(schemaname)||'.'||quote_ident(relname) as tag_table_full_name, 6 | heap_blks_read, 7 | heap_blks_hit, 8 | idx_blks_read, 9 | idx_blks_hit, 10 | toast_blks_read, 11 | toast_blks_hit, 12 | tidx_blks_read, 13 | tidx_blks_hit 14 | FROM 15 | pg_statio_user_tables 16 | WHERE 17 | NOT schemaname LIKE E'pg\\_temp%' 18 | AND (heap_blks_read > 0 OR heap_blks_hit > 0 OR idx_blks_read > 0 OR idx_blks_hit > 0 OR tidx_blks_read > 0 OR tidx_blks_hit > 0) 19 | ORDER BY 20 | coalesce(heap_blks_read, 0) + 21 | coalesce(heap_blks_hit, 0) + 22 | coalesce(idx_blks_read, 0) + 23 | coalesce(idx_blks_hit, 0) + 24 | coalesce(toast_blks_read, 0) + 25 | coalesce(toast_blks_hit, 0) + 26 | coalesce(tidx_blks_read, 0) + 27 | coalesce(tidx_blks_hit, 0) 28 | DESC LIMIT 300; 29 | -------------------------------------------------------------------------------- /pgwatch2/metrics/stat_io/16/metric.sql: -------------------------------------------------------------------------------- 1 | SELECT /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | coalesce(backend_type, 'total') as tag_backend_type, 4 | sum(coalesce(reads, 0))::int8 as reads, 5 | (sum(coalesce(reads, 0) * op_bytes) / 1e6)::int8 as read_bytes_mb, 6 | sum(coalesce(read_time, 0))::int8 as read_time_ms, 7 | sum(coalesce(writes, 0))::int8 as writes, 8 | (sum(coalesce(writes, 0) * op_bytes) / 1e6)::int8 as write_bytes_mb, 9 | sum(coalesce(write_time, 0))::int8 as write_time_ms, 10 | sum(coalesce(writebacks, 0))::int8 as writebacks, 11 | (sum(coalesce(writebacks, 0) * op_bytes) / 1e6)::int8 as writeback_bytes_mb, 12 | sum(coalesce(writeback_time, 0))::int8 as writeback_time_ms, 13 | sum(coalesce(fsyncs, 0))::int8 fsyncs, 14 | sum(coalesce(fsync_time, 0))::int8 fsync_time_ms, 15 | max(extract(epoch from now() - stats_reset)::int) as stats_reset_s 16 | FROM 17 | pg_stat_io 18 | GROUP BY 19 | ROLLUP (backend_type); 20 | -------------------------------------------------------------------------------- /pgwatch2/metrics/reco_drop_index/9.0/metric_master.sql: -------------------------------------------------------------------------------- 1 | /* assumes the pg_qualstats extension */ 2 | with q_database_size as ( 3 | select pg_database_size(current_database()) as database_size_b 4 | ) 5 | select /* pgwatch2_generated */ 6 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 7 | 'drop_index'::text as tag_reco_topic, 8 | quote_ident(schemaname)||'.'||quote_ident(indexrelname) as tag_object_name, 9 | ('DROP INDEX ' || quote_ident(schemaname)||'.'||quote_ident(indexrelname) || ';')::text as recommendation, 10 | 'NB! Before dropping make sure to also check replica pg_stat_user_indexes.idx_scan count if using them for queries'::text as extra_info 11 | from 12 | pg_stat_user_indexes 13 | join 14 | pg_index using (indexrelid) 15 | join 16 | q_database_size on true 17 | where 18 | idx_scan = 0 19 | and ((pg_relation_size(indexrelid)::numeric / database_size_b) > 0.005 /* 0.5% DB size threshold */ 20 | or indisvalid) 21 | and not indisprimary 22 | and not schemaname like '_timescaledb%' 23 | ; 24 | -------------------------------------------------------------------------------- /pgwatch2/config/scalefield.template: -------------------------------------------------------------------------------- 1 | - unique_name: ${NAME} 2 | dbtype: postgres 3 | host: 127.0.0.1 4 | dbname: postgres 5 | user: ${USER} 6 | password: ${PASSWORD} 7 | sslmode: require # supported options: disable, require, verify-ca, verify-full 8 | stmt_timeout: 5 # in seconds 9 | is_superuser: true 10 | preset_metrics: remotedba 11 | custom_metrics: # if both preset and custom are specified, custom wins 12 | preset_metrics_standby: # optional metrics configuration for standby / replica state, v1.8.1+ 13 | custom_metrics_standby: 14 | dbname_include_pattern: # regex to filter databases to actually monitor for the "continuous" modes 15 | dbname_exclude_pattern: 16 | is_enabled: true 17 | custom_tags: 18 | support_plan: ${SUPPORT_PLAN} 19 | customer: ${CUSTOMER} 20 | cluster_name: ${CLUSTER_NAME} 21 | group: default # just for logical grouping of DB hosts or for "sharding", i.e. splitting the workload between many gatherer daemons 22 | sslrootcert: '' 23 | sslcert: '' 24 | sslkey: '' 25 | -------------------------------------------------------------------------------- /pgwatch2/metrics/reco_drop_index/9.4/metric_master.sql: -------------------------------------------------------------------------------- 1 | /* assumes the pg_qualstats extension */ 2 | with q_database_size as ( 3 | select pg_database_size(current_database()) as database_size_b 4 | ) 5 | select /* pgwatch2_generated */ 6 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 7 | 'drop_index'::text as tag_reco_topic, 8 | quote_ident(schemaname)||'.'||quote_ident(indexrelname) as tag_object_name, 9 | ('DROP INDEX ' || quote_ident(schemaname)||'.'||quote_ident(indexrelname) || ';')::text as recommendation, 10 | 'NB! Make sure to also check replica pg_stat_user_indexes.idx_scan count if using them for queries'::text as extra_info 11 | from 12 | pg_stat_user_indexes 13 | join 14 | pg_index using (indexrelid) 15 | join 16 | q_database_size on true 17 | where 18 | idx_scan = 0 19 | and ((pg_relation_size(indexrelid)::numeric / database_size_b) > 0.005 /* 0.5% DB size threshold */ 20 | or indisvalid) 21 | and not indisprimary 22 | and not indisreplident 23 | and not schemaname like '_timescaledb%' 24 | ; 25 | -------------------------------------------------------------------------------- /webpy/startup-scripts/pgwatch2-webui.service: -------------------------------------------------------------------------------- 1 | # This is an example of a systemD config file for pgwatch2. 2 | # You can copy it to "/etc/systemd/system/pgwatch2-webui.service", adjust as necessary and then call 3 | # systemctl daemon-reload && systemctl start pgwatch2-webui && systemctl enable pgwatch2-webui 4 | # to start and also enable auto-start after reboot. 5 | 6 | [Unit] 7 | Description=PgWatch2 Web UI 8 | After=network-online.target 9 | # If you're using the config DB approach and when on the same machine then it's a good idea to launch after Postgres 10 | #After=postgresql@12-main.service 11 | 12 | [Service] 13 | Environment="PW2_WEBPORT=8080" 14 | Environment="PW2_PGHOST=localhost" 15 | Environment="PW2_DATASTORE=postgres" 16 | Environment="PW2_PG_METRIC_STORE_CONN_STR=postgresql://pgwatch2@localhost:5432/pgwatch2_metrics" 17 | 18 | Type=simple 19 | User=pgwatch2 20 | WorkingDirectory=/etc/pgwatch2/webpy 21 | ExecStart=/usr/bin/python3 /etc/pgwatch2/webpy/web.py 22 | TimeoutStartSec=0 23 | 24 | [Install] 25 | WantedBy=multi-user.target 26 | -------------------------------------------------------------------------------- /pgwatch2/metrics/reco_add_index_ext_qualstats_2.0/9.1/metric_master.sql: -------------------------------------------------------------------------------- 1 | /* assumes the pg_qualstats extension and superuser or select grant on pg_qualstats_index_advisor() function */ 2 | select /* pgwatch2_generated */ 3 | epoch_ns, 4 | tag_reco_topic, 5 | tag_object_name, 6 | recommendation, 7 | case when exists (select * from pg_inherits 8 | where inhrelid = regclass(tag_object_name) 9 | ) then 'NB! Partitioned table, create the index on parent' else extra_info 10 | end as extra_info 11 | FROM ( 12 | SELECT (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 13 | 'create_index'::text as tag_reco_topic, 14 | (regexp_matches(v::text, E'ON (.*?) '))[1] as tag_object_name, 15 | v::text as recommendation, 16 | '' as extra_info 17 | FROM json_array_elements( 18 | pg_qualstats_index_advisor() -> 'indexes') v 19 | ) x 20 | ORDER BY tag_object_name; 21 | -------------------------------------------------------------------------------- /pgwatch2/metrics/stat_activity_realtime/9.2/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | pid as tag_pid, 4 | usename::text AS user, 5 | application_name AS appname, 6 | coalesce(client_addr::text, 'local') AS ip, 7 | extract(epoch FROM (now() - query_start))::int AS duration_s, 8 | waiting::int, 9 | case when sa.waiting then 10 | (select array_to_string((select array_agg(distinct b.pid order by b.pid) from pg_locks b join pg_locks l on l.database = b.database and l.relation = b.relation 11 | where l.pid = sa.pid and b.pid != l.pid and b.granted and not l.granted), ',')) 12 | else 13 | null 14 | end as blocking_pids, 15 | ltrim(regexp_replace(query, E'[ \\t\\n\\r]+' , ' ', 'g'))::varchar(300) AS query 16 | FROM 17 | pg_stat_activity sa 18 | WHERE 19 | state != 'idle' 20 | AND pid != pg_backend_pid() 21 | AND datname = current_database() 22 | AND now() - query_start > '500ms'::interval 23 | ORDER BY 24 | now() - query_start DESC 25 | LIMIT 25; 26 | -------------------------------------------------------------------------------- /pgwatch2/non-sql-metric-gathering-samples/disk-smart-health/smart-health.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ## Pushes disk SMART status code to the metrics DB (to enable alerting in Grafana) 4 | ## Meant for Cron for example 5 | 6 | export PGHOST=localhost 7 | export PGDATABASE=pgwatch2_metrics 8 | export PGUSER=pgwatch2 9 | # for password .pgpass is recommended 10 | export PGPORT=5432 11 | 12 | SMART_CMD=$(sudo smartctl --quietmode=silent --health /dev/sda) 13 | RETCODE=$? 14 | 15 | METRIC_NAME=smart_health 16 | DBNAME=x 17 | SQL=$(cat </dev/null 26 | #psql -XAtqc "select admin.ensure_partition_metric_dbname_time('${METRIC_NAME}', '${DBNAME}', now())" &>/dev/null 27 | psql -XAtqc "select admin.ensure_partition_metric_time('${METRIC_NAME}', now())" &>/dev/null 28 | psql -XAtqc "${SQL}" 29 | -------------------------------------------------------------------------------- /docker/Dockerfile-scalefield: -------------------------------------------------------------------------------- 1 | FROM golang:1.22 2 | 3 | # For showing Git version via 'pgwatch2 --version' 4 | ARG GIT_HASH 5 | ARG GIT_TIME 6 | ENV GIT_HASH=${GIT_HASH} 7 | ENV GIT_TIME=${GIT_TIME} 8 | 9 | ADD pgwatch2 /pgwatch2 10 | RUN cd /pgwatch2 && bash build_gatherer.sh 11 | 12 | FROM alpine 13 | 14 | RUN apk update && \ 15 | apk add --no-cache git python3 py3-psycopg2 py3-yaml && \ 16 | apk add --no-cache --virtual .build-deps && \ 17 | apk add --no-cache gettext && \ 18 | apk add --no-cache libc6-compat && \ 19 | mkdir /pgwatch2 20 | 21 | ADD pgwatch2/metrics /pgwatch2/metrics 22 | ADD pgwatch2/config /pgwatch2/config 23 | ADD docker-launcher-scalefield.sh /pgwatch2/entrypoint.sh 24 | RUN chmod +x /pgwatch2/entrypoint.sh 25 | 26 | # Copy over the compiled gatherer 27 | COPY --from=0 /pgwatch2/pgwatch2 /pgwatch2 28 | 29 | RUN chgrp -R 0 /pgwatch2 \ 30 | && chmod -R g=u /pgwatch2 31 | 32 | # pgwatch2 internal status endpoint 33 | EXPOSE 8081 34 | # Prometheus metrics scraping port 35 | EXPOSE 9189 36 | 37 | USER 10001 38 | 39 | ENTRYPOINT ["/pgwatch2/entrypoint.sh"] 40 | -------------------------------------------------------------------------------- /pgwatch2/sql/metric_store/custom/metric_store_custom.sql: -------------------------------------------------------------------------------- 1 | /* 2 | NB! Custom schema is for those cases where the available presets are not satisfactory / applicable. 3 | Then the metrics gathering daemon will try to insert all metrics into the "metrics" table and the user 4 | can freely re-route the data however he likes with an according trigger. In that case also data 5 | all table creation and data cleanup must be performed by the user. Can be used also when only having 6 | a couple of DB-s and performance / minimal storage is no issue. 7 | */ 8 | 9 | SET ROLE TO pgwatch2; 10 | 11 | -- drop table if exists metrics; 12 | 13 | create table public.metrics ( 14 | time timestamptz not null default now(), 15 | dbname text not null, 16 | metric text not null, 17 | data jsonb not null, 18 | tag_data jsonb 19 | ); 20 | 21 | comment on table public.metrics is 'a master table for "custom" mode'; 22 | 23 | /* suggested indexes */ 24 | create index on public.metrics (dbname, metric, time); 25 | create index on public.metrics using gin (metric, tag_data, time) where tag_data notnull; 26 | 27 | RESET ROLE; 28 | -------------------------------------------------------------------------------- /pgwatch2/metrics/replication/9.2/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | application_name as tag_application_name, 4 | concat(coalesce(client_addr::text, client_hostname), '_', client_port::text) as tag_client_info, 5 | coalesce(pg_xlog_location_diff(case when pg_is_in_recovery() then pg_last_xlog_receive_location() else pg_current_xlog_location() end, write_location)::int8, 0) as write_lag_b, 6 | coalesce(pg_xlog_location_diff(case when pg_is_in_recovery() then pg_last_xlog_receive_location() else pg_current_xlog_location() end, flush_location)::int8, 0) as flush_lag_b, 7 | coalesce(pg_xlog_location_diff(case when pg_is_in_recovery() then pg_last_xlog_receive_location() else pg_current_xlog_location() end, replay_location)::int8, 0) as replay_lag_b, 8 | state, 9 | sync_state, 10 | case when sync_state in ('sync', 'quorum') then 1 else 0 end as is_sync_int, 11 | case when pg_is_in_recovery() then 1 else 0 end as in_recovery_int 12 | from 13 | get_stat_replication() 14 | where 15 | coalesce(application_name, '') not in ('pg_basebackup', 'pg_rewind'); 16 | 17 | -------------------------------------------------------------------------------- /pgwatch2/metrics/replication/9.2/metric_su.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | application_name as tag_application_name, 4 | concat(coalesce(client_addr::text, client_hostname), '_', client_port::text) as tag_client_info, 5 | coalesce(pg_xlog_location_diff(case when pg_is_in_recovery() then pg_last_xlog_receive_location() else pg_current_xlog_location() end, write_location)::int8, 0) as write_lag_b, 6 | coalesce(pg_xlog_location_diff(case when pg_is_in_recovery() then pg_last_xlog_receive_location() else pg_current_xlog_location() end, flush_location)::int8, 0) as flush_lag_b, 7 | coalesce(pg_xlog_location_diff(case when pg_is_in_recovery() then pg_last_xlog_receive_location() else pg_current_xlog_location() end, replay_location)::int8, 0) as replay_lag_b, 8 | state, 9 | sync_state, 10 | case when sync_state in ('sync', 'quorum') then 1 else 0 end as is_sync_int, 11 | case when pg_is_in_recovery() then 1 else 0 end as in_recovery_int 12 | from 13 | pg_stat_replication 14 | where 15 | coalesce(application_name, '') not in ('pg_basebackup', 'pg_rewind'); 16 | 17 | -------------------------------------------------------------------------------- /docker/test/launch_all_pg_versions/add_all_to_monitoring.sql: -------------------------------------------------------------------------------- 1 | /* primaries */ 2 | insert into pgwatch2.monitored_db (md_unique_name, md_preset_config_name, md_config, md_hostname, md_port, md_dbname, md_user, md_password, md_is_superuser) 3 | select 'pg'||pgver, 'exhaustive', null, 'localhost', '543'||pgver, 'postgres', 'postgres', 'postgres', true 4 | from unnest(array[90,91,92,93,94,95,96,10,11,12]) as pgver 5 | where not exists ( 6 | select * from pgwatch2.monitored_db where (md_unique_name, md_hostname, md_dbname) = ('pg'||pgver, 'localhost', 'postgres') 7 | ) 8 | ; 9 | 10 | /* replicas */ 11 | insert into pgwatch2.monitored_db (md_unique_name, md_preset_config_name, md_config, md_hostname, md_port, md_dbname, md_user, md_password, md_is_superuser) 12 | select 'pg'||pgver||'_repl', 'exhaustive', null, 'localhost', ('543'||pgver)::int + 1000, 'postgres', 'postgres', 'postgres', true 13 | from unnest(array[90,91,92,93,94,95,96,10,11,12]) as pgver 14 | where not exists ( 15 | select * from pgwatch2.monitored_db where (md_unique_name, md_hostname, md_dbname) = ('pg'||pgver||'_repl', 'localhost', 'postgres') 16 | ) 17 | ; 18 | -------------------------------------------------------------------------------- /pgwatch2/bootstrap/insert_test_monitored_db.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Normally we insert the local pwatch2 config DB as "test" database for monitoring to instantly get some sample graphs 4 | 5 | export PGUSER=postgres 6 | 7 | if [ -z "$NOTESTDB" ] ; then 8 | 9 | if [ ! -f /pgwatch2/test_db_installed_marker ] ; then 10 | 11 | while true ; do 12 | 13 | # It will take some time for Postgres to start 14 | sleep 1 15 | 16 | $(pg_isready -q) 17 | 18 | if [[ "$?" -ne 0 ]] ; then 19 | continue 20 | else 21 | break 22 | fi 23 | 24 | done 25 | 26 | SQL=$(cat <<-HERE 27 | insert into pgwatch2.monitored_db (md_unique_name, md_preset_config_name, md_config, md_hostname, md_port, md_dbname, md_user, md_password) 28 | select 'test', 'full', null, 'localhost', '5432', 'pgwatch2', 'pgwatch2', 'pgwatch2admin' 29 | where not exists ( 30 | select * from pgwatch2.monitored_db where (md_unique_name, md_hostname, md_dbname) = ('test', 'localhost', 'pgwatch2') 31 | ) 32 | HERE 33 | ) 34 | 35 | psql -c "$SQL" pgwatch2 36 | 37 | if [ $? -eq 0 ] ; then 38 | touch /pgwatch2/test_db_installed_marker 39 | fi 40 | 41 | fi 42 | 43 | fi 44 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Close Stale Issues and PRs 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | 7 | workflow_dispatch: 8 | 9 | 10 | jobs: 11 | stale: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/stale@v9 15 | with: 16 | repo-token: ${{ secrets.GITHUB_TOKEN }} 17 | stale-issue-label: 'stale' 18 | stale-pr-label: 'stale' 19 | stale-issue-message: | 20 | 📅 This issue has been automatically marked as stale because lack of recent activity. It will be closed if no further activity occurs. 21 | ♻️ If you think there is new information allowing us to address the issue, please reopen it and provide us with updated details. 22 | 🤝 Thank you for your contributions. 23 | stale-pr-message: | 24 | 📅 This PR has been automatically marked as stale because lack of recent activity. It will be closed if no further activity occurs. 25 | ♻️ If you think there is new information allowing us to address this PR, please reopen it and provide us with updated details. 26 | 🤝 Thank you for your contributions. 27 | -------------------------------------------------------------------------------- /pgwatch2/metrics/stat_activity_realtime/9.0/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | pid as tag_pid, 4 | usename::text AS user, 5 | application_name AS appname, 6 | coalesce(client_addr::text, 'local') AS ip, 7 | extract(epoch FROM (now() - query_start))::int AS duration_s, 8 | waiting::int, 9 | case when sa.waiting then 10 | (select array_to_string((select array_agg(distinct b.pid order by b.pid) from pg_locks b join pg_locks l on l.database = b.database and l.relation = b.relation 11 | where l.pid = sa.procpid and b.pid != l.pid and b.granted and not l.granted), ',')) 12 | else 13 | null 14 | end as blocking_pids, 15 | ltrim(regexp_replace(current_query, E'[ \\t\\n\\r]+' , ' ', 'g'))::varchar(300) AS query 16 | FROM 17 | pg_stat_activity sa 18 | WHERE 19 | current_query <> '' 20 | AND procpid != pg_backend_pid() 21 | AND datname = current_database() 22 | AND NOW() - query_start > '500ms'::interval 23 | ORDER BY 24 | NOW() - query_start DESC 25 | LIMIT 25; 26 | -------------------------------------------------------------------------------- /pgwatch2/metrics/table_bloat_approx_summary/9.5/metric_su.sql: -------------------------------------------------------------------------------- 1 | with table_bloat_approx as ( 2 | select 3 | avg(approx_free_percent)::double precision as approx_free_percent, 4 | sum(approx_free_space)::double precision as approx_free_space, 5 | avg(dead_tuple_percent)::double precision as dead_tuple_percent, 6 | sum(dead_tuple_len)::double precision as dead_tuple_len 7 | from 8 | pg_class c 9 | join 10 | pg_namespace n on n.oid = c.relnamespace 11 | join lateral pgstattuple_approx(c.oid) on (c.oid not in (select relation from pg_locks where mode = 'AccessExclusiveLock')) -- skip locked tables 12 | where 13 | relkind in ('r', 'm') 14 | and c.relpages >= 128 -- tables >1mb 15 | and not n.nspname like any (array[E'pg\\_%', 'information_schema']) 16 | ) 17 | select /* pgwatch2_generated */ 18 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 19 | approx_free_percent, 20 | approx_free_space as approx_free_space_b, 21 | dead_tuple_percent, 22 | dead_tuple_len as dead_tuple_len_b 23 | from 24 | table_bloat_approx 25 | where 26 | approx_free_space > 0; 27 | -------------------------------------------------------------------------------- /docker-launcher-scalefield.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Export environment variables with default values 4 | export USER=${DATA_SOURCE_USER:-postgres} 5 | export PASSWORD=${DATA_SOURCE_PASS:-postgres} 6 | export NAME=${DATA_SOURCE_URI:-localhost} 7 | export CLUSTERNAME=${DATA_SOURCE_CLUSTER:-localhost} 8 | export CUSTOMER=${PG_EXPORTER_CONSTANT_LABEL_CUSTOMER:-customer} 9 | export CLUSTER_NAME=${PG_EXPORTER_CONSTANT_LABEL_CLUSTER_NAME:-cluster_name} 10 | export SUPPORT_PLAN=${PG_EXPORTER_CONSTANT_LABEL_SUPPORT_PLAN:-support_plan} 11 | 12 | # Use envsubst to substitute the variables in the template 13 | envsubst < /pgwatch2/config/scalefield.template > /pgwatch2/config/scalefield.yaml 14 | 15 | /pgwatch2/metrics/00_helpers/rollout_helper.py --mode single-db --host "$CLUSTERNAME" --dbname postgres --user "$USER" --password "$PASSWORD" --monitoring-user "$USER" --confirm --metrics-path /pgwatch2/metrics/00_helpers/ --helpers get_load_average,get_psutil_cpu,get_psutil_disk_io_total,get_psutil_disk,get_psutil_mem --excluded-helpers "" 16 | 17 | exec /pgwatch2/pgwatch2 -c /pgwatch2/config/scalefield.yaml --adhoc-create-helpers=true --prometheus-async-mode=true --prometheus-port=9189 --datastore=prometheus 18 | -------------------------------------------------------------------------------- /pgwatch2/metrics/db_stats/10/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | numbackends, 4 | xact_commit, 5 | xact_rollback, 6 | blks_read, 7 | blks_hit, 8 | tup_returned, 9 | tup_fetched, 10 | tup_inserted, 11 | tup_updated, 12 | tup_deleted, 13 | conflicts, 14 | temp_files, 15 | temp_bytes, 16 | deadlocks, 17 | blk_read_time, 18 | blk_write_time, 19 | extract(epoch from (now() - pg_postmaster_start_time()))::int8 as postmaster_uptime_s, 20 | extract(epoch from (now() - pg_backup_start_time()))::int8 as backup_duration_s, 21 | case when pg_is_in_recovery() then 1 else 0 end as in_recovery_int, 22 | system_identifier::text as tag_sys_id, 23 | (select count(*) from pg_index i 24 | where not indisvalid 25 | and not exists ( /* leave out ones that are being actively rebuilt */ 26 | select * from pg_locks l 27 | join pg_stat_activity a using (pid) 28 | where l.relation = i.indexrelid 29 | and a.state = 'active' 30 | and a.query ~* 'concurrently' 31 | )) as invalid_indexes 32 | from 33 | pg_stat_database, pg_control_system() 34 | where 35 | datname = current_database(); 36 | -------------------------------------------------------------------------------- /pgwatch2/sql/metric_store/timescale/change_chunk_interval.sql: -------------------------------------------------------------------------------- 1 | -- DROP FUNCTION IF EXISTS admin.timescale_change_chunk_interval(interval); 2 | -- select * from admin.timescale_change_chunk_interval('1 day'); 3 | 4 | CREATE OR REPLACE FUNCTION admin.timescale_change_chunk_interval( 5 | new_interval interval 6 | ) 7 | RETURNS void AS 8 | /* 9 | changes all existing tables and writes the new default also into the admin.config table 10 | so that future new metric hypertables would also automatically use it 11 | */ 12 | $SQL$ 13 | DECLARE 14 | r record; 15 | BEGIN 16 | 17 | INSERT INTO admin.config 18 | SELECT 'timescale_chunk_interval', new_interval::text 19 | ON CONFLICT (key) DO UPDATE 20 | SET value = new_interval::text; 21 | 22 | FOR r IN (SELECT quote_ident(table_name) as metric 23 | FROM _timescaledb_catalog.hypertable 24 | WHERE schema_name = 'public') 25 | LOOP 26 | -- RAISE NOTICE 'setting % to %s ...', r.metric, new_interval; 27 | PERFORM set_chunk_time_interval(r.metric, new_interval); 28 | END LOOP; 29 | 30 | END; 31 | $SQL$ LANGUAGE plpgsql; 32 | 33 | GRANT EXECUTE ON FUNCTION admin.timescale_change_chunk_interval(interval) TO pgwatch2; 34 | -------------------------------------------------------------------------------- /pgwatch2/metrics/db_stats/10/metric_su.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | numbackends, 4 | xact_commit, 5 | xact_rollback, 6 | blks_read, 7 | blks_hit, 8 | tup_returned, 9 | tup_fetched, 10 | tup_inserted, 11 | tup_updated, 12 | tup_deleted, 13 | conflicts, 14 | temp_files, 15 | temp_bytes, 16 | deadlocks, 17 | blk_read_time, 18 | blk_write_time, 19 | extract(epoch from (now() - coalesce((pg_stat_file('postmaster.pid', true)).modification, pg_postmaster_start_time())))::int8 as postmaster_uptime_s, 20 | extract(epoch from (now() - pg_backup_start_time()))::int8 as backup_duration_s, 21 | case when pg_is_in_recovery() then 1 else 0 end as in_recovery_int, 22 | system_identifier::text as tag_sys_id, 23 | (select count(*) from pg_index i 24 | where not indisvalid 25 | and not exists ( /* leave out ones that are being actively rebuilt */ 26 | select * from pg_locks l 27 | join pg_stat_activity a using (pid) 28 | where l.relation = i.indexrelid 29 | and a.state = 'active' 30 | and a.query ~* 'concurrently' 31 | )) as invalid_indexes 32 | from 33 | pg_stat_database, pg_control_system() 34 | where 35 | datname = current_database(); 36 | -------------------------------------------------------------------------------- /webpy/crypto.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import os 3 | from binascii import hexlify, unhexlify 4 | from cryptography.hazmat.primitives.ciphers.aead import AESGCM 5 | 6 | # https://gist.github.com/tscholl2/dc7dc15dc132ea70a98e8542fefffa28 7 | 8 | def deriveKey(passphrase: str, salt: bytes=None) -> [str, bytes]: 9 | if salt is None: 10 | salt = os.urandom(8) 11 | return hashlib.pbkdf2_hmac("sha256", passphrase.encode("utf8"), salt, 1000), salt 12 | 13 | 14 | def encrypt(passphrase: str, plaintext: str) -> str: 15 | key, salt = deriveKey(passphrase) 16 | aes = AESGCM(key) 17 | iv = os.urandom(12) 18 | plaintext = plaintext.encode("utf8") 19 | ciphertext = aes.encrypt(iv, plaintext, None) 20 | return "%s-%s-%s" % (hexlify(salt).decode("utf8"), hexlify(iv).decode("utf8"), hexlify(ciphertext).decode("utf8")) 21 | 22 | 23 | def decrypt(passphrase: str, ciphertext: str) -> str: 24 | salt, iv, ciphertext = map(unhexlify, ciphertext.split("-")) 25 | key, _ = deriveKey(passphrase, salt) 26 | aes = AESGCM(key) 27 | plaintext = aes.decrypt(iv, ciphertext, None) 28 | return plaintext.decode("utf8") 29 | 30 | 31 | if __name__ == "__main__": 32 | ciphertext = encrypt("mysecretkey", "postgres") 33 | print(ciphertext) 34 | print(decrypt("mysecretkey", ciphertext)) 35 | -------------------------------------------------------------------------------- /pgwatch2/metrics/db_stats/12/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | numbackends, 4 | xact_commit, 5 | xact_rollback, 6 | blks_read, 7 | blks_hit, 8 | tup_returned, 9 | tup_fetched, 10 | tup_inserted, 11 | tup_updated, 12 | tup_deleted, 13 | conflicts, 14 | temp_files, 15 | temp_bytes, 16 | deadlocks, 17 | blk_read_time, 18 | blk_write_time, 19 | extract(epoch from (now() - pg_postmaster_start_time()))::int8 as postmaster_uptime_s, 20 | extract(epoch from (now() - pg_backup_start_time()))::int8 as backup_duration_s, 21 | checksum_failures, 22 | extract(epoch from (now() - checksum_last_failure))::int8 as checksum_last_failure_s, 23 | case when pg_is_in_recovery() then 1 else 0 end as in_recovery_int, 24 | system_identifier::text as tag_sys_id, 25 | (select count(*) from pg_index i 26 | where not indisvalid 27 | and not exists ( /* leave out ones that are being actively rebuilt */ 28 | select * from pg_locks l 29 | join pg_stat_activity a using (pid) 30 | where l.relation = i.indexrelid 31 | and a.state = 'active' 32 | and a.query ~* 'concurrently' 33 | )) as invalid_indexes 34 | from 35 | pg_stat_database, pg_control_system() 36 | where 37 | datname = current_database(); 38 | -------------------------------------------------------------------------------- /pgwatch2/metrics/table_bloat_approx_summary/10/metric.sql: -------------------------------------------------------------------------------- 1 | /* NB! accessing pgstattuple_approx directly requires superuser or pg_stat_scan_tables/pg_monitor builtin roles or 2 | execute grant on pgstattuple_approx(regclass) 3 | */ 4 | with table_bloat_approx as ( 5 | select 6 | avg(approx_free_percent)::double precision as approx_free_percent, 7 | sum(approx_free_space)::double precision as approx_free_space, 8 | avg(dead_tuple_percent)::double precision as dead_tuple_percent, 9 | sum(dead_tuple_len)::double precision as dead_tuple_len 10 | from 11 | pg_class c 12 | join 13 | pg_namespace n on n.oid = c.relnamespace 14 | join lateral pgstattuple_approx(c.oid) on (c.oid not in (select relation from pg_locks where mode = 'AccessExclusiveLock')) -- skip locked tables 15 | where 16 | relkind in ('r', 'm') 17 | and c.relpages >= 128 -- tables >1mb 18 | and not n.nspname != 'information_schema' 19 | ) 20 | select /* pgwatch2_generated */ 21 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 22 | approx_free_percent, 23 | approx_free_space as approx_free_space_b, 24 | dead_tuple_percent, 25 | dead_tuple_len as dead_tuple_len_b 26 | from 27 | table_bloat_approx 28 | where 29 | approx_free_space > 0; 30 | -------------------------------------------------------------------------------- /webpy/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 | 9 | 10 |
11 | 12 | 26 | {{message}} 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /webpy/utils.py: -------------------------------------------------------------------------------- 1 | 2 | def makePrettySize(size): # copied from https://github.com/zalando/PGObserver 3 | """ mimics pg_size_pretty() """ 4 | sign = '-' if size < 0 else '' 5 | size = abs(size) 6 | if size <= 1024: 7 | return sign + str(size) + ' B' 8 | if size < 10 * 1024**2: 9 | return sign + str(int(round(size / float(1024)))) + ' kB' 10 | if size < 10 * 1024**3: 11 | return sign + str(int(round(size / float(1024**2)))) + ' MB' 12 | if size < 10 * 1024**4: 13 | return sign + str(int(round(size / float(1024**3)))) + ' GB' 14 | return sign + str(int(round(size / float(1024**4)))) + ' TB' 15 | 16 | 17 | def makePrettyCounter(count): 18 | sign = '-' if count < 0 else '' 19 | count = abs(count) 20 | if count <= 1000: 21 | return sign + str(count) 22 | if count < 1000**2: 23 | return sign + str(round(count / float(1000), 1)) + ' K' 24 | if count < 1000**3: 25 | return sign + str(round(count / float(1000**2), 1)) + ' M' 26 | return sign + str(round(count / float(1000**3), 1)) + ' B' 27 | 28 | def fileContentsToString(filePath): 29 | with open(filePath) as f: 30 | return f.read() 31 | 32 | 33 | if __name__ == '__main__': 34 | print(makePrettySize(2.2 * 1e9)) 35 | print(makePrettyCounter(2.2 * 1e9)) 36 | print(fileContentsToString(__file__)) 37 | 38 | -------------------------------------------------------------------------------- /pgwatch2/metrics/db_stats/12/metric_su.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | numbackends, 4 | xact_commit, 5 | xact_rollback, 6 | blks_read, 7 | blks_hit, 8 | tup_returned, 9 | tup_fetched, 10 | tup_inserted, 11 | tup_updated, 12 | tup_deleted, 13 | conflicts, 14 | temp_files, 15 | temp_bytes, 16 | deadlocks, 17 | blk_read_time, 18 | blk_write_time, 19 | extract(epoch from (now() - coalesce((pg_stat_file('postmaster.pid', true)).modification, pg_postmaster_start_time())))::int8 as postmaster_uptime_s, 20 | extract(epoch from (now() - pg_backup_start_time()))::int8 as backup_duration_s, 21 | checksum_failures, 22 | extract(epoch from (now() - checksum_last_failure))::int8 as checksum_last_failure_s, 23 | case when pg_is_in_recovery() then 1 else 0 end as in_recovery_int, 24 | system_identifier::text as tag_sys_id, 25 | (select count(*) from pg_index i 26 | where not indisvalid 27 | and not exists ( /* leave out ones that are being actively rebuilt */ 28 | select * from pg_locks l 29 | join pg_stat_activity a using (pid) 30 | where l.relation = i.indexrelid 31 | and a.state = 'active' 32 | and a.query ~* 'concurrently' 33 | )) as invalid_indexes 34 | from 35 | pg_stat_database, pg_control_system() 36 | where 37 | datname = current_database(); 38 | -------------------------------------------------------------------------------- /pgwatch2/metrics/db_stats/15/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | numbackends, 4 | xact_commit, 5 | xact_rollback, 6 | blks_read, 7 | blks_hit, 8 | tup_returned, 9 | tup_fetched, 10 | tup_inserted, 11 | tup_updated, 12 | tup_deleted, 13 | conflicts, 14 | temp_files, 15 | temp_bytes, 16 | deadlocks, 17 | blk_read_time, 18 | blk_write_time, 19 | extract(epoch from (now() - pg_postmaster_start_time()))::int8 as postmaster_uptime_s, 20 | checksum_failures, 21 | extract(epoch from (now() - checksum_last_failure))::int8 as checksum_last_failure_s, 22 | case when pg_is_in_recovery() then 1 else 0 end as in_recovery_int, 23 | system_identifier::text as tag_sys_id, 24 | session_time::int8, 25 | active_time::int8, 26 | idle_in_transaction_time::int8, 27 | sessions, 28 | sessions_abandoned, 29 | sessions_fatal, 30 | sessions_killed, 31 | (select count(*) from pg_index i 32 | where not indisvalid 33 | and not exists ( /* leave out ones that are being actively rebuilt */ 34 | select * from pg_locks l 35 | join pg_stat_activity a using (pid) 36 | where l.relation = i.indexrelid 37 | and a.state = 'active' 38 | and a.query ~* 'concurrently' 39 | )) as invalid_indexes 40 | from 41 | pg_stat_database, pg_control_system() 42 | where 43 | datname = current_database(); 44 | -------------------------------------------------------------------------------- /pgwatch2/sql/metric_store/metric/ensure_partition.sql: -------------------------------------------------------------------------------- 1 | -- DROP FUNCTION IF EXISTS public.ensure_partition_metric(text); 2 | -- select * from public.ensure_partition_metric('wal'); 3 | 4 | CREATE OR REPLACE FUNCTION admin.ensure_partition_metric( 5 | metric text 6 | ) 7 | RETURNS void AS 8 | /* 9 | creates a top level metric table if not already existing. 10 | expects the "metrics_template" table to exist. 11 | */ 12 | $SQL$ 13 | DECLARE 14 | l_template_table text := 'admin.metrics_template'; 15 | l_unlogged text := ''; 16 | BEGIN 17 | 18 | PERFORM pg_advisory_xact_lock(regexp_replace( md5(metric) , E'\\D', '', 'g')::varchar(10)::int8); 19 | 20 | IF NOT EXISTS (SELECT 1 21 | FROM pg_tables 22 | WHERE tablename = metric 23 | AND schemaname = 'public') 24 | THEN 25 | --RAISE NOTICE 'creating partition % ...', metric; 26 | IF metric ~ 'realtime' THEN 27 | l_template_table := 'admin.metrics_template_realtime'; 28 | l_unlogged := 'UNLOGGED'; 29 | END IF; 30 | EXECUTE format($$CREATE %s TABLE IF NOT EXISTS public.%s (LIKE %s INCLUDING INDEXES)$$, l_unlogged, quote_ident(metric), l_template_table); 31 | EXECUTE format($$COMMENT ON TABLE public.%s IS 'pgwatch2-generated-metric-lvl'$$, quote_ident(metric)); 32 | END IF; 33 | 34 | END; 35 | $SQL$ LANGUAGE plpgsql; 36 | 37 | GRANT EXECUTE ON FUNCTION admin.ensure_partition_metric(text) TO pgwatch2; 38 | -------------------------------------------------------------------------------- /pgwatch2/metrics/kpi/9.0/metric.sql: -------------------------------------------------------------------------------- 1 | WITH q_stat_tables AS ( 2 | SELECT * FROM pg_stat_user_tables t 3 | JOIN pg_class c ON c.oid = t.relid 4 | WHERE NOT schemaname LIKE E'pg\\_temp%' 5 | AND c.relpages > (1e7 / 8) -- >10MB 6 | ), 7 | q_stat_activity AS ( 8 | SELECT * FROM get_stat_activity() 9 | ) 10 | select /* pgwatch2_generated */ 11 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 12 | numbackends - 1 as numbackends, 13 | (select count(*) from q_stat_activity where not current_query in ('', ' in transaction')) AS active_backends, 14 | (select count(*) from q_stat_activity where waiting) AS blocked_backends, 15 | (select round(extract(epoch from now()) - extract(epoch from (select xact_start from q_stat_activity 16 | where datid = d.datid and not current_query like 'autovacuum:%' order by xact_start limit 1))))::int AS kpi_oldest_tx_s, 17 | xact_commit + xact_rollback AS tps, 18 | xact_commit, 19 | xact_rollback, 20 | blks_read, 21 | blks_hit, 22 | (select sum(seq_scan) from q_stat_tables)::int8 AS seq_scans_on_tbls_gt_10mb, 23 | tup_inserted, 24 | tup_updated, 25 | tup_deleted, 26 | (select sum(calls) from pg_stat_user_functions where not schemaname like any(array[E'pg\\_%', 'information_schema']))::int8 AS sproc_calls, 27 | extract(epoch from (now() - pg_postmaster_start_time()))::int8 as postmaster_uptime_s 28 | FROM 29 | pg_stat_database d 30 | WHERE 31 | datname = current_database(); 32 | -------------------------------------------------------------------------------- /pgwatch2/metrics/db_stats/15/metric_su.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | numbackends, 4 | xact_commit, 5 | xact_rollback, 6 | blks_read, 7 | blks_hit, 8 | tup_returned, 9 | tup_fetched, 10 | tup_inserted, 11 | tup_updated, 12 | tup_deleted, 13 | conflicts, 14 | temp_files, 15 | temp_bytes, 16 | deadlocks, 17 | blk_read_time, 18 | blk_write_time, 19 | extract(epoch from (now() - coalesce((pg_stat_file('postmaster.pid', true)).modification, pg_postmaster_start_time())))::int8 as postmaster_uptime_s, 20 | checksum_failures, 21 | extract(epoch from (now() - checksum_last_failure))::int8 as checksum_last_failure_s, 22 | case when pg_is_in_recovery() then 1 else 0 end as in_recovery_int, 23 | system_identifier::text as tag_sys_id, 24 | session_time::int8, 25 | active_time::int8, 26 | idle_in_transaction_time::int8, 27 | sessions, 28 | sessions_abandoned, 29 | sessions_fatal, 30 | sessions_killed, 31 | (select count(*) from pg_index i 32 | where not indisvalid 33 | and not exists ( /* leave out ones that are being actively rebuilt */ 34 | select * from pg_locks l 35 | join pg_stat_activity a using (pid) 36 | where l.relation = i.indexrelid 37 | and a.state = 'active' 38 | and a.query ~* 'concurrently' 39 | )) as invalid_indexes 40 | from 41 | pg_stat_database, pg_control_system() 42 | where 43 | datname = current_database(); 44 | -------------------------------------------------------------------------------- /pgwatch2/metrics/db_stats/14/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | numbackends, 4 | xact_commit, 5 | xact_rollback, 6 | blks_read, 7 | blks_hit, 8 | tup_returned, 9 | tup_fetched, 10 | tup_inserted, 11 | tup_updated, 12 | tup_deleted, 13 | conflicts, 14 | temp_files, 15 | temp_bytes, 16 | deadlocks, 17 | blk_read_time, 18 | blk_write_time, 19 | extract(epoch from (now() - pg_postmaster_start_time()))::int8 as postmaster_uptime_s, 20 | extract(epoch from (now() - pg_backup_start_time()))::int8 as backup_duration_s, 21 | checksum_failures, 22 | extract(epoch from (now() - checksum_last_failure))::int8 as checksum_last_failure_s, 23 | case when pg_is_in_recovery() then 1 else 0 end as in_recovery_int, 24 | system_identifier::text as tag_sys_id, 25 | session_time::int8, 26 | active_time::int8, 27 | idle_in_transaction_time::int8, 28 | sessions, 29 | sessions_abandoned, 30 | sessions_fatal, 31 | sessions_killed, 32 | (select count(*) from pg_index i 33 | where not indisvalid 34 | and not exists ( /* leave out ones that are being actively rebuilt */ 35 | select * from pg_locks l 36 | join pg_stat_activity a using (pid) 37 | where l.relation = i.indexrelid 38 | and a.state = 'active' 39 | and a.query ~* 'concurrently' 40 | )) as invalid_indexes 41 | from 42 | pg_stat_database, pg_control_system() 43 | where 44 | datname = current_database(); 45 | -------------------------------------------------------------------------------- /pgwatch2/metrics/reco_partial_index_candidates/9.0/metric.sql: -------------------------------------------------------------------------------- 1 | select distinct /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 3 | 'partial_index_candidates'::text as tag_reco_topic, 4 | quote_ident(ni.nspname)||'.'||quote_ident(ci.relname) as tag_object_name, 5 | ('index ' || quote_ident(ni.nspname)||'.'||quote_ident(ci.relname) || ' on ' || quote_ident(s.schemaname) || '.' || quote_ident(s.tablename) || ' column ' || quote_ident(s.attname) || ' could possibly be declared partial leaving out NULL-s')::text as recommendation, 6 | 'NULL fraction: ' || round((null_frac * 100)::numeric, 1) || '%, rowcount estimate: ' || (c.reltuples)::int8 || ', current definition: ' || pg_get_indexdef(i.indexrelid) as extra_info 7 | from 8 | pg_stats s 9 | join pg_attribute a using (attname) 10 | join pg_index i on i.indkey[0] = a.attnum and i.indrelid = a.attrelid 11 | join pg_class c on c.oid = i.indrelid 12 | join pg_class ci on ci.oid = i.indexrelid 13 | join pg_namespace ni on ni.oid = ci.relnamespace 14 | where 15 | not indisprimary 16 | and not indisunique 17 | and indisready 18 | and indisvalid 19 | and i.indnatts = 1 /* simple 1 column indexes */ 20 | and null_frac > 0.5 /* 50% empty */ 21 | and not pg_get_indexdef(i.indexrelid) like '% WHERE %' 22 | and c.reltuples >= 1e5 /* ignore smaller tables */ 23 | and not exists ( /* leave out sub-partitions */ 24 | select * from pg_inherits where inhrelid = c.oid 25 | ) 26 | ; 27 | -------------------------------------------------------------------------------- /pgwatch2/metrics/blocking_locks/9.2/metric.sql: -------------------------------------------------------------------------------- 1 | select /* pgwatch2_generated */ 2 | (extract(epoch from now()) * 1e9)::int8 AS epoch_ns, 3 | waiting.locktype AS tag_waiting_locktype, 4 | waiting_stm.usename::text AS tag_waiting_user, 5 | coalesce(waiting.mode, 'null'::text) AS tag_waiting_mode, 6 | coalesce(waiting.relation::regclass::text, 'null') AS tag_waiting_table, 7 | waiting_stm.query AS waiting_query, 8 | waiting.pid AS waiting_pid, 9 | other.locktype AS other_locktype, 10 | other.relation::regclass::text AS other_table, 11 | other_stm.query AS other_query, 12 | other.mode AS other_mode, 13 | other.pid AS other_pid, 14 | other_stm.usename::text AS other_user 15 | FROM 16 | pg_catalog.pg_locks AS waiting 17 | JOIN 18 | get_stat_activity() AS waiting_stm 19 | ON ( 20 | waiting_stm.pid = waiting.pid 21 | ) 22 | JOIN 23 | pg_catalog.pg_locks AS other 24 | ON ( 25 | ( 26 | waiting."database" = other."database" 27 | AND waiting.relation = other.relation 28 | ) 29 | OR waiting.transactionid = other.transactionid 30 | ) 31 | JOIN 32 | get_stat_activity() AS other_stm 33 | ON ( 34 | other_stm.pid = other.pid 35 | ) 36 | WHERE 37 | NOT waiting.GRANTED 38 | AND 39 | waiting.pid <> other.pid 40 | AND 41 | other.GRANTED 42 | AND 43 | waiting_stm.datname = current_database(); 44 | -------------------------------------------------------------------------------- /pgwatch2/metrics/kpi/9.0/metric_su.sql: -------------------------------------------------------------------------------- 1 | WITH q_stat_tables AS ( 2 | SELECT * FROM pg_stat_user_tables t 3 | JOIN pg_class c ON c.oid = t.relid 4 | WHERE NOT schemaname LIKE E'pg\\_temp%' 5 | AND c.relpages > (1e7 / 8) -- >10MB 6 | ), 7 | q_stat_activity AS ( 8 | SELECT * FROM pg_stat_activity WHERE procpid != pg_backend_pid() AND datname = current_database() 9 | ) 10 | select /* pgwatch2_generated */ 11 | (extract(epoch from now()) * 1e9)::int8 as epoch_ns, 12 | numbackends - 1 as numbackends, 13 | (select count(*) from q_stat_activity where not current_query in ('', ' in transaction')) AS active_backends, 14 | (select count(*) from q_stat_activity where waiting) AS blocked_backends, 15 | (select round(extract(epoch from now()) - extract(epoch from (select xact_start from q_stat_activity 16 | where datid = d.datid and not current_query like 'autovacuum:%' order by xact_start limit 1))))::int AS kpi_oldest_tx_s, 17 | xact_commit + xact_rollback AS tps, 18 | xact_commit, 19 | xact_rollback, 20 | blks_read, 21 | blks_hit, 22 | (select sum(seq_scan) from q_stat_tables)::int8 AS seq_scans_on_tbls_gt_10mb, 23 | tup_inserted, 24 | tup_updated, 25 | tup_deleted, 26 | (select sum(calls) from pg_stat_user_functions where not schemaname like any(array[E'pg\\_%', 'information_schema']))::int8 AS sproc_calls, 27 | extract(epoch from (now() - pg_postmaster_start_time()))::int8 as postmaster_uptime_s 28 | FROM 29 | pg_stat_database d 30 | WHERE 31 | datname = current_database(); 32 | --------------------------------------------------------------------------------