8000 Fix longstanding bug in HeapTupleSatisfiesVacuum(). · larkly/postgres-docker@6bf6e52 · GitHub
[go: up one dir, main page]

Skip to content

Commit 6bf6e52

Browse files
committed
Fix longstanding bug in HeapTupleSatisfiesVacuum().
HeapTupleSatisfiesVacuum() didn't properly discern between DELETE_IN_PROGRESS and INSERT_IN_PROGRESS for rows that have been inserted in the current transaction and deleted in a aborted subtransaction of the current backend. At the very least that caused problems for CLUSTER and CREATE INDEX in transactions that had aborting subtransactions producing rows, leading to warnings like: WARNING: concurrent delete in progress within table "..." possibly in an endless, uninterruptible, loop. Instead of treating *InProgress xmins the same as *IsCurrent ones, treat them as being distinct like the other visibility routines. As implemented this separatation can cause a behaviour change for rows that have been inserted and deleted in another, still running, transaction. HTSV will now return INSERT_IN_PROGRESS instead of DELETE_IN_PROGRESS for those. That's both, more in line with the other visibility routines and arguably more correct. The latter because a INSERT_IN_PROGRESS will make callers look at/wait for xmin, instead of xmax. The only current caller where that's possibly worse than the old behaviour is heap_prune_chain() which now won't mark the page as prunable if a row has concurrently been inserted and deleted. That's harmless enough. As a cautionary measure also insert a interrupt check before the gotos in IndexBuildHeapScan() that lead to the uninterruptible loop. There are other possible causes, like a row that several sessions try to update and all fail, for repeated loops and the cost of doing so in the retry case is low. As this bug goes back all the way to the introduction of subtransactions in 573a71a backpatch to all supported releases. Reported-By: Sandro Santilli
1 parent d661582 commit 6bf6e52

File tree

2 files changed

+19
-2
lines changed

2 files changed

+19
-2
lines changed

src/backend/catalog/index.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2100,6 +2100,7 @@ IndexBuildHeapScan(Relation heapRelation,
21002100
*/
21012101
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
21022102
XactLockTableWait(xwait);
2103+
CHECK_FOR_INTERRUPTS();
21032104
goto recheck;
21042105
}
21052106
}
@@ -2147,6 +2148,7 @@ IndexBuildHeapScan(Relation heapRelation,
21472148
*/
21482149
LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
21492150
XactLockTableWait(xwait);
2151+
CHECK_FOR_INTERRUPTS();
21502152
goto recheck;
21512153
}
21522154

src/backend/utils/time/tqual.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1116,14 +1116,29 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin,
11161116
return HEAPTUPLE_DEAD;
11171117
}
11181118
}
1119-
else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple)))
1119+
else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple)))
11201120
{
11211121
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
11221122
return HEAPTUPLE_INSERT_IN_PROGRESS;
11231123
if (tuple->t_infomask & HEAP_IS_LOCKED)
11241124
return HEAPTUPLE_INSERT_IN_PROGRESS;
11251125
/* inserted and then deleted by same xact */
1126-
return HEAPTUPLE_DELETE_IN_PROGRESS;
1126+
if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
1127+
return HEAPTUPLE_DELETE_IN_PROGRESS;
1128+
/* deleting subtransaction must have aborted */
1129+
return HEAPTUPLE_INSERT_IN_PROGRESS;
1130+
}
1131+
else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple)))
1132+
{
1133+
/*
1134+
* It'd be possible to discern between INSERT/DELETE in progress
1135+
* here by looking at xmax - but that doesn't seem beneficial for
1136+
* the majority of callers and even detrimental for some. We'd
1137+
* rather have callers look at/wait for xmin than xmax. It's
1138+
* always correct to return INSERT_IN_PROGRESS because that's
1139+
* what's happening from the view of other backends.
1140+
*/
1141+
return HEAPTUPLE_INSERT_IN_PROGRESS;
11271142
}
11281143
else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
11291144
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,

0 commit comments

Comments
 (0)
0