From a454f10919fa765b5498ef2b47e0689b6a7959d2 Mon Sep 17 00:00:00 2001 From: Sami Imseih Date: Mon, 24 Aug 2026 18:30:27 +0000 Subject: [PATCH v8 1/3] pgstat: Prepare db pending stats before flush Move pgstat_prep_database_pending() in the relation and index flush callbacks before any shared stats are updated, so an ERROR cannot leave stats only partially flushed. Reported-by: Bertrand Drouvot Author: Sami Imseih Discussion: https://postgr.es/m/aowFezMGPX4VTeFa%40bdtpg --- src/backend/utils/activity/pgstat_index.c | 7 ++++++- src/backend/utils/activity/pgstat_relation.c | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/backend/utils/activity/pgstat_index.c b/src/backend/utils/activity/pgstat_index.c index a1f9a4c6ac1..10d2350b7e0 100644 --- a/src/backend/utils/activity/pgstat_index.c +++ b/src/backend/utils/activity/pgstat_index.c @@ -52,6 +52,12 @@ pgstat_index_flush_cb(PgStat_EntryRef *entry_ref, bool nowait) sizeof(struct PgStat_IndexCounts))) return true; + /* + * Do this before mutating shared stats, so an ERROR leaves no partial + * flush. + */ + dbentry = pgstat_prep_database_pending(dboid); + if (!pgstat_lock_entry(entry_ref, nowait)) return false; @@ -74,7 +80,6 @@ pgstat_index_flush_cb(PgStat_EntryRef *entry_ref, bool nowait) pgstat_unlock_entry(entry_ref); /* The entry was successfully flushed, add the same to database stats */ - dbentry = pgstat_prep_database_pending(dboid); dbentry->tuples_returned += lstats->idx.tuples_returned; dbentry->tuples_fetched += lstats->idx.tuples_fetched; dbentry->blocks_fetched += lstats->idx.blocks_fetched; diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c index 17746bf5c54..346730ac062 100644 --- a/src/backend/utils/activity/pgstat_relation.c +++ b/src/backend/utils/activity/pgstat_relation.c @@ -902,6 +902,12 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait) sizeof(struct PgStat_TableCounts))) return true; + /* + * Do this before mutating shared stats, so an ERROR leaves no partial + * flush. + */ + dbentry = pgstat_prep_database_pending(dboid); + if (!pgstat_lock_entry(entry_ref, nowait)) return false; @@ -958,7 +964,6 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait) pgstat_unlock_entry(entry_ref); /* The entry was successfully flushed, add the same to database stats */ - dbentry = pgstat_prep_database_pending(dboid); dbentry->tuples_returned += lstats->tab.counts.tuples_returned; dbentry->tuples_fetched += lstats->tab.counts.tuples_fetched; dbentry->tuples_inserted += lstats->tab.counts.tuples_inserted; -- 2.47.3