TransactionXmin != MyProc->xmin

Heikki Linnakangas <hlinnaka@iki.fi>

From: Heikki Linnakangas <hlinnaka@iki.fi>
To: "pgsql-hackers@postgresql.org" <pgsql-hackers@postgresql.org>
Date: 2024-12-12T18:16:39Z
Lists: pgsql-hackers

Attachments

The comment in GetSnapshotData() defines transactionXmin like this:

> TransactionXmin: the oldest xmin of any snapshot in use in the 
> current transaction (this is the same as MyProc->xmin).
However, we don't update TransactionXmin when we update MyProc->xmin in 
SnapshotResetXmin(). So TransactionXmin can be older than MyProc->xmin.

I browsed around to see if that might cause trouble, and found one such 
case: SubTransGetTopmostTransaction() uses TransactionXmin as the 
cut-off point so that it doesn't try to perform lookups of old XIDs that 
might've already been truncated away from pg_subtrans. When 
TransactionXmin is older than MyProc->xmin, then it might do that. The 
attached isolation test demonstrates that case and produces an error:

ERROR:  could not access status of transaction 17190290
DETAIL:  Could not open file "pg_subtrans/0106": No such file or directory.

A straightforward fix is to ensure that TransactionXmin is updated 
whenever MyProc->xmin is:

diff --git a/src/backend/utils/time/snapmgr.c 
b/src/backend/utils/time/snapmgr.c
index a1a0c2adeb6..f59830abd21 100644
--- a/src/backend/utils/time/snapmgr.c
+++ b/src/backend/utils/time/snapmgr.c
@@ -883,7 +883,7 @@ SnapshotResetXmin(void)
  										pairingheap_first(&RegisteredSnapshots));

  	if (TransactionIdPrecedes(MyProc->xmin, minSnapshot->xmin))
-		MyProc->xmin = minSnapshot->xmin;
+		MyProc->xmin = TransactionXmin = minSnapshot->xmin;
  }

  /*

Anyone see a reason not to do that?


There are a two other places where we set MyProc->xmin without updating 
TransactionXmin:

1. In ProcessStandbyHSFeedbackMessage(). AFAICS that's OK because 
walsender doesn't use TransactionXmin for anything.

2. In SnapBuildInitialSnapshot(). I suppose that's also OK because the 
TransactionXmin isn't used. I don't quite remember when that function 
might be called though.

In any case, I propose that we set TransactionXmin in all of those cases 
as well, so that TransactionXmin is always the equal to MyProc->xmin. 
Maybe even rename it to MyProcXmin to make that more clear.

-- 
Heikki Linnakangas
Neon (https://neon.tech)

Commits

  1. Update TransactionXmin when MyProc->xmin is updated