Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 18781cd

Browse files
committed
Avoid duplicate XIDs at recovery when building initial snapshot
On a primary, sets of XLOG_RUNNING_XACTS records are generated on a periodic basis to allow recovery to build the initial state of transactions for a hot standby. The set of transaction IDs is created by scanning all the entries in ProcArray. However it happens that its logic never counted on the fact that two-phase transactions finishing to prepare can put ProcArray in a state where there are two entries with the same transaction ID, one for the initial transaction which gets cleared when prepare finishes, and a second, dummy, entry to track that the transaction is still running after prepare finishes. This way ensures a continuous presence of the transaction so as callers of for example TransactionIdIsInProgress() are always able to see it as alive. So, if a XLOG_RUNNING_XACTS takes a standby snapshot while a two-phase transaction finishes to prepare, the record can finish with duplicated XIDs, which is a state expected by design. If this record gets applied on a standby to initial its recovery state, then it would simply fail, so the odds of facing this failure are very low in practice. It would be tempting to change the generation of XLOG_RUNNING_XACTS so as duplicates are removed on the source, but this requires to hold on ProcArrayLock for longer and this would impact all workloads, particularly those using heavily two-phase transactions. XLOG_RUNNING_XACTS is also actually used only to initialize the standby state at recovery, so instead the solution is taken to discard duplicates when applying the initial snapshot. Diagnosed-by: Konstantin Knizhnik Author: Michael Paquier Discussion: https://postgr.es/m/0c96b653-4696-d4b4-6b5d-78143175d113@postgrespro.ru Backpatch-through: 9.3
1 parent 52f76a0 commit 18781cd

File tree

1 file changed

+19
-2
lines changed

1 file changed

+19
-2
lines changed

src/backend/storage/ipc/procarray.c

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -805,10 +805,21 @@ ProcArrayApplyRecoveryInfo(RunningTransactions running)
805805
qsort(xids, nxids, sizeof(TransactionId), xidComparator);
806806

807807
/*
808-
* Add the sorted snapshot into KnownAssignedXids
808+
* Add the sorted snapshot into KnownAssignedXids. The running-xacts
809+
* snapshot may include duplicated xids because of prepared
810+
* transactions, so ignore them.
809811
*/
810812
for (i = 0; i < nxids; i++)
813+
{
814+
if (i > 0 && TransactionIdEquals(xids[i - 1], xids[i]))
815+
{
816+
elog(DEBUG1,
817+
"found duplicated transaction %u for KnownAssignedXids insertion",
818+
xids[i]);
819+
continue;
820+
}
811821
KnownAssignedXidsAdd(xids[i], xids[i], true);
822+
}
812823

813824
KnownAssignedXidsDisplay(trace_recovery(DEBUG3));
814825
}
@@ -1904,7 +1915,8 @@ ProcArrayInstallRestoredXmin(TransactionId xmin, PGPROC *proc)
19041915
* GetRunningTransactionData -- returns information about running transactions.
19051916
*
19061917
* Similar to GetSnapshotData but returns more information. We include
1907-
* all PGXACTs with an assigned TransactionId, even VACUUM processes.
1918+
* all PGXACTs with an assigned TransactionId, even VACUUM processes and
1919+
* prepared transactions.
19081920
*
19091921
* We acquire XidGenLock and ProcArrayLock, but the caller is responsible for
19101922
* releasing them. Acquiring XidGenLock ensures that no new XIDs enter the proc
@@ -1918,6 +1930,11 @@ ProcArrayInstallRestoredXmin(TransactionId xmin, PGPROC *proc)
19181930
* This is never executed during recovery so there is no need to look at
19191931
* KnownAssignedXids.
19201932
*
1933+
* Dummy PGXACTs from prepared transaction are included, meaning that this
1934+
* may return entries with duplicated TransactionId values coming from
1935+
* transaction finishing to prepare. Nothing is done about duplicated
1936+
* entries here to not hold on ProcArrayLock more than necessary.
1937+
*
19211938
* We don't worry about updating other counters, we want to keep this as
19221939
* simple as possible and leave GetSnapshotData() as the primary code for
19231940
* that bookkeeping.

0 commit comments

Comments
 (0)