Re: Correcting freeze conflict horizon calculation

From: Melanie Plageman <melanieplageman(at)gmail(dot)com>
To: Peter Geoghegan <pg(at)bowt(dot)ie>
Cc: PostgreSQL Hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org>, Andres Freund <andres(at)anarazel(dot)de>, Heikki Linnakangas <hlinnaka(at)iki(dot)fi>
Subject: Re: Correcting freeze conflict horizon calculation
Date: 2026-03-02 17:15:34
Message-ID: CAAKRu_bbaUV8OUjAfVa_iALgKnTSfB4gO3jnkfpcFgrxEpSGJQ@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

On Tue, Jun 3, 2025 at 2:00 PM Peter Geoghegan <pg(at)bowt(dot)ie> wrote:
>
> BTW, I don't think that you have to worry about removing/freezing xmax
> when it comes to generating a safe snapshotConflictHorizon.
>
> Nothing makes it unsafe for VACUUM to remove an xmax set by an updater
> that is known to have aborted right away -- no matter how that xmax
> compares to OldestXmin. Pruning has always been able to remove the
> successor version right away, no matter how recently the abort
> happened, so why shouldn't we *also* be able to set the xmax in the
> original version to InvalidTransactionId? That doesn't need to be
> taken into account by snapshotConflictHorizon handling. (Note that
> HeapTupleHeaderAdvanceConflictHorizon() is specifically aware that an
> aborted tuple's xmin shouldn't need to affect a prune record's
> conflict horizon; an aborted update can also skip consideration by
> snapshotConflictHorizon maintenance.)
>
> I also think that a locker-only xmax can be removed/frozen/set to
> InvalidTransactionId, as long as the tuple lock is no longer held on
> the primary -- it can safely be set to InvalidTransactionId, without
> it needing to affect snapshotConflictHorizon tracking at all. In a
> way, we already do this with Multis.
>
> It's already possible (though not particularly common) for VACUUM to
> fully set a Multi xmax to InvalidTransactionId when the Multi is >
> OldestMxact -- it can even happen when most of the individual members
> are still > OldestXmin. As long as the operation cannot affect tuple
> visibility on standbys, no special snapshotConflictHorizon is
> required. We do currently end up using OldestXmin-1 as our
> snapshotConflictHorizon when this happens, but as I said in the other
> email, I don't think that that's really required.

I wrote the attached patch to keep track of the newest to-be-frozen
xid. I think the freeze_xmin and replace_xvac cases are right. But I'm
not sure about the freeze_xmax and replace_xmax for multixacts cases.

For the freeze_xmax case for regular transaction IDs, are you saying
that the only way you can have one older than OldestXmin is if the
update transaction aborted? Or are there other ways you can have an
xmax older than OldestXmin?

My patch does not consider any multi member xids when calculating the
newest to-be-frozen xid. Locker-only xmaxes shouldn't affect
visibility on the standby. And I think, partially based on what you
are saying above and partially from reading the code, that update XIDs
older than OldestXmin don't matter because they would be from aborted
transactions and XIDs newer than OldestXmin won't be removed or
frozen. Does this sound right?

- Melanie

Attachment Content-Type Size
v1-0001-Use-the-newest-to-be-frozen-xid-as-the-conflict-h.patch text/x-patch 7.2 KB

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Antonin Houska 2026-03-02 17:23:30 Re: Adding REPACK [concurrently]
Previous Message Florin Irion 2026-03-02 16:41:41 Re: [PATCH] pg_get_domain_ddl: DDL reconstruction function for CREATE DOMAIN statement