Skip to content

Commit 547cedc

Browse files
nkey0Commitfest Bot
authored andcommitted
Fix logical replication conflict detection during tuple lookup
SNAPSHOT_DIRTY scans could miss conflict detection with concurrent transactions during logical replication. Replace SNAPSHOT_DIRTY scan with the GetLatestSnapshot in RelationFindReplTupleByIndex and RelationFindReplTupleSeq.
1 parent a5c9d3f commit 547cedc

File tree

1 file changed

+18
-45
lines changed

1 file changed

+18
-45
lines changed

src/backend/executor/execReplication.c

Lines changed: 18 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,6 @@ RelationFindReplTupleByIndex(Relation rel, Oid idxoid,
186186
ScanKeyData skey[INDEX_MAX_KEYS];
187187
int skey_attoff;
188188
IndexScanDesc scan;
189-
SnapshotData snap;
190-
TransactionId xwait;
191189
Relation idxrel;
192190
bool found;
193191
TypeCacheEntry **eq = NULL;
@@ -198,17 +196,17 @@ RelationFindReplTupleByIndex(Relation rel, Oid idxoid,
198196

199197
isIdxSafeToSkipDuplicates = (GetRelationIdentityOrPK(rel) == idxoid);
200198

201-
InitDirtySnapshot(snap);
202-
203199
/* Build scan key. */
204200
skey_attoff = build_replindex_scan_key(skey, rel, idxrel, searchslot);
205201

206-
/* Start an index scan. */
207-
scan = index_beginscan(rel, idxrel, &snap, NULL, skey_attoff, 0);
202+
/* Start an index scan. SnapshotAny will be replaced below. */
203+
scan = index_beginscan(rel, idxrel, SnapshotAny, NULL, skey_attoff, 0);
208204

209205
retry:
210206
found = false;
211-
207+
PushActiveSnapshot(GetLatestSnapshot());
208+
/* Update the actual scan snapshot each retry */
209+
scan->xs_snapshot = GetActiveSnapshot();
212210
index_rescan(scan, skey, skey_attoff, NULL, 0);
213211

214212
/* Try to find the tuple */
@@ -229,19 +227,6 @@ RelationFindReplTupleByIndex(Relation rel, Oid idxoid,
229227

230228
ExecMaterializeSlot(outslot);
231229

232-
xwait = TransactionIdIsValid(snap.xmin) ?
233-
snap.xmin : snap.xmax;
234-
235-
/*
236-
* If the tuple is locked, wait for locking transaction to finish and
237-
* retry.
238-
*/
239-
if (TransactionIdIsValid(xwait))
240-
{
241-
XactLockTableWait(xwait, NULL, NULL, XLTW_None);
242-
goto retry;
243-
}
244-
245230
/* Found our tuple and it's not locked */
246231
found = true;
247232
break;
@@ -253,8 +238,6 @@ RelationFindReplTupleByIndex(Relation rel, Oid idxoid,
253238
TM_FailureData tmfd;
254239
TM_Result res;
255240

256-
PushActiveSnapshot(GetLatestSnapshot());
257-
258241
res = table_tuple_lock(rel, &(outslot->tts_tid), GetActiveSnapshot(),
259242
outslot,
260243
GetCurrentCommandId(false),
@@ -263,13 +246,15 @@ RelationFindReplTupleByIndex(Relation rel, Oid idxoid,
263246
0 /* don't follow updates */ ,
264247
&tmfd);
265248

266-
PopActiveSnapshot();
267-
268249
if (should_refetch_tuple(res, &tmfd))
250+
{
251+
PopActiveSnapshot();
269252
goto retry;
253+
}
270254
}
271255

272256
index_endscan(scan);
257+
PopActiveSnapshot();
273258

274259
/* Don't release lock until commit. */
275260
index_close(idxrel, NoLock);
@@ -370,23 +355,23 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
370355
{
371356
TupleTableSlot *scanslot;
372357
TableScanDesc scan;
373-
SnapshotData snap;
374358
TypeCacheEntry **eq;
375-
TransactionId xwait;
376359
bool found;
377360
TupleDesc desc PG_USED_FOR_ASSERTS_ONLY = RelationGetDescr(rel);
378361

379362
Assert(equalTupleDescs(desc, outslot->tts_tupleDescriptor));
380363

381364
eq = palloc0(sizeof(*eq) * outslot->tts_tupleDescriptor->natts);
382365

383-
/* Start a heap scan. */
384-
InitDirtySnapshot(snap);
385-
scan = table_beginscan(rel, &snap, 0, NULL);
366+
/* Start a heap scan. SnapshotAny will be replaced below. */
367+
scan = table_beginscan(rel, SnapshotAny, 0, NULL);
386368
scanslot = table_slot_create(rel, NULL);
387369

388370
retry:
389371
found = false;
372+
PushActiveSnapshot(GetLatestSnapshot());
373+
/* Update the actual scan snapshot each retry */
374+
scan->rs_snapshot = GetActiveSnapshot();
390375

391376
table_rescan(scan, NULL);
392377

@@ -399,19 +384,6 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
399384
found = true;
400385
ExecCopySlot(outslot, scanslot);
401386

402-
xwait = TransactionIdIsValid(snap.xmin) ?
403-
snap.xmin : snap.xmax;
404-
405-
/*
406-
* If the tuple is locked, wait for locking transaction to finish and
407-
* retry.
408-
*/
409-
if (TransactionIdIsValid(xwait))
410-
{
411-
XactLockTableWait(xwait, NULL, NULL, XLTW_None);
412-
goto retry;
413-
}
414-
415387
/* Found our tuple and it's not locked */
416388
break;
417389
}
@@ -422,8 +394,6 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
422394
TM_FailureData tmfd;
423395
TM_Result res;
424396

425-
PushActiveSnapshot(GetLatestSnapshot());
426-
427397
res = table_tuple_lock(rel, &(outslot->tts_tid), GetActiveSnapshot(),
428398
outslot,
429399
GetCurrentCommandId(false),
@@ -432,13 +402,16 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
432402
0 /* don't follow updates */ ,
433403
&tmfd);
434404

435-
PopActiveSnapshot();
436405

437406
if (should_refetch_tuple(res, &tmfd))
407+
{
408+
PopActiveSnapshot();
438409
goto retry;
410+
}
439411
}
440412

441413
table_endscan(scan);
414+
PopActiveSnapshot();
442415
ExecDropSingleTupleTableSlot(scanslot);
443416

444417
return found;

0 commit comments

Comments
 (0)