Re: eliminate xl_heap_visible to reduce WAL (and eventually set VM on-access)
Melanie Plageman <melanieplageman@gmail.com>
Commits
GET /api/v1/messages/:b64id/commits
the thread's linked commits as JSON, with link sources.
API reference →
-
Remove table_scan_analyze_next_tuple unneeded parameter OldestXmin
- 284925508ae6 19 (unreleased) landed
-
Simplify visibility check in heap_page_would_be_all_visible()
- 3efe58febc3c 19 (unreleased) landed
-
Eliminate use of cached VM value in lazy_scan_prune()
- 648a7e28d7c2 19 (unreleased) landed
-
Combine visibilitymap_set() cases in lazy_scan_prune()
- 21796c267d0a 19 (unreleased) landed
-
Fix const qualification in prune_freeze_setup()
- 4877391ce894 19 (unreleased) landed
-
Simplify vacuum visibility assertion
- bd298f54a0d6 19 (unreleased) landed
-
Split heap_page_prune_and_freeze() into helpers
- e135e044572e 19 (unreleased) landed
-
Assert that cutoffs are provided if freezing will be attempted
- cd38b7e77315 19 (unreleased) landed
-
Split PruneFreezeParams initializers to one field per line
- 1e14edcea5e1 19 (unreleased) landed
-
Refactor heap_page_prune_and_freeze() parameters into a struct
- 1937ed70621e 19 (unreleased) landed
-
Make heap_page_is_all_visible independent of LVRelState
- 3e4705484e0c 19 (unreleased) landed
-
Inline TransactionIdFollows/Precedes[OrEquals]()
- 43b05b38ea4d 19 (unreleased) landed
-
Add helper for freeze determination to heap_page_prune_and_freeze
- c8dd6542bae4 19 (unreleased) landed
-
Bump XLOG_PAGE_MAGIC after xl_heap_prune change
- 4a8fb58671d3 19 (unreleased) landed
-
Correct prune WAL record opcode name in comment
- ae8ea7278c16 19 (unreleased) landed
-
Add error codes when vacuum discovers VM corruption
- 8ec97e78a771 19 (unreleased) landed
-
Remove unused xl_heap_prune member, reason
- 4b5f206de2bb 19 (unreleased) landed
-
Remove unneeded VM pin from VM replay
- 3399c265543e 19 (unreleased) landed
-
Add assert and log message to visibilitymap_set
- e3d5ddb7ca91 19 (unreleased) landed
-
Add error codes to some corruption log messages
- fd6ec93bf890 13.0 cited
Attachments
- v16-0005-Make-heap_page_is_all_visible-independent-of-LVR.patch (text/x-patch) patch v16-0005
- v16-0004-Update-PruneState.all_-visible-frozen-earlier-in.patch (text/x-patch) patch v16-0004
- v16-0002-Assorted-trivial-heap_page_prune_and_freeze-clea.patch (text/x-patch) patch v16-0002
- v16-0003-Add-helper-for-freeze-determination-to-heap_page.patch (text/x-patch) patch v16-0003
- v16-0001-Eliminate-COPY-FREEZE-use-of-XLOG_HEAP2_VISIBLE.patch (text/x-patch) patch v16-0001
- v16-0006-Eliminate-XLOG_HEAP2_VISIBLE-from-vacuum-prune-f.patch (text/x-patch) patch v16-0006
- v16-0007-Eliminate-XLOG_HEAP2_VISIBLE-from-vacuum-phase-I.patch (text/x-patch) patch v16-0007
- v16-0008-Eliminate-XLOG_HEAP2_VISIBLE-from-empty-page-vac.patch (text/x-patch) patch v16-0008
- v16-0009-Remove-XLOG_HEAP2_VISIBLE-entirely.patch (text/x-patch) patch v16-0009
- v16-0010-Rename-GlobalVisTestIsRemovableXid-to-GlobalVisX.patch (text/x-patch) patch v16-0010
- v16-0011-Use-GlobalVisState-in-vacuum-to-determine-page-l.patch (text/x-patch) patch v16-0011
- v16-0012-Inline-TransactionIdFollows-Precedes.patch (text/x-patch) patch v16-0012
- v16-0014-Set-pd_prune_xid-on-insert.patch (text/x-patch) patch v16-0014
- v16-0013-Allow-on-access-pruning-to-set-pages-all-visible.patch (text/x-patch) patch v16-0013
On Wed, Sep 24, 2025 at 4:13 PM Robert Haas <robertmhaas@gmail.com> wrote: > > I find this patch set quite hard to follow. 0001 altogether removes > the use of XLOG_HEAP2_VISIBLE in cases where we use > XLOG_HEAP2_MULTI_INSERT, but then 0007 (the next non-refactoring > patch) begins half-removing the dependency on XLOG_HEAP2_VISIBLE, > assisted by 0009 and 0010, and then later you come back and remove the > other half of the dependency. I know it was I who proposed (off-list) > first making the XLOG_HEAP2_VISIBLE record only deal with the VM page > and not the heap buffer, but I'm not sure that idea quite worked out > in terms of making this easier to follow. At the least, it seems weird > that COPY FREEZE is an exception that gets handled in a different way > than all the other cases, fully removing the dependency in one step. > It would also be nice if each time you repost this, or maybe in a > README that you post along beside the actual patches, you'd include > some kind of roadmap to help the reader understand the internal > structure of the patch set, like 1 does this, 2-9 get us to here, > 10-whatever get us to this next place. In attached v16, I’ve reverted to removing XLOG_HEAP2_VISIBLE entirely, rather than first removing each caller's heap page from the VM WAL chain. I reordered changes and squashed several refactoring patches to improve patch-by-patch readability. This should make the set read differently from earlier versions that removed XLOG_HEAP2_VISIBLE and had more step-by-step mechanical refactoring. I think if we plan to go all the way with removing XLOG_HEAP2_VISIBLE, having intermediate patches that just set PD_ALL_VISIBLE when making other heap pages are more confusing than helpful. Also, I think having separate flags for setting PD_ALL_VISIBLE in the WAL record over-complicated the code. 0001: remove XLOG_HEAP2_VISIBLE from COPY FREEZE 0002 - 0005: various refactoring in advance of removing XLOG_HEAP2_VISIBLE in pruning 0006: Pruning and freezing by vacuum sets the VM and emits a single WAL record with those changes 0007: Reaping (phase III) by vacuum sets the VM and sets line pointers unused in a single WAL record 0008 - 0009: XLOG_HEAP2_VISIBLE is eliminated 0010 - 0012: preparation for setting VM on-access 0013: set VM on-access 0014: set pd_prune_xid on insert > I find myself fearful of the way that 0007 propagates the existing > hacks around setting the VM bit into a new place: > > + /* > + * We always emit a WAL record when setting > PD_ALL_VISIBLE, but we are > + * careful not to emit a full page image unless > + * checksums/wal_log_hints are enabled. We only set > the heap page LSN > + * if full page images were an option when emitting > WAL. Otherwise, > + * subsequent modifications of the page may > incorrectly skip emitting > + * a full page image. > + */ > + if (do_prune || nplans > 0 || > + (xlrec.flags & XLHP_SET_PD_ALL_VIS && > XLogHintBitIsNeeded())) > + PageSetLSN(page, lsn); > > I suppose it's not the worst thing to duplicate this logic, because > you're later going to remove the original copy. But, it took me >10 > minutes to find the text in src/backend/access/transam/README, in the > second half of the "Writing Hints" section, that explains the overall > principle here, and since the patch set doesn't seem to touch that > text, maybe you weren't even aware it was there. I don't think that src/backend/access/transam/README must change with my patch. It is still true that if the only change we are making to the heap page is setting PD_ALL_VISIBLE and checksums/wal_log_hints are disabled, we explicitly avoid an FPI and thus can't stamp the page LSN. > And, it's a little > weird to have a single WAL record that is either a hint or not a hint > depending on a complex set of conditions. PD_ALL_VISIBLE is different from tuple hints and other page hints because setting the VM is always WAL logged and when we replay that, it will always set PD_ALL_VISIBLE, so PD_ALL_VISIBLE is effectively always WAL-logged. The other hints aren't wal-logged unless checksums are enabled and we need an FPI. So PD_ALL_VISIBLE is different from other page hints in multiple ways. We can't make it more like those hints because of needing to preserve the invariant that the VM is never set when the page is clear. The only thing we could do is forbid omitting the FPI even when checksums are not enabled. > Anyway, I kind of wonder if it's time to back out the hack that I > installed here many years ago. At the time, I thought that it would be > bad if a VACUUM swept over the visibility map setting VM bits and as a > result emitted an FPI for every page in the entire heap ... but > everyone who is running with checksums has accepted that cost already, > and with those being the default, that's probably going to be most > people. I agree that PD_ALL_VISIBLE persistence is complicated, but we have other special cases that complicate the code for a performance benefit. I guess the question is if we are saying people shouldn't run without checksums in production. If that's true, then it's fine to remove this optimization. Otherwise, I'm not so sure. I think cloud providers generally have checksums enabled, but I don't know what is common on-prem. > It would be even more compelling if we were going to freeze, > prune, and set all-visible on access, because then presumably the case > where we touch a page and ONLY set the VM bit would be rare, so the > cost of doing that wouldn't matter much, but I guess the patch doesn't > go that far -- we can freeze or set all-visible on access but not > prune, without which the scenario I was worrying about at the time is > still fairly plausible, I think, if checksums are turned off. With the whole set applied, we can prune and set the VM on access but not freeze. I have a patch to do that, but it introduced noticeable CPU overhead to prepare the freeze plans. I'd have to spend much more time studying it to avoid regressing workloads where we don't end up freezing but prepare the freeze plans during SELECT queries. - Melanie