From c41e3adcc60ac7172dffbb2b9e868839202c47e2 Mon Sep 17 00:00:00 2001 From: Alexander Petric Date: Tue, 31 Mar 2026 01:55:38 -0400 Subject: [PATCH] db health page improvements (#8631) * db health page improvements * Update SQLx metadata --------- Co-authored-by: windmill-internal-app[bot] --- ...d6b6792ae5b97ed90b0ee07d17480b2dffe2.json} | 6 +- ...8137e4173a34c54344ee522745640d48b8813.json | 32 ++++++ ...b2e890bc4146bb0419210cd76743cacda8bfa.json | 44 +++++++++ ...153c43903f929ae5d62fbba12610f89c36d55.json | 2 +- ...3c5798e6f7aca1e6e564330766d77038ef0e3.json | 44 --------- backend/windmill-api/src/db_health.rs | 97 +++++++++++++------ .../instanceSettings/DbHealth.svelte | 18 ++-- 7 files changed, 160 insertions(+), 83 deletions(-) rename backend/.sqlx/{query-87d07998fe8373f5b89eccf6f0528c02e389bf827d935d867430ad3459104dd9.json => query-07770a002a49428c4f956cfc7262d6b6792ae5b97ed90b0ee07d17480b2dffe2.json} (52%) create mode 100644 backend/.sqlx/query-2d95191e899d60385b32f36f2e38137e4173a34c54344ee522745640d48b8813.json create mode 100644 backend/.sqlx/query-30930bfb0513f1a70194a900011b2e890bc4146bb0419210cd76743cacda8bfa.json delete mode 100644 backend/.sqlx/query-bc54ea311580a0525c1f36aaa543c5798e6f7aca1e6e564330766d77038ef0e3.json diff --git a/backend/.sqlx/query-87d07998fe8373f5b89eccf6f0528c02e389bf827d935d867430ad3459104dd9.json b/backend/.sqlx/query-07770a002a49428c4f956cfc7262d6b6792ae5b97ed90b0ee07d17480b2dffe2.json similarity index 52% rename from backend/.sqlx/query-87d07998fe8373f5b89eccf6f0528c02e389bf827d935d867430ad3459104dd9.json rename to backend/.sqlx/query-07770a002a49428c4f956cfc7262d6b6792ae5b97ed90b0ee07d17480b2dffe2.json index 24d15601e7..145fdc1229 100644 --- a/backend/.sqlx/query-87d07998fe8373f5b89eccf6f0528c02e389bf827d935d867430ad3459104dd9.json +++ b/backend/.sqlx/query-07770a002a49428c4f956cfc7262d6b6792ae5b97ed90b0ee07d17480b2dffe2.json @@ -1,11 +1,11 @@ { "db_name": "PostgreSQL", - "query": "SELECT COUNT(*) as cnt FROM pg_stat_activity WHERE state = 'active'", + "query": "SELECT setting::bigint as \"max!\" FROM pg_settings WHERE name = 'max_connections'", "describe": { "columns": [ { "ordinal": 0, - "name": "cnt", + "name": "max!", "type_info": "Int8" } ], @@ -16,5 +16,5 @@ null ] }, - "hash": "87d07998fe8373f5b89eccf6f0528c02e389bf827d935d867430ad3459104dd9" + "hash": "07770a002a49428c4f956cfc7262d6b6792ae5b97ed90b0ee07d17480b2dffe2" } diff --git a/backend/.sqlx/query-2d95191e899d60385b32f36f2e38137e4173a34c54344ee522745640d48b8813.json b/backend/.sqlx/query-2d95191e899d60385b32f36f2e38137e4173a34c54344ee522745640d48b8813.json new file mode 100644 index 0000000000..d31420ada0 --- /dev/null +++ b/backend/.sqlx/query-2d95191e899d60385b32f36f2e38137e4173a34c54344ee522745640d48b8813.json @@ -0,0 +1,32 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT\n COUNT(*) as \"total!\",\n COUNT(*) FILTER (WHERE state = 'active') as \"active!\",\n COUNT(*) FILTER (WHERE state = 'idle') as \"idle!\"\n FROM pg_stat_activity\n WHERE backend_type = 'client backend'", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "total!", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "active!", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "idle!", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null, + null, + null + ] + }, + "hash": "2d95191e899d60385b32f36f2e38137e4173a34c54344ee522745640d48b8813" +} diff --git a/backend/.sqlx/query-30930bfb0513f1a70194a900011b2e890bc4146bb0419210cd76743cacda8bfa.json b/backend/.sqlx/query-30930bfb0513f1a70194a900011b2e890bc4146bb0419210cd76743cacda8bfa.json new file mode 100644 index 0000000000..ca31cbc9ca --- /dev/null +++ b/backend/.sqlx/query-30930bfb0513f1a70194a900011b2e890bc4146bb0419210cd76743cacda8bfa.json @@ -0,0 +1,44 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT\n table_name as \"table_name!\",\n SUM(live_tuples)::bigint as \"live_tuples!\",\n SUM(dead_tuples)::bigint as \"dead_tuples!\",\n MAX(last_autovacuum) as \"last_autovacuum\",\n MAX(last_autoanalyze) as \"last_autoanalyze\"\n FROM (\n SELECT\n CASE\n WHEN i.inhparent IS NOT NULL THEN schemaname || '.' || p.relname\n ELSE schemaname || '.' || s.relname\n END as table_name,\n COALESCE(n_live_tup, 0) as live_tuples,\n COALESCE(n_dead_tup, 0) as dead_tuples,\n last_autovacuum,\n last_autoanalyze\n FROM pg_stat_user_tables s\n LEFT JOIN pg_class c ON c.relname = s.relname AND c.relnamespace = (\n SELECT oid FROM pg_namespace WHERE nspname = s.schemaname\n )\n LEFT JOIN pg_inherits i ON i.inhrelid = c.oid\n LEFT JOIN pg_class p ON p.oid = i.inhparent\n ) sub\n GROUP BY table_name\n ORDER BY SUM(dead_tuples) DESC", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "table_name!", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "live_tuples!", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "dead_tuples!", + "type_info": "Int8" + }, + { + "ordinal": 3, + "name": "last_autovacuum", + "type_info": "Timestamptz" + }, + { + "ordinal": 4, + "name": "last_autoanalyze", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null, + null, + null, + null, + null + ] + }, + "hash": "30930bfb0513f1a70194a900011b2e890bc4146bb0419210cd76743cacda8bfa" +} diff --git a/backend/.sqlx/query-5a219a2532517869578c4504ff3153c43903f929ae5d62fbba12610f89c36d55.json b/backend/.sqlx/query-5a219a2532517869578c4504ff3153c43903f929ae5d62fbba12610f89c36d55.json index 36ddb8ab9f..713ccb9dd3 100644 --- a/backend/.sqlx/query-5a219a2532517869578c4504ff3153c43903f929ae5d62fbba12610f89c36d55.json +++ b/backend/.sqlx/query-5a219a2532517869578c4504ff3153c43903f929ae5d62fbba12610f89c36d55.json @@ -15,7 +15,7 @@ ] }, "nullable": [ - true + null ] }, "hash": "5a219a2532517869578c4504ff3153c43903f929ae5d62fbba12610f89c36d55" diff --git a/backend/.sqlx/query-bc54ea311580a0525c1f36aaa543c5798e6f7aca1e6e564330766d77038ef0e3.json b/backend/.sqlx/query-bc54ea311580a0525c1f36aaa543c5798e6f7aca1e6e564330766d77038ef0e3.json deleted file mode 100644 index c68971b7ba..0000000000 --- a/backend/.sqlx/query-bc54ea311580a0525c1f36aaa543c5798e6f7aca1e6e564330766d77038ef0e3.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT\n schemaname || '.' || relname as \"table_name!\",\n COALESCE(n_live_tup, 0) as \"live_tuples!\",\n COALESCE(n_dead_tup, 0) as \"dead_tuples!\",\n last_autovacuum as \"last_autovacuum\",\n last_autoanalyze as \"last_autoanalyze\"\n FROM pg_stat_user_tables\n ORDER BY n_dead_tup DESC\n LIMIT 15", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "table_name!", - "type_info": "Text" - }, - { - "ordinal": 1, - "name": "live_tuples!", - "type_info": "Int8" - }, - { - "ordinal": 2, - "name": "dead_tuples!", - "type_info": "Int8" - }, - { - "ordinal": 3, - "name": "last_autovacuum", - "type_info": "Timestamptz" - }, - { - "ordinal": 4, - "name": "last_autoanalyze", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - null, - null, - null, - true, - true - ] - }, - "hash": "bc54ea311580a0525c1f36aaa543c5798e6f7aca1e6e564330766d77038ef0e3" -} diff --git a/backend/windmill-api/src/db_health.rs b/backend/windmill-api/src/db_health.rs index 91c8013950..5b881331c0 100644 --- a/backend/windmill-api/src/db_health.rs +++ b/backend/windmill-api/src/db_health.rs @@ -12,8 +12,6 @@ use serde::{Deserialize, Serialize}; use windmill_common::error::JsonResult; use crate::db::{ApiAuthed, DB}; -use crate::health::get_pool_stats; -use crate::health::PoolStats; use crate::utils::require_super_admin; pub fn global_service() -> Router { @@ -81,8 +79,10 @@ pub struct LargeResultRow { #[derive(Serialize)] pub struct ConnectionPoolInfo { - pub pool: PoolStats, + pub pg_max_connections: i64, + pub pg_total_connections: i64, pub pg_active_connections: i64, + pub pg_idle_connections: i64, pub status: HealthLevel, pub message: String, } @@ -307,16 +307,30 @@ async fn fetch_large_results( } async fn fetch_connection_pool(db: &DB) -> windmill_common::error::Result { - let pool = get_pool_stats(db); + let max_row = sqlx::query_scalar!( + r#"SELECT setting::bigint as "max!" FROM pg_settings WHERE name = 'max_connections'"# + ) + .fetch_one(db) + .await?; - let active_row = - sqlx::query!("SELECT COUNT(*) as cnt FROM pg_stat_activity WHERE state = 'active'") - .fetch_one(db) - .await?; + let stats_row = sqlx::query!( + r#"SELECT + COUNT(*) as "total!", + COUNT(*) FILTER (WHERE state = 'active') as "active!", + COUNT(*) FILTER (WHERE state = 'idle') as "idle!" + FROM pg_stat_activity + WHERE backend_type = 'client backend'"# + ) + .fetch_one(db) + .await?; - let pg_active = active_row.cnt.unwrap_or(0); - let utilization = if pool.max_connections > 0 { - pool.size as f64 / pool.max_connections as f64 + let pg_max = max_row; + let pg_total = stats_row.total; + let pg_active = stats_row.active; + let pg_idle = stats_row.idle; + + let utilization = if pg_max > 0 { + pg_total as f64 / pg_max as f64 } else { 0.0 }; @@ -325,50 +339,75 @@ async fn fetch_connection_pool(db: &DB) -> windmill_common::error::Result windmill_common::error::Result> { + // Aggregate partitioned tables (e.g. audit_YYYYMMDD -> audit_partitioned) + // while keeping non-partitioned tables as-is let rows = sqlx::query!( r#"SELECT - schemaname || '.' || relname as "table_name!", - COALESCE(n_live_tup, 0) as "live_tuples!", - COALESCE(n_dead_tup, 0) as "dead_tuples!", - last_autovacuum as "last_autovacuum", - last_autoanalyze as "last_autoanalyze" - FROM pg_stat_user_tables - ORDER BY n_dead_tup DESC - LIMIT 15"# + table_name as "table_name!", + SUM(live_tuples)::bigint as "live_tuples!", + SUM(dead_tuples)::bigint as "dead_tuples!", + MAX(last_autovacuum) as "last_autovacuum", + MAX(last_autoanalyze) as "last_autoanalyze" + FROM ( + SELECT + CASE + WHEN i.inhparent IS NOT NULL THEN schemaname || '.' || p.relname + ELSE schemaname || '.' || s.relname + END as table_name, + COALESCE(n_live_tup, 0) as live_tuples, + COALESCE(n_dead_tup, 0) as dead_tuples, + last_autovacuum, + last_autoanalyze + FROM pg_stat_user_tables s + LEFT JOIN pg_class c ON c.relname = s.relname AND c.relnamespace = ( + SELECT oid FROM pg_namespace WHERE nspname = s.schemaname + ) + LEFT JOIN pg_inherits i ON i.inhrelid = c.oid + LEFT JOIN pg_class p ON p.oid = i.inhparent + ) sub + GROUP BY table_name + ORDER BY SUM(dead_tuples) DESC"# ) .fetch_all(db) .await?; diff --git a/frontend/src/lib/components/instanceSettings/DbHealth.svelte b/frontend/src/lib/components/instanceSettings/DbHealth.svelte index 1986e78a9c..9614422552 100644 --- a/frontend/src/lib/components/instanceSettings/DbHealth.svelte +++ b/frontend/src/lib/components/instanceSettings/DbHealth.svelte @@ -267,7 +267,13 @@ {#each data.large_results.top_large_results as r} - {r.id.substring(0, 8)}... + {r.id.substring(0, 8)}... {r.workspace_id} {r.runnable_path ?? '-'} toggleSection('connection_pool')} > -

Connection Pool

+

Database Connections

- Pool size: {data.connection_pool.pool.size} / Max: - {data.connection_pool.pool.max_connections} - / Idle: {data.connection_pool.pool.idle} + Total connections: {data.connection_pool.pg_total_connections} / Max: + {data.connection_pool.pg_max_connections}

- Active PG connections: {data.connection_pool.pg_active_connections} + Active: {data.connection_pool.pg_active_connections} + / Idle: {data.connection_pool.pg_idle_connections}

{data.connection_pool.message}