Re: Correcting freeze conflict horizon calculation

Melanie Plageman <melanieplageman@gmail.com>

From: Melanie Plageman <melanieplageman@gmail.com>
To: Peter Geoghegan <pg@bowt.ie>
Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>, Andres Freund <andres@anarazel.de>, Heikki Linnakangas <hlinnaka@iki.fi>
Date: 2026-03-10T16:12:15Z
Lists: pgsql-hackers

Attachments

On Mon, Mar 9, 2026 at 4:15 PM Peter Geoghegan <pg@bowt.ie> wrote:
>
> I'm not sure that you need to add any new comments above
> FreezeMultiXactId. The underlying principles that justify ignoring
> xmax when it is a multi are exactly the same as those that apply when
> xmax is a normal XID. I think that what you actually need is a single
> comment block (maybe 2) near the start of or above
> heap_prepare_freeze_tuple explaining your new snapshotConflictHorizon
> maintenance code, mentioning:
>
[...snip...]
>
> I'm trying not to be too prescriptive here; I just think that
> emphasizing high-level logical database concepts over physical
> database implementation details makes sense. I don't expect you to
> follow what I've written here all too closely. I didn't have the time
> to distill it down myself.

I've taken a stab at distilling down the comment and put it above
heap_prepare_freeze_tuple(). This is what I came up with:

 * FreezePageConflictXid is advanced only for xmin/xvac freezing, not for xmax
 * changes. We only remove xmax state here when it is lock-only, or when the
 * updater XID (including an updater member of a MultiXact) must be aborted;
 * otherwise, the tuple would already be removable. Neither case affects
 * visibility on a standby.

I don't mention why we need a conflict horizon when freezing there,
but I do in the comment above the struct member:

    /*
     * Newest XID that this page's freeze actions will remove from tuple
     * visibility metadata (currently xmin and/or xvac). It is used to derive
     * the snapshot conflict horizon for a WAL record that freezes tuples. On
     * a standby, we must not replay that change while any snapshot could
     * still treat that XID as running.
     *
     * It's only used if we execute freeze plans for this page, so there is no
     * corresponding "no freeze" tracker.
     */
    TransactionId FreezePageConflictXid;

Do these seem correct enough and understandable?

- Melanie

Commits

  1. Use the newest to-be-frozen xid as the conflict horizon for freezing

  2. Avoid hot standby cancels from VAC FREEZE