This is an automated email from the ASF dual-hosted git repository. chenjinbao1989 pushed a commit to branch cbdb-postgres-merge in repository https://gitbox.apache.org/repos/asf/cloudberry.git
commit ec26821b683b662ec7237d39a882709cda563055 Author: liushengsong <[email protected]> AuthorDate: Wed Mar 11 11:19:44 2026 +0800 fix: ensure dropped database pgstat entry is cleaned up and use gp_stat_force_next_flush in test - Add missing pgstat_drop_database() call in dropdb() that was lost during PG16 merge, ensuring database stats are cleaned up on DROP DATABASE - Replace pg_sleep(0.77) with gp_stat_force_next_flush() in autovacuum-analyze isolation2 test for reliable and faster stats flush - Use HTAB instead of List for zeroed_rels in pgstat_combine_from_qe() to avoid O(N²) lookup when many tables are modified across segments - Move pgstat_send_qd_tabstats() before PortalDrop() in exec_mpp_query() to avoid potential issues if PortalDrop triggers subtransaction cleanup Co-Authored-By: Claude Opus 4.6 <[email protected]> --- src/backend/commands/dbcommands.c | 5 ++++ src/backend/tcop/postgres.c | 13 +++++----- src/backend/utils/activity/pgstat_relation.c | 17 ++++++++++--- .../isolation2/input/autovacuum-analyze.source | 14 +++++------ .../isolation2/output/autovacuum-analyze.source | 29 ++++++++++++++++------ 5 files changed, 54 insertions(+), 24 deletions(-) diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index 6b5a0ae371a..1c4a6afa8b3 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -1873,6 +1873,11 @@ dropdb(const char *dbname, bool missing_ok, bool force) */ dropDatabaseDependencies(db_id); + /* + * Tell the cumulative stats system to forget it immediately, too. + */ + pgstat_drop_database(db_id); + /* * Drop db-specific replication slots. */ diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 92f744e673f..4014ad8bac2 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -1496,16 +1496,17 @@ exec_mpp_query(const char *query_string, (*receiver->rDestroy) (receiver); - PortalDrop(portal, false); - /* - * GPDB: Send pending relation stats to QD before closing the - * transaction. The stats are in pgStatXactStack (transaction-level - * counts); finish_xact_command() will call AtEOXact_PgStat() which - * NULLs pgStatXactStack, so we must capture the stats first. + * GPDB: Send pending relation stats to QD before PortalDrop and + * finish_xact_command(). The stats are in pgStatXactStack + * (transaction-level counts); finish_xact_command() calls + * AtEOXact_PgStat() which NULLs pgStatXactStack. We also send + * before PortalDrop to avoid any subtransaction cleanup side effects. */ pgstat_send_qd_tabstats(); + PortalDrop(portal, false); + /* * Close down transaction statement before reporting command-complete. * This is so that any end-of-transaction errors are reported before diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c index 33532b0030e..45bbf9abcb7 100644 --- a/src/backend/utils/activity/pgstat_relation.c +++ b/src/backend/utils/activity/pgstat_relation.c @@ -22,6 +22,7 @@ #include "catalog/partition.h" #include "libpq-int.h" #include "postmaster/autovacuum.h" +#include "utils/hsearch.h" #include "utils/memutils.h" #include "utils/pgstat_internal.h" #include "utils/rel.h" @@ -985,11 +986,19 @@ pgstat_combine_from_qe(CdbDispatchResults *primaryResults) int i, j; int nest_level = GetCurrentTransactionNestLevel(); - List *zeroed_rels = NIL; + HASHCTL hctl; + HTAB *zeroed_rels; if (primaryResults == NULL) return; + /* Use a hash table for O(1) lookup instead of O(N) list scan */ + hctl.keysize = sizeof(Oid); + hctl.entrysize = sizeof(Oid); + hctl.hcxt = CurrentMemoryContext; + zeroed_rels = hash_create("pgstat_combine zeroed_rels", 64, &hctl, + HASH_ELEM | HASH_BLOBS | HASH_CONTEXT); + for (i = 0; i < primaryResults->resultCount; i++) { CdbDispatchResult *dispResult = &primaryResults->resultArray[i]; @@ -1040,13 +1049,13 @@ pgstat_combine_from_qe(CdbDispatchResults *primaryResults) trans = tabstat->trans; /* Zero on first encounter to undo previous merge */ - if (!list_member_oid(zeroed_rels, rec->t_id)) + if (!hash_search(zeroed_rels, &rec->t_id, HASH_FIND, NULL)) { trans->tuples_inserted = 0; trans->tuples_updated = 0; trans->tuples_deleted = 0; trans->truncdropped = false; - zeroed_rels = lappend_oid(zeroed_rels, rec->t_id); + hash_search(zeroed_rels, &rec->t_id, HASH_ENTER, NULL); } /* Accumulate QE counts from this segment */ @@ -1059,5 +1068,5 @@ pgstat_combine_from_qe(CdbDispatchResults *primaryResults) } } - list_free(zeroed_rels); + hash_destroy(zeroed_rels); } diff --git a/src/test/isolation2/input/autovacuum-analyze.source b/src/test/isolation2/input/autovacuum-analyze.source index 19187b107c2..2445c4178b8 100644 --- a/src/test/isolation2/input/autovacuum-analyze.source +++ b/src/test/isolation2/input/autovacuum-analyze.source @@ -171,13 +171,13 @@ SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', ' -- with auto_stats, the auto-ANALYZE still trigger 2: INSERT INTO autostatstbl select i from generate_series(1, 1000) as i; -2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat. +2: select gp_stat_force_next_flush(); -- auto_stats executed but auto-ANALYZE not execute yet since we suspend before finish ANALYZE. SELECT count(*) FROM pg_statistic where starelid = 'autostatstbl'::regclass; select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass; --- expect analyze_count = 1, autoanalyze_count = 0, and n_mod_since_analyze = 1000 since ANALYZE executed --- in same transaction for the insert statement. +-- expect analyze_count = 1, autoanalyze_count = 0, and n_mod_since_analyze = 1000 since ANALYZE +-- executed in same transaction for the insert statement. select analyze_count, autoanalyze_count, n_mod_since_analyze from pg_stat_all_tables where relname = 'autostatstbl'; -- Wait until autovacuum is triggered @@ -205,7 +205,7 @@ SELECT gp_inject_fault('analyze_finished_one_relation', 'skip', '', '', 'autosta SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', '', 'autostatstbl', 1, -1, 0, 1); 2: INSERT INTO autostatstbl select i from generate_series(1001, 2000) as i; -2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat. +2: select gp_stat_force_next_flush(); -- auto_stats executed but auto-ANALYZE not execute yet since we suspend before finish ANALYZE. select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass; @@ -238,12 +238,12 @@ SELECT gp_inject_fault('analyze_finished_one_relation', 'skip', '', '', 'autosta SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', '', 'autostatstbl', 1, -1, 0, 1); 2: INSERT INTO autostatstbl select i from generate_series(2001, 3000) as i; -2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat. +2: select gp_stat_force_next_flush(); -- auto_stats should not executed and auto-ANALYZE not execute yet since we suspend before finish ANALYZE. select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass; --- expect analyze_count = 2, autoanalyze_count = 2, and n_mod_since_analyze = 1000 since ANALYZE executed --- in same transaction for the insert statement. +-- expect analyze_count = 2, autoanalyze_count = 2, and n_mod_since_analyze = 1000 since ANALYZE +-- executed in same transaction for the insert statement. select analyze_count, autoanalyze_count, n_mod_since_analyze from pg_stat_all_tables where relname = 'autostatstbl'; -- Wait until autovacuum is triggered diff --git a/src/test/isolation2/output/autovacuum-analyze.source b/src/test/isolation2/output/autovacuum-analyze.source index b8727a10a51..457ec1b38fb 100644 --- a/src/test/isolation2/output/autovacuum-analyze.source +++ b/src/test/isolation2/output/autovacuum-analyze.source @@ -408,7 +408,12 @@ SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', ' -- with auto_stats, the auto-ANALYZE still trigger 2: INSERT INTO autostatstbl select i from generate_series(1, 1000) as i; INSERT 1000 -2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat. +2: select gp_stat_force_next_flush(); + gp_stat_force_next_flush +-------------------------- + +(1 row) + -- auto_stats executed but auto-ANALYZE not execute yet since we suspend before finish ANALYZE. SELECT count(*) FROM pg_statistic where starelid = 'autostatstbl'::regclass; count @@ -420,8 +425,8 @@ select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass; ----------+----------- 3 | 1000 (1 row) --- expect analyze_count = 1, autoanalyze_count = 0, and n_mod_since_analyze = 1000 since ANALYZE executed --- in same transaction for the insert statement. +-- expect analyze_count = 1, autoanalyze_count = 0, and n_mod_since_analyze = 1000 since ANALYZE +-- executed in same transaction for the insert statement. select analyze_count, autoanalyze_count, n_mod_since_analyze from pg_stat_all_tables where relname = 'autostatstbl'; analyze_count | autoanalyze_count | n_mod_since_analyze ---------------+-------------------+--------------------- @@ -493,7 +498,12 @@ SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', ' 2: INSERT INTO autostatstbl select i from generate_series(1001, 2000) as i; INSERT 1000 -2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat. +2: select gp_stat_force_next_flush(); + gp_stat_force_next_flush +-------------------------- + +(1 row) + -- auto_stats executed but auto-ANALYZE not execute yet since we suspend before finish ANALYZE. select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass; relpages | reltuples @@ -572,15 +582,20 @@ SELECT gp_inject_fault('auto_vac_worker_after_report_activity', 'suspend', '', ' 2: INSERT INTO autostatstbl select i from generate_series(2001, 3000) as i; INSERT 1000 -2: select pg_sleep(0.77); -- Force pgstat_report_stat() to send tabstat. +2: select gp_stat_force_next_flush(); + gp_stat_force_next_flush +-------------------------- + +(1 row) + -- auto_stats should not executed and auto-ANALYZE not execute yet since we suspend before finish ANALYZE. select relpages, reltuples from pg_class where oid = 'autostatstbl'::regclass; relpages | reltuples ----------+----------- 3 | 2000 (1 row) --- expect analyze_count = 2, autoanalyze_count = 2, and n_mod_since_analyze = 1000 since ANALYZE executed --- in same transaction for the insert statement. +-- expect analyze_count = 2, autoanalyze_count = 2, and n_mod_since_analyze = 1000 since ANALYZE +-- executed in same transaction for the insert statement. select analyze_count, autoanalyze_count, n_mod_since_analyze from pg_stat_all_tables where relname = 'autostatstbl'; analyze_count | autoanalyze_count | n_mod_since_analyze ---------------+-------------------+--------------------- --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
