1
0
mirror of https://github.com/postgres/postgres.git synced 2025-09-02 04:21:28 +03:00

Fix assert failures in parallel SERIALIZABLE READ ONLY.

1.  Make sure that we don't decrement SxactGlobalXminCount twice when
the SXACT_FLAG_RO_SAFE optimization is reached in a parallel query.
This could trigger a sanity check failure in assert builds.  Non-assert
builds recompute the count in SetNewSxactGlobalXmin(), so the problem
was hidden, explaining the lack of field reports.  Add a new isolation
test to exercise that case.

2.  Remove an assertion that the DOOMED flag can't be set on a partially
released SERIALIZABLEXACT.  Instead, ignore the flag (our transaction
was already determined to be read-only safe, and DOOMED is in fact set
during partial release, and there was already an assertion that it
wasn't set sooner).  Improve an existing isolation test so that it
reaches that case (previously it wasn't quite testing what it was
supposed to be testing; see discussion).

Back-patch to 12.  Bug #17116.  Defects in commit 47a338cf.

Reported-by: Alexander Lakhin <exclusion@gmail.com>
Discussion: https://postgr.es/m/17116-d6ca217acc180e30%40postgresql.org
This commit is contained in:
Thomas Munro
2023-03-06 15:07:15 +13:00
parent 102a5c164a
commit 47c0accbe0
6 changed files with 184 additions and 50 deletions

View File

@@ -3232,6 +3232,7 @@ SetNewSxactGlobalXmin(void)
void
ReleasePredicateLocks(bool isCommit, bool isReadOnlySafe)
{
bool partiallyReleasing = false;
bool needToClear;
SERIALIZABLEXACT *roXact;
dlist_mutable_iter iter;
@@ -3330,6 +3331,7 @@ ReleasePredicateLocks(bool isCommit, bool isReadOnlySafe)
else
{
MySerializableXact->flags |= SXACT_FLAG_PARTIALLY_RELEASED;
partiallyReleasing = true;
/* ... and proceed to perform the partial release below. */
}
}
@@ -3548,9 +3550,15 @@ ReleasePredicateLocks(bool isCommit, bool isReadOnlySafe)
* serializable transactions completes. We then find the "new oldest"
* xmin and purge any transactions which finished before this transaction
* was launched.
*
* For parallel queries in read-only transactions, it might run twice.
* We only release the reference on the first call.
*/
needToClear = false;
if (TransactionIdEquals(MySerializableXact->xmin, PredXact->SxactGlobalXmin))
if ((partiallyReleasing ||
!SxactIsPartiallyReleased(MySerializableXact)) &&
TransactionIdEquals(MySerializableXact->xmin,
PredXact->SxactGlobalXmin))
{
Assert(PredXact->SxactGlobalXminCount > 0);
if (--(PredXact->SxactGlobalXminCount) == 0)
@@ -4624,10 +4632,14 @@ PreCommit_CheckForSerializationFailure(void)
LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
/* Check if someone else has already decided that we need to die */
if (SxactIsDoomed(MySerializableXact))
/*
* Check if someone else has already decided that we need to die. Since
* we set our own DOOMED flag when partially releasing, ignore in that
* case.
*/
if (SxactIsDoomed(MySerializableXact) &&
!SxactIsPartiallyReleased(MySerializableXact))
{
Assert(!SxactIsPartiallyReleased(MySerializableXact));
LWLockRelease(SerializableXactHashLock);
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),