Caching a partition index's parent OID in the relcache?

From: Ayush Tiwari <ayushtiwari(dot)slg01(at)gmail(dot)com>
To: PostgreSQL Hackers <pgsql-hackers(at)postgresql(dot)org>, Álvaro Herrera <alvherre(at)kurilemu(dot)de>
Subject: Caching a partition index's parent OID in the relcache?
Date: 2026-07-17 13:41:51
Message-ID: CAJTYsWUV3Tz5A161y2EcCMqnmCYqKNm6Jt=rXCx9bEOJ-hqL=A@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi,

While poking at INSERT ... ON CONFLICT on partitioned tables,
get_partition_ancestors() kept showing up in profiles, it scans
pg_inherits once per hierarchy level, and ExecInitPartitionInfo() calls
it once per leaf-partition index while sorting out arbiter indexes.
There's already an XXX in that loop wondering about a syscache "or some
other way to cache".

I tried the "other way": keep the immediate parent OID on the relcache
entry (a lazily-filled rd_partparent), then use the existing walker above
that. This avoids repeatedly scanning the leaf link and eliminates the
pg_inherits scan entirely for the common one-level hierarchy. Patch
attached (fairly small).

Invalidation uses the existing pg_class relcache events. ATTACH and
DETACH update the child index's relispartition flag, while REINDEX
CONCURRENTLY swaps that flag between the old and replacement index;
creation and drop replace the relation altogether. One detail is that an
open index takes RelationReloadIndexInfo() rather than the full relcache
rebuild path, so the patch clears rd_partparent there as well. No explicit
pg_inherits invalidation is needed.

Quick numbers (256-leaf table, one upsert touching every leaf per statement,
median per-statement latency over 10 warm-session runs):

indexes/table master patched speedup
1 3.17 ms 2.35 ms 1.3x
8 10.18 ms 5.51 ms 1.8x
16 22.40 ms 7.70 ms 2.9x

To be upfront, that's the warm case, a cold first statement still scans
pg_inherits once per index, so it's really repeated upserts that benefit.

Measured on an Azure Standard_D32as_v5 (AMD EPYC 7763, 32 vCPU) under WSL2,
-O2 build without asserts.

I checked REINDEX CONCURRENTLY (010_index_concurrently_upsert.pl), the
core regression and isolation suites, and added a regression that warms
the cache, moves a partition between two partitioned tables, and repeats
the exercise across a savepoint rollback.

One thing I'm unsure about is whether rd_partparent, as the first "upward"
partition field in RelationData (the rest point downward), is the right
place for this cache.

Does this seem worth pursuing, thoughts?

Thanks,
Ayush

Attachment Content-Type Size
v1-0001-Cache-partition-index-parent-in-relcache.patch application/octet-stream 12.4 KB

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Daniel Westermann (DWE) 2026-07-17 14:04:00 Re: Deprecation warnings on Rocky 10.2 with current dev branch
Previous Message Marcos Pegoraro 2026-07-17 13:41:06 Re: [PATCH] Add pg_get_trigger_ddl() to retrieve the CREATE TRIGGER statement