Skip to content

Commit 520bcd9

Browse files
committed
Fix bugs in indexing of in-doubt HOT-updated tuples.
If we find a DELETE_IN_PROGRESS HOT-updated tuple, it is impossible to know whether to index it or not except by waiting to see if the deleting transaction commits. If it doesn't, the tuple might again be LIVE, meaning we have to index it. So wait and recheck in that case. Also, we must not rely on ii_BrokenHotChain to decide that it's possible to omit tuples from the index. That could result in omitting tuples that we need, particularly in view of yesterday's fixes to not necessarily set indcheckxmin (but it's broken even without that, as per my analysis today). Since this is just an extremely marginal performance optimization, dropping the test shouldn't hurt. These cases are only expected to happen in system catalogs (they're possible there due to early release of RowExclusiveLock in most catalog-update code paths). Since reindexing of a system catalog isn't a particularly performance-critical operation anyway, there's no real need to be concerned about possible performance degradation from these changes. The worst aspects of this bug were introduced in 9.0 --- 8.x will always wait out a DELETE_IN_PROGRESS tuple. But I think dropping index entries on the strength of ii_BrokenHotChain is dangerous even without that, so back-patch removal of that optimization to 8.3 and 8.4.
1 parent 9ad7e15 commit 520bcd9

File tree

1 file changed

+42
-20
lines changed

1 file changed

+42
-20
lines changed

src/backend/catalog/index.c

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1840,8 +1840,9 @@ index_build(Relation heapRelation,
18401840
*
18411841
* A side effect is to set indexInfo->ii_BrokenHotChain to true if we detect
18421842
* any potentially broken HOT chains. Currently, we set this if there are
1843-
* any RECENTLY_DEAD entries in a HOT chain, without trying very hard to
1844-
* detect whether they're really incompatible with the chain tip.
1843+
* any RECENTLY_DEAD or DELETE_IN_PROGRESS entries in a HOT chain, without
1844+
* trying very hard to detect whether they're really incompatible with the
1845+
* chain tip.
18451846
*/
18461847
double
18471848
IndexBuildHeapScan(Relation heapRelation,
@@ -1953,8 +1954,14 @@ IndexBuildHeapScan(Relation heapRelation,
19531954
* buffer continuously while visiting the page, so no pruning
19541955
* operation can occur either.
19551956
*
1957+
* Also, although our opinions about tuple liveness could change while
1958+
* we scan the page (due to concurrent transaction commits/aborts),
1959+
* the chain root locations won't, so this info doesn't need to be
1960+
* rebuilt after waiting for another transaction.
1961+
*
19561962
* Note the implied assumption that there is no more than one live
1957-
* tuple per HOT-chain ...
1963+
* tuple per HOT-chain --- else we could create more than one index
1964+
* entry pointing to the same root tuple.
19581965
*/
19591966
if (scan->rs_cblock != root_blkno)
19601967
{
@@ -2008,20 +2015,13 @@ IndexBuildHeapScan(Relation heapRelation,
20082015
* the live tuple at the end of the HOT-chain. Since this
20092016
* breaks semantics for pre-existing snapshots, mark the
20102017
* index as unusable for them.
2011-
*
2012-
* If we've already decided that the index will be unsafe
2013-
* for old snapshots, we may as well stop indexing
2014-
* recently-dead tuples, since there's no longer any
2015-
* point.
20162018
*/
20172019
if (HeapTupleIsHotUpdated(heapTuple))
20182020
{
20192021
indexIt = false;
20202022
/* mark the index as unsafe for old snapshots */
20212023
indexInfo->ii_BrokenHotChain = true;
20222024
}
2023-
else if (indexInfo->ii_BrokenHotChain)
2024-
indexIt = false;
20252025
else
20262026
indexIt = true;
20272027
/* In any case, exclude the tuple from unique-checking */
@@ -2071,7 +2071,8 @@ IndexBuildHeapScan(Relation heapRelation,
20712071
case HEAPTUPLE_DELETE_IN_PROGRESS:
20722072

20732073
/*
2074-
* Similar situation to INSERT_IN_PROGRESS case.
2074+
* As with INSERT_IN_PROGRESS case, this is unexpected
2075+
* unless it's our own deletion or a system catalog.
20752076
*/
20762077
Assert(!(heapTuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
20772078
xwait = HeapTupleHeaderGetXmax(heapTuple->t_data);
@@ -2086,8 +2087,17 @@ IndexBuildHeapScan(Relation heapRelation,
20862087
* the tuple is dead could lead to missing a
20872088
* uniqueness violation. In that case we wait for the
20882089
* deleting transaction to finish and check again.
2090+
*
2091+
* Also, if it's a HOT-updated tuple, we should not
2092+
* index it but rather the live tuple at the end of
2093+
* the HOT-chain. However, the deleting transaction
2094+
* could abort, possibly leaving this tuple as live
2095+
* after all, in which case it has to be indexed. The
2096+
* only way to know what to do is to wait for the
2097+
* deleting transaction to finish and check again.
20892098
*/
2090-
if (checking_uniqueness)
2099+
if (checking_uniqueness ||
2100+
HeapTupleIsHotUpdated(heapTuple))
20912101
{
20922102
/*
20932103
* Must drop the lock on the buffer before we wait
@@ -2096,22 +2106,34 @@ IndexBuildHeapScan(Relation heapRelation,
20962106
XactLockTableWait(xwait);
20972107
goto recheck;
20982108
}
2099-
}
21002109

2101-
/*
2102-
* Otherwise, we have to treat these tuples just like
2103-
* RECENTLY_DELETED ones.
2104-
*/
2105-
if (HeapTupleIsHotUpdated(heapTuple))
2110+
/*
2111+
* Otherwise index it but don't check for uniqueness,
2112+
* the same as a RECENTLY_DEAD tuple.
2113+
*/
2114+
indexIt = true;
2115+
}
2116+
else if (HeapTupleIsHotUpdated(heapTuple))
21062117
{
2118+
/*
2119+
* It's a HOT-updated tuple deleted by our own xact.
2120+
* We can assume the deletion will commit (else the
2121+
* index contents don't matter), so treat the same
2122+
* as RECENTLY_DEAD HOT-updated tuples.
2123+
*/
21072124
indexIt = false;
21082125
/* mark the index as unsafe for old snapshots */
21092126
indexInfo->ii_BrokenHotChain = true;
21102127
}
2111-
else if (indexInfo->ii_BrokenHotChain)
2112-
indexIt = false;
21132128
else
2129+
{
2130+
/*
2131+
* It's a regular tuple deleted by our own xact.
2132+
* Index it but don't check for uniqueness, the same
2133+
* as a RECENTLY_DEAD tuple.
2134+
*/
21142135
indexIt = true;
2136+
}
21152137
/* In any case, exclude the tuple from unique-checking */
21162138
tupleIsAlive = false;
21172139
break;

0 commit comments

Comments
 (0)