diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c index 94589cbdc88..8871e4b2794 100644 --- a/src/backend/executor/execReplication.c +++ b/src/backend/executor/execReplication.c @@ -234,6 +234,51 @@ retry: break; } + /* + * Nothing is visible to our snapshot. Before we let the caller report + * the row as missing, check whether some transaction is inserting it + * right now: the SnapshotDirty scan this code used to do would have + * waited for that transaction, and conflict detection depends on that + * wait. + * + * This extra scan is only used to find a transaction to wait for. The + * tuple it returns is never handed back to the caller, so it cannot + * reintroduce the concurrent-update race that the MVCC scan above fixes: + * after the wait we start over with a fresh MVCC snapshot. + */ + if (!found) + { + SnapshotData snap; + TransactionId xwait = InvalidTransactionId; + + InitDirtySnapshot(snap); + scan->xs_snapshot = &snap; + index_rescan(scan, skey, skey_attoff, NULL, 0); + + while (table_index_getnext_slot(scan, ForwardScanDirection, outslot)) + { + if (!isIdxSafeToSkipDuplicates) + { + if (eq == NULL) + eq = palloc0_array(TypeCacheEntry *, outslot->tts_tupleDescriptor->natts); + + if (!tuples_equal(outslot, searchslot, eq, NULL)) + continue; + } + + xwait = TransactionIdIsValid(snap.xmin) ? + snap.xmin : snap.xmax; + break; + } + + if (TransactionIdIsValid(xwait)) + { + PopActiveSnapshot(); + XactLockTableWait(xwait, NULL, NULL, XLTW_None); + goto retry; + } + } + /* Found tuple, try to lock it in the lockmode. */ if (found) { @@ -390,6 +435,34 @@ retry: break; } + /* See the matching comment in RelationFindReplTupleByIndex(). */ + if (!found) + { + SnapshotData snap; + TransactionId xwait = InvalidTransactionId; + + InitDirtySnapshot(snap); + scan->rs_snapshot = &snap; + table_rescan(scan, NULL); + + while (table_scan_getnextslot(scan, ForwardScanDirection, scanslot)) + { + if (!tuples_equal(scanslot, searchslot, eq, NULL)) + continue; + + xwait = TransactionIdIsValid(snap.xmin) ? + snap.xmin : snap.xmax; + break; + } + + if (TransactionIdIsValid(xwait)) + { + PopActiveSnapshot(); + XactLockTableWait(xwait, NULL, NULL, XLTW_None); + goto retry; + } + } + /* Found tuple, try to lock it in the lockmode. */ if (found) {