You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
pgcolumnar_index_oid() does get_namespace_oid() plus get_relname_relid()
on every call, and it is now called on two hot paths. It is the single cause
behind a measured planning regression on main (#1198) and a measured maintenance
cost in #1213, and neither of us spotted it in review because we both checked
that scan counts moved, not what they cost.
The cost is already profiled, in this file
The #445 comment above the metadata flush session in columnar_metadata.c says
it outright:
Each open ran get_namespace_oid plus get_relname_relid (catcache probes)
and table_open (a relcache lookup, a lock, a resource-owner remember), and
the matching close undid it. A profile of the numeric write path (#445) put
that per-row open and close cycle, not the encoding, at the top of the
profile.
That path solved it with a caching session. The planner and the maintenance path
did not get the same treatment, and both have since grown callers.
#1198, on main: linear becomes constant, at a price
Measured by @OffgridwithJD. PG18, 8288510d vs 8ea98fc8, planning shared hit + read from the second EXPLAIN in a fresh backend per reading,
probe table created last:
tables
options pages
before
after
delta
10
1
12
17
+5
100
1
12
17
+5
500
3
17
21
+4
1000
6
20
21
+1
2000
11
25
21
-4
3000
17
31
21
-10
after is flat at 21 from 500 upward; before grows with pages. So #1198 is a
real optimisation above ~1200 tables. It should not be reverted.
But there is a configuration where it never wins
pgcolumnar.options gets a row only when set_options() is called. For an
installation that never calls it, both catalogs are empty, the two sequential
scans #1198 removes cost nothing, and the seven lookups replacing them cost six
buffers — at every size, with no crossover to reach:
1000 columnar tables, no set_options, no projections
before options_rows=0 projection_rows=0 19 hits
after options_rows=0 projection_rows=0 25 hits +6
That is the default configuration, and it is the case that makes this an
issue rather than a trade-off note.
So this should not be one bundled fix, which is what I was about to propose
before @OffgridwithJD separated the scalings.
Two measurements, not one number
@OffgridwithJD measured +5 at 10 tables on planning buffers with a fresh backend
per reading. I measured +16 and +37 on a different fixture and a different query. Same sign, different quantity. Neither confirms the other and averaging them
would be wrong. Quoting a single "cost of an index_oid lookup" from this issue is
a misreading of it.
How this got past two reviewers
I reported seq_scan and idx_scan moving the right way as the win. Those are
counts of scans, not of work — the house rule is measure the work, never the
intent, and I broke it. @OffgridwithJD verified the arms moved under a removal
proof, which establishes that a test can detect a change and says nothing
about whether the change is an improvement. Both checks passed; neither
asked the question.
pgcolumnar_index_oid()doesget_namespace_oid()plusget_relname_relid()on every call, and it is now called on two hot paths. It is the single cause
behind a measured planning regression on main (#1198) and a measured maintenance
cost in #1213, and neither of us spotted it in review because we both checked
that scan counts moved, not what they cost.
The cost is already profiled, in this file
The #445 comment above the metadata flush session in
columnar_metadata.csaysit outright:
That path solved it with a caching session. The planner and the maintenance path
did not get the same treatment, and both have since grown callers.
#1198, on main: linear becomes constant, at a price
Measured by @OffgridwithJD. PG18,
8288510dvs8ea98fc8, planningshared hit + readfrom the second EXPLAIN in a fresh backend per reading,probe table created last:
optionspagesafteris flat at 21 from 500 upward;beforegrows with pages. So #1198 is areal optimisation above ~1200 tables. It should not be reverted.
But there is a configuration where it never wins
pgcolumnar.optionsgets a row only whenset_options()is called. For aninstallation that never calls it, both catalogs are empty, the two sequential
scans #1198 removes cost nothing, and the seven lookups replacing them cost six
buffers — at every size, with no crossover to reach:
That is the default configuration, and it is the case that makes this an
issue rather than a trade-off note.
#1213: same cause, different scaling
Measured here.
EXPLAIN (ANALYZE, BUFFERS)overcompact(), 20 groups with 10retired, varying the number of other columnar tables sharing the catalogs:
row_grouppagesCrossover near 120 tables. The +140 is 140 lookups at about one buffer each
— seven per retired group across twenty groups.
Why the two need different remedies
bounds that; the lookup wants to come out of the loop entirely.
So this should not be one bundled fix, which is what I was about to propose
before @OffgridwithJD separated the scalings.
Two measurements, not one number
@OffgridwithJD measured +5 at 10 tables on planning buffers with a fresh backend
per reading. I measured +16 and +37 on a different fixture and a different query.
Same sign, different quantity. Neither confirms the other and averaging them
would be wrong. Quoting a single "cost of an index_oid lookup" from this issue is
a misreading of it.
How this got past two reviewers
I reported
seq_scanandidx_scanmoving the right way as the win. Those arecounts of scans, not of work — the house rule is measure the work, never the
intent, and I broke it. @OffgridwithJD verified the arms moved under a removal
proof, which establishes that a test can detect a change and says nothing
about whether the change is an improvement. Both checks passed; neither
asked the question.
Proposed order
🤖 Generated with Claude Code
https://claude.ai/code/session_01XiFn3HteTXnGdRiA2xDP2n