Skip to content

Commit ebcbc8c

Browse files
committed
Avoid changing an index's indcheckxmin horizon during REINDEX.
There can never be a need to push the indcheckxmin horizon forward, since any HOT chains that are actually broken with respect to the index must pre-date its original creation. So we can just avoid changing pg_index altogether during a REINDEX operation. This offers a cleaner solution than my previous patch for the problem found a few days ago that we mustn't try to update pg_index while we are reindexing it. System catalog indexes will always be created with indcheckxmin = false during initdb, and with this modified code we should never try to change their pg_index entries. This avoids special-casing system catalogs as the former patch did, and should provide a performance benefit for many cases where REINDEX formerly caused an index to be considered unusable for a short time. Back-patch to 8.3 to cover all versions containing HOT. Note that this patch changes the API for index_build(), but I believe it is unlikely that any add-on code is calling that directly.
1 parent 1da9966 commit ebcbc8c

File tree

6 files changed

+42
-11
lines changed

6 files changed

+42
-11
lines changed

src/backend/bootstrap/bootstrap.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1127,7 +1127,7 @@ build_indices(void)
11271127
heap = heap_open(ILHead->il_heap, NoLock);
11281128
ind = index_open(ILHead->il_ind, NoLock);
11291129

1130-
index_build(heap, ind, ILHead->il_info, false);
1130+
index_build(heap, ind, ILHead->il_info, false, false);
11311131

11321132
index_close(ind, NoLock);
11331133
heap_close(heap, NoLock);

src/backend/catalog/heap.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2451,7 +2451,7 @@ RelationTruncateIndexes(Relation heapRelation)
24512451

24522452
/* Initialize the index and rebuild */
24532453
/* Note: we do not need to re-establish pkey setting */
2454-
index_build(heapRelation, currentIndex, indexInfo, false);
2454+
index_build(heapRelation, currentIndex, indexInfo, false, true);
24552455

24562456
/* We're done with this index */
24572457
index_close(currentIndex, NoLock);

src/backend/catalog/index.c

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -957,7 +957,7 @@ index_create(Oid heapRelationId,
957957
}
958958
else
959959
{
960-
index_build(heapRelation, indexRelation, indexInfo, isprimary);
960+
index_build(heapRelation, indexRelation, indexInfo, isprimary, false);
961961
}
962962

963963
/*
@@ -1389,8 +1389,11 @@ index_update_stats(Relation rel,
13891389
* entries of the index and heap relation as needed, using statistics
13901390
* returned by ambuild as well as data passed by the caller.
13911391
*
1392-
* Note: when reindexing an existing index, isprimary can be false;
1393-
* the index is already properly marked and need not be re-marked.
1392+
* isprimary tells whether to mark the index as a primary-key index.
1393+
* isreindex indicates we are recreating a previously-existing index.
1394+
*
1395+
* Note: when reindexing an existing index, isprimary can be false even if
1396+
* the index is a PK; it's already properly marked and need not be re-marked.
13941397
*
13951398
* Note: before Postgres 8.2, the passed-in heap and index Relations
13961399
* were automatically closed by this routine. This is no longer the case.
@@ -1400,7 +1403,8 @@ void
14001403
index_build(Relation heapRelation,
14011404
Relation indexRelation,
14021405
IndexInfo *indexInfo,
1403-
bool isprimary)
1406+
bool isprimary,
1407+
bool isreindex)
14041408
{
14051409
RegProcedure procedure;
14061410
IndexBuildResult *stats;
@@ -1454,8 +1458,15 @@ index_build(Relation heapRelation,
14541458
* If we found any potentially broken HOT chains, mark the index as not
14551459
* being usable until the current transaction is below the event horizon.
14561460
* See src/backend/access/heap/README.HOT for discussion.
1457-
*/
1458-
if (indexInfo->ii_BrokenHotChain)
1461+
*
1462+
* However, when reindexing an existing index, we should do nothing here.
1463+
* Any HOT chains that are broken with respect to the index must predate
1464+
* the index's original creation, so there is no need to change the
1465+
* index's usability horizon. Moreover, we *must not* try to change
1466+
* the index's pg_index entry while reindexing pg_index itself, and this
1467+
* optimization nicely prevents that.
1468+
*/
1469+
if (indexInfo->ii_BrokenHotChain && !isreindex)
14591470
{
14601471
Oid indexId = RelationGetRelid(indexRelation);
14611472
Relation pg_index;
@@ -1470,6 +1481,9 @@ index_build(Relation heapRelation,
14701481
elog(ERROR, "cache lookup failed for index %u", indexId);
14711482
indexForm = (Form_pg_index) GETSTRUCT(indexTuple);
14721483

1484+
/* If it's a new index, indcheckxmin shouldn't be set ... */
1485+
Assert(!indexForm->indcheckxmin);
1486+
14731487
indexForm->indcheckxmin = true;
14741488
simple_heap_update(pg_index, &indexTuple->t_self, indexTuple);
14751489
CatalogUpdateIndexes(pg_index, indexTuple);
@@ -2461,7 +2475,7 @@ reindex_index(Oid indexId, bool skip_constraint_checks)
24612475

24622476
/* Initialize the index and rebuild */
24632477
/* Note: we do not need to re-establish pkey setting */
2464-
index_build(heapRelation, iRel, indexInfo, false);
2478+
index_build(heapRelation, iRel, indexInfo, false, true);
24652479
}
24662480
PG_CATCH();
24672481
{
@@ -2481,6 +2495,16 @@ reindex_index(Oid indexId, bool skip_constraint_checks)
24812495
* We can also reset indcheckxmin, because we have now done a
24822496
* non-concurrent index build, *except* in the case where index_build
24832497
* found some still-broken HOT chains.
2498+
*
2499+
* Note that it is important to not update the pg_index entry if we don't
2500+
* have to, because updating it will move the index's usability horizon
2501+
* (recorded as the tuple's xmin value) if indcheckxmin is true. We don't
2502+
* really want REINDEX to move the usability horizon forward ever, but we
2503+
* have no choice if we are to fix indisvalid or indisready. Of course,
2504+
* clearing indcheckxmin eliminates the issue, so we're happy to do that
2505+
* if we can. Another reason for caution here is that while reindexing
2506+
* pg_index itself, we must not try to update it. We assume that
2507+
* pg_index's indexes will always have these flags in their clean state.
24842508
*/
24852509
if (!skipped_constraint)
24862510
{

src/backend/commands/cluster.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1360,6 +1360,12 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
13601360
* advantage to the other order anyway because this is all transactional,
13611361
* so no chance to reclaim disk space before commit. We do not need a
13621362
* final CommandCounterIncrement() because reindex_relation does it.
1363+
*
1364+
* Note: because index_build is called via reindex_relation, it will never
1365+
* set indcheckxmin true for the indexes. This is OK even though in some
1366+
* sense we are building new indexes rather than rebuilding existing ones,
1367+
* because the new heap won't contain any HOT chains at all, let alone
1368+
* broken ones, so it can't be necessary to set indcheckxmin.
13631369
*/
13641370
reindex_flags = REINDEX_SUPPRESS_INDEX_USE;
13651371
if (check_constraints)

src/backend/commands/indexcmds.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,7 @@ DefineIndex(RangeVar *heapRelation,
584584
indexInfo->ii_BrokenHotChain = false;
585585

586586
/* Now build the index */
587-
index_build(rel, indexRelation, indexInfo, primary);
587+
index_build(rel, indexRelation, indexInfo, primary, false);
588588

589589
/* Close both the relations, but keep the locks */
590590
heap_close(rel, NoLock);

src/include/catalog/index.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ extern void FormIndexDatum(IndexInfo *indexInfo,
5959
extern void index_build(Relation heapRelation,
6060
Relation indexRelation,
6161
IndexInfo *indexInfo,
62-
bool isprimary);
62+
bool isprimary,
63+
bool isreindex);
6364

6465
extern double IndexBuildHeapScan(Relation heapRelation,
6566
Relation indexRelation,

0 commit comments

Comments
 (0)