summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Munro2023-03-09 03:33:24 +0000
committerThomas Munro2023-03-09 03:33:24 +0000
commit65e388d4182ae4eba5afe880471453d33a4097d8 (patch)
tree4ec2e8ed722c288c00b8287371eb09cb8304bb9a
parent8bf826528ae9ff7a543a49c0dce665fa9ec542cb (diff)
Fix race in SERIALIZABLE READ ONLY.
Commit bdaabb9b started skipping doomed transactions when building the list of possible conflicts for SERIALIZABLE READ ONLY. That makes sense, because doomed transactions won't commit, but a couple of subtle things broke: 1. If all uncommitted r/w transactions are doomed, a READ ONLY transaction would arbitrarily not benefit from the safe snapshot optimization. It would not be taken immediately, and yet no other transaction would set SXACT_FLAG_RO_SAFE later. 2. In the same circumstances but with DEFERRABLE, GetSafeSnapshot() would correctly exit its wait loop without sleeping and then take the optimization in non-assert builds, but assert builds would fail a sanity check that SXACT_FLAG_RO_SAFE had been set by another transaction. This is similar to the case for PredXact->WritableSxactCount == 0. We should opt out immediately if our possibleUnsafeConflicts list is empty after filtering. The code to maintain the serializable global xmin is moved down below the new opt out site, because otherwise we'd have to reverse its effects before returning. Back-patch to all supported releases. Bug #17368. Reported-by: Alexander Lakhin <[email protected]> Discussion: https://postgr.es/m/17116-d6ca217acc180e30%40postgresql.org Discussion: https://postgr.es/m/20110707212159.GF76634%40csail.mit.edu
-rw-r--r--src/backend/storage/lmgr/predicate.c49
1 files changed, 31 insertions, 18 deletions
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 6b5a416873..23461b46f6 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -1782,24 +1782,6 @@ GetSerializableTransactionSnapshotInt(Snapshot snapshot,
return snapshot;
}
- /* Maintain serializable global xmin info. */
- if (!TransactionIdIsValid(PredXact->SxactGlobalXmin))
- {
- Assert(PredXact->SxactGlobalXminCount == 0);
- PredXact->SxactGlobalXmin = snapshot->xmin;
- PredXact->SxactGlobalXminCount = 1;
- SerialSetActiveSerXmin(snapshot->xmin);
- }
- else if (TransactionIdEquals(snapshot->xmin, PredXact->SxactGlobalXmin))
- {
- Assert(PredXact->SxactGlobalXminCount > 0);
- PredXact->SxactGlobalXminCount++;
- }
- else
- {
- Assert(TransactionIdFollows(snapshot->xmin, PredXact->SxactGlobalXmin));
- }
-
/* Initialize the structure. */
sxact->vxid = vxid;
sxact->SeqNo.lastCommitBeforeSnapshot = PredXact->LastSxactCommitSeqNo;
@@ -1839,6 +1821,19 @@ GetSerializableTransactionSnapshotInt(Snapshot snapshot,
SetPossibleUnsafeConflict(sxact, othersxact);
}
}
+
+ /*
+ * If we didn't find any possibly unsafe conflicts because every
+ * uncommitted writable transaction turned out to be doomed, then we
+ * can "opt out" immediately. See comments above the earlier check for
+ * PredXact->WritableSxactCount == 0.
+ */
+ if (dlist_is_empty(&sxact->possibleUnsafeConflicts))
+ {
+ ReleasePredXact(sxact);
+ LWLockRelease(SerializableXactHashLock);
+ return snapshot;
+ }
}
else
{
@@ -1847,6 +1842,24 @@ GetSerializableTransactionSnapshotInt(Snapshot snapshot,
(MaxBackends + max_prepared_xacts));
}
+ /* Maintain serializable global xmin info. */
+ if (!TransactionIdIsValid(PredXact->SxactGlobalXmin))
+ {
+ Assert(PredXact->SxactGlobalXminCount == 0);
+ PredXact->SxactGlobalXmin = snapshot->xmin;
+ PredXact->SxactGlobalXminCount = 1;
+ SerialSetActiveSerXmin(snapshot->xmin);
+ }
+ else if (TransactionIdEquals(snapshot->xmin, PredXact->SxactGlobalXmin))
+ {
+ Assert(PredXact->SxactGlobalXminCount > 0);
+ PredXact->SxactGlobalXminCount++;
+ }
+ else
+ {
+ Assert(TransactionIdFollows(snapshot->xmin, PredXact->SxactGlobalXmin));
+ }
+
MySerializableXact = sxact;
MyXactDidWrite = false; /* haven't written anything yet */