Memoize shows only the last execution's stats for parallel workers

From: Tatsuya Kawata <kawatatatsuya0913(at)gmail(dot)com>
To: Postgres hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org>
Subject: Memoize shows only the last execution's stats for parallel workers
Date: 2026-10-12 06:35:30
Message-ID: CAHza6qeA-4vB355co-2yAxDMRTpEneY0rg9CD2zMLYscYvv2UA@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi,

I noticed this while reviewing the patch in [1].

When a Gather (or Gather Merge) node is rescanned, a new set of workers
is started, and they write their Memoize stats into the same shared
memory slots as the previous ones. ExecEndMemoize() does this with
memcpy(), so EXPLAIN ANALYZE shows the per-worker Hits / Misses etc.
only for the last execution. Since the "actual rows" and "loops" of the
same worker are accumulated across all executions, the two don't match.

=== Example

CREATE TABLE mz_a (x int);
CREATE TABLE mz_b (y int);
INSERT INTO mz_a SELECT g % 10 FROM generate_series(1, 1000) g;
INSERT INTO mz_b SELECT g FROM generate_series(1, 1000) g;
CREATE INDEX ON mz_b (y);
ANALYZE mz_a, mz_b;
ALTER TABLE mz_a SET (parallel_workers = 2);

SET enable_hashjoin = off;
SET enable_mergejoin = off;
SET enable_material = off;
SET parallel_setup_cost = 0;
SET parallel_tuple_cost = 0;
SET min_parallel_table_scan_size = 0;
SET max_parallel_workers_per_gather = 2;
SET parallel_leader_participation = off;
SET max_parallel_workers = 1; -- so that Worker 0 runs both executions

EXPLAIN (ANALYZE, VERBOSE, COSTS OFF, TIMING OFF, SUMMARY OFF, BUFFERS OFF)
SELECT o.z, (SELECT count(*) + o.z FROM mz_a a JOIN mz_b b ON b.y = a.x)
FROM (VALUES (1), (2)) o(z);

=== Output on master

Hits + Misses (1000) doesn't match the loops (2000), and
Misses (10) doesn't match the loops of the inner Index Only Scan (20):

-> Memoize (actual rows=0.90 loops=2000)
...
Worker 0: actual rows=0.90 loops=2000
Hits: 990 Misses: 10 Evictions: 0 Overflows: 0 Memory Usage:
1kB
-> Index Only Scan using mz_b_y_idx on public.mz_b b (actual
rows=0.90 loops=20)
...
Worker 0: actual rows=0.90 loops=20

=== With the patch

Hits: 1980 Misses: 20 Evictions: 0 Overflows: 0 Memory Usage: 1kB

The attached patch accumulates the stats instead of using memcpy(), as
is already done for Parallel Bitmap Heap Scan (5a1e6df3b84). For Memory
Usage, it keeps the maximum.

Regards,
Tatsuya Kawata

[1]
https://www.postgresql.org/message-id/flat/CAApHDvoUWCms-b27MpDKd5_RUFtAnVrxPgN9u3QoQbBihk%2BS6g%40mail.gmail.com

Attachment Content-Type Size
v1-0001-Accumulate-Memoize-parallel-worker-stats-across-r.patch application/octet-stream 1.8 KB

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Rui Zhao 2026-10-12 06:50:18 Re: Deprecation warnings on Rocky 10.2 with current dev branch
Previous Message QQ Mail 2026-10-12 06:35:25 Re: BUG #19736: `JSON_OBJECT ... ABSENT ON NULL` accepts invalid keys when the value is NULL