Skip to content

Commit c74e75b

Browse files
committed
Merge branch 'feat/db-size' into 'main'
feat(reporter): add database size to H001, H002, H004, F004, F005 reports See merge request postgres-ai/postgres_ai!91
2 parents 183bb9a + b375551 commit c74e75b

File tree

2 files changed

+77
-7
lines changed

2 files changed

+77
-7
lines changed

reporter/postgres_reports.py

Lines changed: 76 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ def generate_a004_cluster_report(self, cluster: str = "local", node_name: str =
274274
'active_connections': f'sum(last_over_time(pgwatch_pg_stat_activity_count{{cluster="{cluster}", node_name="{node_name}", state="active"}}[3h]))',
275275
'idle_connections': f'sum(last_over_time(pgwatch_pg_stat_activity_count{{cluster="{cluster}", node_name="{node_name}", state="idle"}}[3h]))',
276276
'total_connections': f'sum(last_over_time(pgwatch_pg_stat_activity_count{{cluster="{cluster}", node_name="{node_name}"}}[3h]))',
277-
'database_sizes': f'sum(last_over_time(pgwatch_pg_database_size_bytes{{cluster="{cluster}", node_name="{node_name}"}}[3h]))',
277+
'database_sizes': f'sum(last_over_time(pgwatch_db_size_size_b{{cluster="{cluster}", node_name="{node_name}"}}[3h]))',
278278
'cache_hit_ratio': f'sum(last_over_time(pgwatch_db_stats_blks_hit{{cluster="{cluster}", node_name="{node_name}"}}[3h])) / clamp_min(sum(last_over_time(pgwatch_db_stats_blks_hit{{cluster="{cluster}", node_name="{node_name}"}}[3h])) + sum(last_over_time(pgwatch_db_stats_blks_read{{cluster="{cluster}", node_name="{node_name}"}}[3h])), 1) * 100',
279279
'transactions_per_sec': f'sum(rate(pgwatch_db_stats_xact_commit{{cluster="{cluster}", node_name="{node_name}"}}[5m])) + sum(rate(pgwatch_db_stats_xact_rollback{{cluster="{cluster}", node_name="{node_name}"}}[5m]))',
280280
'checkpoints_per_sec': f'sum(rate(pgwatch_pg_stat_bgwriter_checkpoints_timed{{cluster="{cluster}", node_name="{node_name}"}}[5m])) + sum(rate(pgwatch_pg_stat_bgwriter_checkpoints_req{{cluster="{cluster}", node_name="{node_name}"}}[5m]))',
@@ -297,7 +297,7 @@ def generate_a004_cluster_report(self, cluster: str = "local", node_name: str =
297297
}
298298

299299
# Get database sizes
300-
db_sizes_query = f'last_over_time(pgwatch_pg_database_size_bytes{{cluster="{cluster}", node_name="{node_name}"}}[3h])'
300+
db_sizes_query = f'last_over_time(pgwatch_db_size_size_b{{cluster="{cluster}", node_name="{node_name}"}}[3h])'
301301
db_sizes_result = self.query_instant(db_sizes_query)
302302
database_sizes = {}
303303

@@ -374,6 +374,17 @@ def generate_h001_invalid_indexes_report(self, cluster: str = "local", node_name
374374
# Get all databases
375375
databases = self.get_all_databases(cluster, node_name)
376376

377+
# Get database sizes
378+
db_sizes_query = f'last_over_time(pgwatch_db_size_size_b{{cluster="{cluster}", node_name="{node_name}"}}[3h])'
379+
db_sizes_result = self.query_instant(db_sizes_query)
380+
database_sizes = {}
381+
382+
if db_sizes_result.get('status') == 'success' and db_sizes_result.get('data', {}).get('result'):
383+
for result in db_sizes_result['data']['result']:
384+
db_name = result['metric'].get('datname', 'unknown')
385+
size_bytes = float(result['value'][1])
386+
database_sizes[db_name] = size_bytes
387+
377388
invalid_indexes_by_db = {}
378389
for db_name in databases:
379390
# Query invalid indexes for each database
@@ -408,11 +419,14 @@ def generate_h001_invalid_indexes_report(self, cluster: str = "local", node_name
408419
invalid_indexes.append(invalid_index)
409420
total_size += index_size_bytes
410421

422+
db_size_bytes = database_sizes.get(db_name, 0)
411423
invalid_indexes_by_db[db_name] = {
412424
"invalid_indexes": invalid_indexes,
413425
"total_count": len(invalid_indexes),
414426
"total_size_bytes": total_size,
415-
"total_size_pretty": self.format_bytes(total_size)
427+
"total_size_pretty": self.format_bytes(total_size),
428+
"database_size_bytes": db_size_bytes,
429+
"database_size_pretty": self.format_bytes(db_size_bytes)
416430
}
417431

418432
return self.format_report_data("H001", invalid_indexes_by_db, node_name)
@@ -433,6 +447,17 @@ def generate_h002_unused_indexes_report(self, cluster: str = "local", node_name:
433447
# Get all databases
434448
databases = self.get_all_databases(cluster, node_name)
435449

450+
# Get database sizes
451+
db_sizes_query = f'last_over_time(pgwatch_db_size_size_b{{cluster="{cluster}", node_name="{node_name}"}}[3h])'
452+
db_sizes_result = self.query_instant(db_sizes_query)
453+
database_sizes = {}
454+
455+
if db_sizes_result.get('status') == 'success' and db_sizes_result.get('data', {}).get('result'):
456+
for result in db_sizes_result['data']['result']:
457+
db_name = result['metric'].get('datname', 'unknown')
458+
size_bytes = float(result['value'][1])
459+
database_sizes[db_name] = size_bytes
460+
436461
# Query postmaster uptime to get startup time
437462
postmaster_uptime_query = f'last_over_time(pgwatch_db_stats_postmaster_uptime_s{{cluster="{cluster}", node_name="{node_name}"}}[3h])'
438463
postmaster_uptime_result = self.query_instant(postmaster_uptime_query)
@@ -509,11 +534,14 @@ def generate_h002_unused_indexes_report(self, cluster: str = "local", node_name:
509534

510535
total_unused_size = sum(idx['index_size_bytes'] for idx in unused_indexes)
511536

537+
db_size_bytes = database_sizes.get(db_name, 0)
512538
unused_indexes_by_db[db_name] = {
513539
"unused_indexes": unused_indexes,
514540
"total_count": len(unused_indexes),
515541
"total_size_bytes": total_unused_size,
516542
"total_size_pretty": self.format_bytes(total_unused_size),
543+
"database_size_bytes": db_size_bytes,
544+
"database_size_pretty": self.format_bytes(db_size_bytes),
517545
"stats_reset": {
518546
"stats_reset_epoch": stats_reset_epoch,
519547
"stats_reset_time": stats_reset_time,
@@ -542,6 +570,17 @@ def generate_h004_redundant_indexes_report(self, cluster: str = "local", node_na
542570
# Get all databases
543571
databases = self.get_all_databases(cluster, node_name)
544572

573+
# Get database sizes
574+
db_sizes_query = f'last_over_time(pgwatch_db_size_size_b{{cluster="{cluster}", node_name="{node_name}"}}[3h])'
575+
db_sizes_result = self.query_instant(db_sizes_query)
576+
database_sizes = {}
577+
578+
if db_sizes_result.get('status') == 'success' and db_sizes_result.get('data', {}).get('result'):
579+
for result in db_sizes_result['data']['result']:
580+
db_name = result['metric'].get('datname', 'unknown')
581+
size_bytes = float(result['value'][1])
582+
database_sizes[db_name] = size_bytes
583+
545584
redundant_indexes_by_db = {}
546585
for db_name in databases:
547586
# Fetch index definitions from the sink for this database (used to aid remediation)
@@ -606,11 +645,14 @@ def generate_h004_redundant_indexes_report(self, cluster: str = "local", node_na
606645
# Sort by index size descending
607646
redundant_indexes.sort(key=lambda x: x['index_size_bytes'], reverse=True)
608647

648+
db_size_bytes = database_sizes.get(db_name, 0)
609649
redundant_indexes_by_db[db_name] = {
610650
"redundant_indexes": redundant_indexes,
611651
"total_count": len(redundant_indexes),
612652
"total_size_bytes": total_size,
613-
"total_size_pretty": self.format_bytes(total_size)
653+
"total_size_pretty": self.format_bytes(total_size),
654+
"database_size_bytes": db_size_bytes,
655+
"database_size_pretty": self.format_bytes(db_size_bytes)
614656
}
615657

616658
return self.format_report_data("H004", redundant_indexes_by_db, node_name)
@@ -872,6 +914,17 @@ def generate_f005_btree_bloat_report(self, cluster: str = "local", node_name: st
872914
# Get all databases
873915
databases = self.get_all_databases(cluster, node_name)
874916

917+
# Get database sizes
918+
db_sizes_query = f'last_over_time(pgwatch_db_size_size_b{{cluster="{cluster}", node_name="{node_name}"}}[3h])'
919+
db_sizes_result = self.query_instant(db_sizes_query)
920+
database_sizes = {}
921+
922+
if db_sizes_result.get('status') == 'success' and db_sizes_result.get('data', {}).get('result'):
923+
for result in db_sizes_result['data']['result']:
924+
db_name = result['metric'].get('datname', 'unknown')
925+
size_bytes = float(result['value'][1])
926+
database_sizes[db_name] = size_bytes
927+
875928
bloated_indexes_by_db = {}
876929
for db_name in databases:
877930
# Query btree bloat using multiple metrics for each database with last_over_time [1d]
@@ -922,11 +975,14 @@ def generate_f005_btree_bloat_report(self, cluster: str = "local", node_name: st
922975
# Sort by bloat percentage descending
923976
bloated_indexes_list.sort(key=lambda x: x['bloat_pct'], reverse=True)
924977

978+
db_size_bytes = database_sizes.get(db_name, 0)
925979
bloated_indexes_by_db[db_name] = {
926980
"bloated_indexes": bloated_indexes_list,
927981
"total_count": len(bloated_indexes_list),
928982
"total_bloat_size_bytes": total_bloat_size,
929-
"total_bloat_size_pretty": self.format_bytes(total_bloat_size)
983+
"total_bloat_size_pretty": self.format_bytes(total_bloat_size),
984+
"database_size_bytes": db_size_bytes,
985+
"database_size_pretty": self.format_bytes(db_size_bytes)
930986
}
931987

932988
return self.format_report_data("F005", bloated_indexes_by_db, node_name)
@@ -1120,6 +1176,17 @@ def generate_f004_heap_bloat_report(self, cluster: str = "local", node_name: str
11201176
if not databases:
11211177
print("Warning: F004 - No databases found")
11221178

1179+
# Get database sizes
1180+
db_sizes_query = f'last_over_time(pgwatch_db_size_size_b{{cluster="{cluster}", node_name="{node_name}"}}[3h])'
1181+
db_sizes_result = self.query_instant(db_sizes_query)
1182+
database_sizes = {}
1183+
1184+
if db_sizes_result.get('status') == 'success' and db_sizes_result.get('data', {}).get('result'):
1185+
for result in db_sizes_result['data']['result']:
1186+
db_name = result['metric'].get('datname', 'unknown')
1187+
size_bytes = float(result['value'][1])
1188+
database_sizes[db_name] = size_bytes
1189+
11231190
bloated_tables_by_db = {}
11241191
for db_name in databases:
11251192
# Query table bloat using multiple metrics for each database
@@ -1174,11 +1241,14 @@ def generate_f004_heap_bloat_report(self, cluster: str = "local", node_name: str
11741241
# Sort by bloat percentage descending
11751242
bloated_tables_list.sort(key=lambda x: x['bloat_pct'], reverse=True)
11761243

1244+
db_size_bytes = database_sizes.get(db_name, 0)
11771245
bloated_tables_by_db[db_name] = {
11781246
"bloated_tables": bloated_tables_list,
11791247
"total_count": len(bloated_tables_list),
11801248
"total_bloat_size_bytes": total_bloat_size,
1181-
"total_bloat_size_pretty": self.format_bytes(total_bloat_size)
1249+
"total_bloat_size_pretty": self.format_bytes(total_bloat_size),
1250+
"database_size_bytes": db_size_bytes,
1251+
"database_size_pretty": self.format_bytes(db_size_bytes)
11821252
}
11831253

11841254
return self.format_report_data("F004", bloated_tables_by_db, node_name)

tests/reporter/test_generators_unit.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ def test_generate_a004_cluster_report(
165165
generator: PostgresReportGenerator,
166166
) -> None:
167167
def fake_query(query: str) -> dict[str, Any]:
168-
if "pgwatch_pg_database_size_bytes" in query and "sum(" not in query:
168+
if "pgwatch_db_size_size_b" in query and "sum(" not in query:
169169
return {
170170
"status": "success",
171171
"data": {

0 commit comments

Comments
 (0)