On 2020-Nov-26, Alvaro Herrera wrote: > So let's discuss the next step in this series: what to do about REINDEX > CONCURRENTLY.
> [...] ... as in the attached.
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index ca24620fd0..9c1c0aad3e 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -385,7 +385,7 @@ CompareOpclassOptions(Datum *opts1, Datum *opts2, int natts) * lazy VACUUMs, because they won't be fazed by missing index entries * either. (Manual ANALYZEs, however, can't be excluded because they * might be within transactions that are going to do arbitrary operations - * later.) Processes running CREATE INDEX CONCURRENTLY + * later.) Processes running CREATE INDEX CONCURRENTLY or REINDEX CONCURRENTLY * on indexes that are neither expressional nor partial are also safe to * ignore, since we know that those processes won't examine any data * outside the table they're indexing. @@ -1564,9 +1564,11 @@ DefineIndex(Oid relationId, CommitTransactionCommand(); StartTransactionCommand(); - /* Tell concurrent index builds to ignore us, if index qualifies */ - if (safe_index) - set_indexsafe_procflags(); + /* + * This transaction doesn't need to set the PROC_IN_SAFE_IC flag, because + * it only takes a snapshot to do some catalog manipulations, after the + * wait is over. + */ /* We should now definitely not be advertising any xmin. */ Assert(MyProc->xmin == InvalidTransactionId); @@ -3020,6 +3022,13 @@ ReindexMultipleInternal(List *relids, int options) * concerns, so there's no need for the more complicated concurrent build, * anyway, and a non-concurrent reindex is more efficient. */ + +typedef struct reindex_idx +{ + Oid indexId; + bool safe; +} reindex_idx; + static bool ReindexRelationConcurrently(Oid relationOid, int options) { @@ -3132,10 +3141,15 @@ ReindexRelationConcurrently(Oid relationOid, int options) get_rel_name(cellOid)))); else { + reindex_idx *idx; + /* Save the list of relation OIDs in private context */ oldcontext = MemoryContextSwitchTo(private_context); - indexIds = lappend_oid(indexIds, cellOid); + idx = palloc(sizeof(reindex_idx)); + idx->indexId = cellOid; + + indexIds = lappend(indexIds, idx); MemoryContextSwitchTo(oldcontext); } @@ -3172,13 +3186,17 @@ ReindexRelationConcurrently(Oid relationOid, int options) get_rel_name(cellOid)))); else { + reindex_idx *idx; + /* * Save the list of relation OIDs in private * context */ oldcontext = MemoryContextSwitchTo(private_context); - indexIds = lappend_oid(indexIds, cellOid); + idx = palloc(sizeof(reindex_idx)); + idx->indexId = cellOid; + indexIds = lappend(indexIds, idx); MemoryContextSwitchTo(oldcontext); } @@ -3197,6 +3215,7 @@ ReindexRelationConcurrently(Oid relationOid, int options) Oid heapId = IndexGetRelation(relationOid, (options & REINDEXOPT_MISSING_OK) != 0); Relation heapRelation; + reindex_idx *idx; /* if relation is missing, leave */ if (!OidIsValid(heapId)) @@ -3247,7 +3266,9 @@ ReindexRelationConcurrently(Oid relationOid, int options) * Save the list of relation OIDs in private context. Note * that invalid indexes are allowed here. */ - indexIds = lappend_oid(indexIds, relationOid); + idx = palloc(sizeof(reindex_idx)); + idx->indexId = relationOid; + indexIds = lappend(indexIds, idx); MemoryContextSwitchTo(oldcontext); break; @@ -3306,8 +3327,10 @@ ReindexRelationConcurrently(Oid relationOid, int options) foreach(lc, indexIds) { char *concurrentName; - Oid indexId = lfirst_oid(lc); + reindex_idx *index = lfirst(lc); + Oid indexId = index->indexId; Oid newIndexId; + reindex_idx *newidx; Relation indexRel; Relation heapRel; Relation newIndexRel; @@ -3347,12 +3370,20 @@ ReindexRelationConcurrently(Oid relationOid, int options) */ newIndexRel = index_open(newIndexId, ShareUpdateExclusiveLock); + /* determine safety of this index for set_indexsafe_procflags */ + index->safe = (newIndexRel->rd_indexprs == NIL && + newIndexRel->rd_indpred == NIL); + /* * Save the list of OIDs and locks in private context */ oldcontext = MemoryContextSwitchTo(private_context); - newIndexIds = lappend_oid(newIndexIds, newIndexId); + newidx = palloc(sizeof(reindex_idx)); + newidx->indexId = newIndexId; + newidx->safe = index->safe; + + newIndexIds = lappend(newIndexIds, newidx); /* * Save lockrelid to protect each relation from drop then close @@ -3416,6 +3447,11 @@ ReindexRelationConcurrently(Oid relationOid, int options) CommitTransactionCommand(); StartTransactionCommand(); + /* + * Because we don't take a snapshot in this transaction, there's no + * need to set the PROC_IN_SAFE_IC flag here. + */ + /* * Phase 2 of REINDEX CONCURRENTLY * @@ -3434,7 +3470,8 @@ ReindexRelationConcurrently(Oid relationOid, int options) foreach(lc, newIndexIds) { Relation newIndexRel; - Oid newIndexId = lfirst_oid(lc); + reindex_idx *idx = lfirst(lc); + Oid newIndexId = idx->indexId; Oid heapId; Oid indexam; @@ -3448,6 +3485,10 @@ ReindexRelationConcurrently(Oid relationOid, int options) */ CHECK_FOR_INTERRUPTS(); + /* Tell concurrent indexing to ignore us, if index qualifies */ + if (idx->safe) + set_indexsafe_procflags(); + /* Set ActiveSnapshot since functions in the indexes may need it */ PushActiveSnapshot(GetTransactionSnapshot()); @@ -3477,8 +3518,14 @@ ReindexRelationConcurrently(Oid relationOid, int options) PopActiveSnapshot(); CommitTransactionCommand(); } + StartTransactionCommand(); + /* + * Because we don't take a snapshot in this transaction, there's no + * need to set the PROC_IN_SAFE_IC flag here. + */ + /* * Phase 3 of REINDEX CONCURRENTLY * @@ -3494,7 +3541,8 @@ ReindexRelationConcurrently(Oid relationOid, int options) foreach(lc, newIndexIds) { - Oid newIndexId = lfirst_oid(lc); + reindex_idx *idx = lfirst(lc); + Oid newIndexId = idx->indexId; Oid heapId; TransactionId limitXmin; Snapshot snapshot; @@ -3510,6 +3558,10 @@ ReindexRelationConcurrently(Oid relationOid, int options) */ CHECK_FOR_INTERRUPTS(); + /* Tell concurrent indexing to ignore us, if index qualifies */ + if (idx->safe) + set_indexsafe_procflags(); + /* * Take the "reference snapshot" that will be used by validate_index() * to filter candidate tuples. @@ -3552,6 +3604,9 @@ ReindexRelationConcurrently(Oid relationOid, int options) * To ensure no deadlocks, we must commit and start yet another * transaction, and do our wait before any snapshot has been taken in * it. + * + * Because we don't take a snapshot in this transaction, there's no + * need to set the PROC_IN_SAFE_IC flag here. */ CommitTransactionCommand(); StartTransactionCommand(); @@ -3582,11 +3637,20 @@ ReindexRelationConcurrently(Oid relationOid, int options) StartTransactionCommand(); + /* + * Because this transaction only does catalog manipulations and doesn't + * do any index operations, we can set the PROC_IN_SAFE_IC flag here + * unconditionally. + */ + set_indexsafe_procflags(); + forboth(lc, indexIds, lc2, newIndexIds) { char *oldName; - Oid oldIndexId = lfirst_oid(lc); - Oid newIndexId = lfirst_oid(lc2); + reindex_idx *oldidx = lfirst(lc); + reindex_idx *newidx = lfirst(lc2); + Oid oldIndexId = oldidx->indexId; + Oid newIndexId = newidx->indexId; Oid heapId; /* @@ -3632,6 +3696,12 @@ ReindexRelationConcurrently(Oid relationOid, int options) CommitTransactionCommand(); StartTransactionCommand(); + /* + * This transaction doesn't need to set the PROC_IN_SAFE_IC flag, because + * it only takes a snapshot to do some catalog manipulations, after the + * wait is over. + */ + /* * Phase 5 of REINDEX CONCURRENTLY * @@ -3646,7 +3716,8 @@ ReindexRelationConcurrently(Oid relationOid, int options) foreach(lc, indexIds) { - Oid oldIndexId = lfirst_oid(lc); + reindex_idx *idx = lfirst(lc); + Oid oldIndexId = idx->indexId; Oid heapId; /* @@ -3664,6 +3735,12 @@ ReindexRelationConcurrently(Oid relationOid, int options) CommitTransactionCommand(); StartTransactionCommand(); + /* + * This transaction doesn't need to set the PROC_IN_SAFE_IC flag, because + * it only takes a snapshot to do some catalog manipulations, after all + * the waiting has been completed. + */ + /* * Phase 6 of REINDEX CONCURRENTLY * @@ -3681,7 +3758,8 @@ ReindexRelationConcurrently(Oid relationOid, int options) foreach(lc, indexIds) { - Oid oldIndexId = lfirst_oid(lc); + reindex_idx *idx = lfirst(lc); + Oid oldIndexId = idx->indexId; ObjectAddress object; object.classId = RelationRelationId; @@ -3728,7 +3806,8 @@ ReindexRelationConcurrently(Oid relationOid, int options) { foreach(lc, newIndexIds) { - Oid indOid = lfirst_oid(lc); + reindex_idx *idx = lfirst(lc); + Oid indOid = idx->indexId; ereport(INFO, (errmsg("index \"%s.%s\" was reindexed", diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h index e77f76ae8a..e1a6bc5170 100644 --- a/src/include/storage/proc.h +++ b/src/include/storage/proc.h @@ -54,6 +54,7 @@ struct XidCache #define PROC_IS_AUTOVACUUM 0x01 /* is it an autovac worker? */ #define PROC_IN_VACUUM 0x02 /* currently running lazy vacuum */ #define PROC_IN_SAFE_IC 0x04 /* currently running CREATE INDEX + * CONCURRENTLY or REINDEX * CONCURRENTLY on non-expressional, * non-partial index */ #define PROC_VACUUM_FOR_WRAPAROUND 0x08 /* set by autovac only */