From a345db783a3676bfce57154a8a2756ef9525dcb1 Mon Sep 17 00:00:00 2001 From: David Rowley Date: Wed, 14 Oct 2026 22:07:25 +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 fdca97d7426..09d527a812a 100644 --- a/src/backend/executor/nodeMemoize.c +++ b/src/backend/executor/nodeMemoize.c @@ -1125,7 +1125,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 */