Fix race condition in snapshot caching when 2PC is used.
authorAndres Freund <andres@anarazel.de>
Tue, 18 Aug 2020 23:31:12 +0000 (16:31 -0700)
committerAndres Freund <andres@anarazel.de>
Tue, 18 Aug 2020 23:31:12 +0000 (16:31 -0700)
When preparing a transaction xactCompletionCount needs to be
incremented, even though the transaction has not committed
yet. Otherwise the snapshot used within the transaction otherwise can
get reused outside of the prepared transaction. As GetSnapshotData()
does not include the current xid when building a snapshot, reuse would
not be correct.

Somewhat surprisingly the regression tests only rarely show incorrect
results without the fix. The reason for that is that often the
snapshot's xmax will be >= the backend xid, yielding a snapshot that
is correct, despite the bug.

I'm working on a reliable test for the bug, but it seems worth seeing
whether this fixes all the BF failures while I do.

Author: Andres Freund <andres@anarazel.de>
Discussion: https://postgr.es/m/E1k7tGP-0005V0-5k@gemulon.postgresql.org

src/backend/storage/ipc/procarray.c

index e687cde6f176fcf955df020ca29f3e1489dad0a0..51f8099cad2ca43d7be1c5b5f66426d282fdbcb1 100644 (file)
@@ -860,6 +860,15 @@ ProcArrayClearTransaction(PGPROC *proc)
    Assert(!(proc->vacuumFlags & PROC_VACUUM_STATE_MASK));
    Assert(!proc->delayChkpt);
 
+   /*
+    * Need to increment completion count even though transaction hasn't
+    * really committed yet. The reason for that is that GetSnapshotData()
+    * omits the xid of the current transaction, thus without the increment we
+    * otherwise could end up reusing the snapshot later. Which would be bad,
+    * because it might not count the prepared transaction as running.
+    */
+   ShmemVariableCache->xactCompletionCount++;
+
    /* Clear the subtransaction-XID cache too */
    Assert(ProcGlobal->subxidStates[pgxactoff].count == proc->subxidStatus.count &&
           ProcGlobal->subxidStates[pgxactoff].overflowed == proc->subxidStatus.overflowed);