Hi,
On Fri, Aug 14, 2026 at 02:08:20PM -0500, Sami Imseih wrote:
> Hi,
>
> Thanks for the review.
>
> This version is rebased over 72a6dad1c9119c5. Index stats are now
> their own kind,
> so the patch also carries the counts/flushed delta calculations, as index
> stats
> can all be flushed mid-transaction.
Thanks!
> pgstat_report_stat() sets pgStatFlushInProgress while it flushes and resets it
> on entry, so a callback calling pgstat_force_next_flush() cannot re-enter. An
> error that leaves the flag set is harmless, since pgstat_report_stat() resets
> it on its next entry and a skipped force only defers the flush.
=== 1
+ pgStatFlushInProgress = true;
+
/* flush of variable-numbered stats tracked in pending entries list */
partial_flush |= pgstat_flush_pending_entries(nowait);
@@ -823,20 +836,24 @@ pgstat_report_stat(bool force)
if (!kind_info->flush_static_cb)
continue;
- partial_flush |= kind_info->flush_static_cb(nowait);
+ partial_flush |= kind_info->flush_static_cb(nowait,
+
!IsTransactionOrTransactionBlock());
}
}
+ pgStatFlushInProgress = false;
An ERROR raised by CHECK_FOR_INTERRUPTS() or by a flush callback would bypass
the final assignment. If it is caught in a subtransaction, the outer transaction
continues with pgStatFlushInProgress still true, so a later
pg_stat_force_next_flush()
skips the immediate flush.
I wonder if pgStatFlushInProgress should be reset through PG_FINALLY? The reset
at the beginning of pgstat_report_stat() could then become an assert.
=== 2
+ if (memcmp(&lstats->tab.counts, &lstats->tab.flushed,
+ sizeof(struct PgStat_TableCounts)) == 0)
+ return flush_txn ? PGSTAT_FLUSH_DONE : PGSTAT_FLUSH_PARTIAL;
.
.
.
+ lstats->tab.flushed.numscans = lstats->tab.counts.numscans;
+ lstats->tab.flushed.tuples_returned = lstats->tab.counts.tuples_returned;
+ lstats->tab.flushed.tuples_fetched = lstats->tab.counts.tuples_fetched;
+ lstats->tab.flushed.blocks_fetched = lstats->tab.counts.blocks_fetched;
+ lstats->tab.flushed.blocks_hit = lstats->tab.counts.blocks_hit;
After a HOT or new-page update, every subsequent in transaction flush takes the
relation lock, even when there is nothing new to flush (because those deferred
counters keep the memcmp() unequal).
I wonder if, when flush_txn is false, we should check whether any of the non
transactional counters changed before taking the lock? If only deferred counters
differ, the callback could return PGSTAT_FLUSH_PARTIAL immediately.
=== 3
Some comments look stale:
"Once the stats are flushed, PgStat_EntryRef->pending is freed."
The pending entry can now be retained until the transaction boundary.
"
/* Force statistics to be reported at the next occasion */
Datum
pg_stat_force_next_flush(PG_FUNCTION_ARGS)
"
It can flush immediately.
+ * totals are never double-counted. The same counts/flushed scheme is used for
+ * relation stats; see PgStat_TableStatus.
and
+ * This struct should contain only actual event counters, because we byte
+ * compare it against the flushed baseline (see PgStat_TableStatus) to detect
s/PgStat_TableStatus/PgStat_RelationStatus/?
In the commit message:
"
pg_stat_force_next_flush() is not documented"
s/is not/is?
Regards,
--
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com