From 04609efb38411ec83c7293528e6ea251e89cb231 Mon Sep 17 00:00:00 2001 From: David Rowley Date: Wed, 14 Oct 2026 22:08:21 +1300 Subject: [PATCH] Accumulate Memoize parallel worker stats across rescans When a Gather or Gather Merge node is rescanned, it starts a new set of workers, which write their Memoize stats into the same shared memory slots as the previous worker. ExecEndMemoize() used memcpy() for this, so EXPLAIN ANALYZE showed only the stats for the final scan. Here we adjust ExecEndMemoize() to accumulate the counters so that we gather up the totals over all rescans. Author: Tatsuya Kawata Reviewed-by: David Rowley Discussion: https://postgr.es/m/CAHza6qeA-4vB355co-2yAxDMRTpEneY0rg9CD2zMLYscYvv2UA@mail.gmail.com Backpatch-through: 14 --- src/backend/executor/nodeMemoize.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/backend/executor/nodeMemoize.c b/src/backend/executor/nodeMemoize.c index 79ed1cf7f0c..c8398bf9450 100644 --- a/src/backend/executor/nodeMemoize.c +++ b/src/backend/executor/nodeMemoize.c @@ -1124,7 +1124,16 @@ ExecEndMemoize(MemoizeState *node) Assert(ParallelWorkerNumber < node->shared_info->num_workers); si = &node->shared_info->sinstrument[ParallelWorkerNumber]; - memcpy(si, &node->stats, sizeof(MemoizeInstrumentation)); + + /* + * Gather and GatherMerge spin up new workers on rescan, so add these + * counters to accumulate the values over all scans. + */ + si->cache_hits += node->stats.cache_hits; + si->cache_misses += node->stats.cache_misses; + si->cache_evictions += node->stats.cache_evictions; + si->cache_overflows += node->stats.cache_overflows; + si->mem_peak = Max(si->mem_peak, node->stats.mem_peak); } /* Remove the cache context */