| From: | Greg Burd <greg(at)burd(dot)me> |
|---|---|
| To: | pgsql-hackers(at)lists(dot)postgresql(dot)org |
| Cc: | Peter Geoghegan <pg(at)bowt(dot)ie> |
| Subject: | Re: nbtree: opt-in leaf key prefix compression |
| Date: | 2026-10-27 17:50:50 |
| Message-ID: | y8lIX15Mjse_ALeMMfsK1oakyoxjIU4p2HLHF8sQArBsYgFDvAZz7vVCW26vl6IP7AEMWQArSkqBxvyCa4OKdXe8eiZ6OxqgvIC0FnJEM7c=@burd.me |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
Hello,
v3 attached. This closes the two open items I flagged with v2 and reports back
on the nbtsplitloc.c design question, which turned out to have a clearer answer
than I expected once I measured it.
1. The forced-full-page-image fallbacks
XLH_SPLIT_RFPI and XLH_DEDUP_RFPI carry a coded page as a full-page image in
the one case redo can't rebuild from logged plain items: a page so
compressible that its decoded payload exceeds a block. I could not reach
that threshold with any realistic data in v2, which left the path
unexercised. I instrumented it on maximally compressible keys (a 125-byte
shared prefix, tiny suffixes), the right half of a split tops out around 6
KB of plain payload, which is close, but the split-point balance keeps a
half under a block, so the fallback never fires naturally.
It is reachable in principle (a different item-size mix could get there), so
I'm keeping it rather than demoting it to a can't-happen error. An error
there would be a rare production PANIC on data that is merely unusual, not
corrupt. To make it testable, there's now a nbtree-prefix-force-rfpi
injection point that forces the fallback for every prefix split and
deduplication, and a test in src/test/modules/nbtree that builds and
deduplicates a prefix index with it attached, then amchecks the result.
Writing that test immediately caught a bug (the injection-point check sat
inside the WAL critical section, where the first-use cache load palloc()s)-
which is now fixed (the decision is made before the critical section).
Byte-for-byte redo through the forced image is covered by the existing
crash-recovery and streaming runs under wal_consistency_checking.
2. Alternatives-considered
The README now spells out why this is a separate mechanism rather than an
extension of posting lists: deduplication collapses tuples whose whole key
is equal; prefix compression factors out a leading run shared by distinct
keys. They attack different redundancy and compose on one page (deduplicate
first, then code the survivors), which is why a build runs both.
Overloading the posting-list format to also carry a shared prefix was
considered and rejected; a posting list is one key plus many TIDs, prefix
coding is many keys that share a prefix, so they'd need different per-item
layouts anyway, and it would entangle the two paths this patch keeps apart.
3. The nbtsplitloc.c question
I proposed moving the degenerate-split guard (the one that rejects a split
leaving the left page with no data items) out of nbtsplitloc.c. I tried the
cleanest version of that; make the guard unconditional instead of
prefix-only, on the theory that upstream never chooses that split point for
a plain page anyway, so it would be a no-op there. It is not a no-op: a
probe showed _bt_recsplitloc() is called at that point for plain pages all
the time, and rejecting it unconditionally broke three test suites. The
guard genuinely has to be prefix-specific, because it exists precisely
because coded-size accounting makes a point attractive that plain-size
accounting rejects on its own via the leftfree check.
Moving it into _bt_split() instead of nbtsplitloc.c doesn't help either:
_bt_split() doesn't choose split points, so it would have to re-derive or
reject-and-retry, which is more prefix logic in a more dangerous place (next
to the critical section), not less. So I've left the guard where it is: one
well-commented "if" in _bt_recsplitloc(). The coded-size accounting stays
too, for the reason in v2 (a compressed leaf legitimately holds more than
two plain pages' worth, so plain-size accounting makes feasible splits look
infeasible). If you'd still rather see it elsewhere I'll do it, but I
wanted to report that the "obvious" relocation makes the code worse, not
better.
Everything else from v2 stands: one split-collection routine shared by
primary and redo, amcheck structural checks plus a prefix-index test,
docs matched to the tuple-rebuild WAL design.
I think v3 is in good shape, so I'll leave it here and pick it back up on
review feedback (or if cf-bot turns something up).
I might run more benchmarks, just for fun.
-greg
| Attachment | Content-Type | Size |
|---|---|---|
| v3-0001-nbtree-opt-in-leaf-key-prefix-compression.patch | text/x-patch | 165.9 KB |
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Greg Burd | 2026-10-27 17:56:32 | Re: Let an ordering index scan hand its ORDER BY value to the target list |
| Previous Message | Greg Burd | 2026-10-27 17:18:39 | Re: Clean up code comments for pg_wchar_tbl |