mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-01-12 18:34:36 +08:00
Re-think guts of DROP INDEX CONCURRENTLY.
Concurrent behaviour was flawed when using a two-step process, so add an additional phase of processing to ensure concurrency for both SELECTs and INSERT/UPDATE/DELETEs. Backpatch to 9.2 Andres Freund, tweaked by me
This commit is contained in:
parent
72a4231f0c
commit
2f0e480d02
@ -1316,6 +1316,10 @@ index_drop(Oid indexId, bool concurrent)
|
||||
* table lock strong enough to prevent all queries on the table from
|
||||
* proceeding until we commit and send out a shared-cache-inval notice
|
||||
* that will make them update their index lists.
|
||||
*
|
||||
* In the concurrent case we make sure that nobody can be looking at the
|
||||
* indexes by dropping the index in multiple steps, so we don't need a full
|
||||
* AccessExclusiveLock yet.
|
||||
*/
|
||||
heapId = IndexGetRelation(indexId, false);
|
||||
if (concurrent)
|
||||
@ -1336,7 +1340,19 @@ index_drop(Oid indexId, bool concurrent)
|
||||
|
||||
/*
|
||||
* Drop Index concurrently is similar in many ways to creating an index
|
||||
* concurrently, so some actions are similar to DefineIndex()
|
||||
* concurrently, so some actions are similar to DefineIndex() just in the
|
||||
* reverse order.
|
||||
*
|
||||
* First we unset indisvalid so queries starting afterwards don't use the
|
||||
* index to answer queries anymore. We have to keep indisready = true
|
||||
* so transactions that are still scanning the index can continue to
|
||||
* see valid index contents. E.g. when they are using READ COMMITTED mode,
|
||||
* and another transactions that started later commits makes changes and
|
||||
* commits, they need to see those new tuples in the index.
|
||||
*
|
||||
* After all transactions that could possibly have used it for queries
|
||||
* ended we can unset indisready and wait till nobody could be updating it
|
||||
* anymore.
|
||||
*/
|
||||
if (concurrent)
|
||||
{
|
||||
@ -1355,21 +1371,21 @@ index_drop(Oid indexId, bool concurrent)
|
||||
elog(ERROR, "cache lookup failed for index %u", indexId);
|
||||
indexForm = (Form_pg_index) GETSTRUCT(tuple);
|
||||
|
||||
indexForm->indisvalid = false; /* make unusable for queries */
|
||||
indexForm->indisready = false; /* make invisible to changes */
|
||||
/*
|
||||
* If indisready == true we leave it set so the index still gets
|
||||
* maintained by pre-existing transactions. We only need to ensure
|
||||
* that indisvalid is false.
|
||||
*/
|
||||
if (indexForm->indisvalid)
|
||||
{
|
||||
indexForm->indisvalid = false; /* make unusable for new queries */
|
||||
|
||||
simple_heap_update(indexRelation, &tuple->t_self, tuple);
|
||||
CatalogUpdateIndexes(indexRelation, tuple);
|
||||
simple_heap_update(indexRelation, &tuple->t_self, tuple);
|
||||
CatalogUpdateIndexes(indexRelation, tuple);
|
||||
}
|
||||
|
||||
heap_close(indexRelation, RowExclusiveLock);
|
||||
|
||||
/*
|
||||
* Invalidate the relcache for the table, so that after this
|
||||
* transaction we will refresh the index list. Forgetting just the
|
||||
* index is not enough.
|
||||
*/
|
||||
CacheInvalidateRelcache(userHeapRelation);
|
||||
|
||||
/* save lockrelid and locktag for below, then close but keep locks */
|
||||
heaprelid = userHeapRelation->rd_lockInfo.lockRelId;
|
||||
SET_LOCKTAG_RELATION(heaplocktag, heaprelid.dbId, heaprelid.relId);
|
||||
@ -1381,8 +1397,8 @@ index_drop(Oid indexId, bool concurrent)
|
||||
/*
|
||||
* For a concurrent drop, it's important to make the catalog entries
|
||||
* visible to other transactions before we drop the index. The index
|
||||
* will be marked not indisvalid, so that no one else tries to either
|
||||
* insert into it or use it for queries.
|
||||
* will be marked not indisvalid, so that no one else tries to use it
|
||||
* for queries.
|
||||
*
|
||||
* We must commit our current transaction so that the index update
|
||||
* becomes visible; then start another. Note that all the data
|
||||
@ -1428,6 +1444,66 @@ index_drop(Oid indexId, bool concurrent)
|
||||
old_lockholders++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now we are sure that nobody uses the index for queries, they just
|
||||
* might have it opened for updating it. So now we can unset
|
||||
* indisready and wait till nobody could update the index anymore.
|
||||
*/
|
||||
indexRelation = heap_open(IndexRelationId, RowExclusiveLock);
|
||||
|
||||
userHeapRelation = heap_open(heapId, ShareUpdateExclusiveLock);
|
||||
userIndexRelation = index_open(indexId, ShareUpdateExclusiveLock);
|
||||
|
||||
tuple = SearchSysCacheCopy1(INDEXRELID,
|
||||
ObjectIdGetDatum(indexId));
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
elog(ERROR, "cache lookup failed for index %u", indexId);
|
||||
indexForm = (Form_pg_index) GETSTRUCT(tuple);
|
||||
|
||||
Assert(indexForm->indisvalid == false);
|
||||
if (indexForm->indisready)
|
||||
{
|
||||
indexForm->indisready = false; /* don't update index anymore */
|
||||
|
||||
simple_heap_update(indexRelation, &tuple->t_self, tuple);
|
||||
CatalogUpdateIndexes(indexRelation, tuple);
|
||||
}
|
||||
|
||||
heap_close(indexRelation, RowExclusiveLock);
|
||||
|
||||
/*
|
||||
* Close the relations again, though still holding session lock.
|
||||
*/
|
||||
heap_close(userHeapRelation, NoLock);
|
||||
index_close(userIndexRelation, NoLock);
|
||||
|
||||
/*
|
||||
* Invalidate the relcache for the table, so that after this
|
||||
* transaction we will refresh the index list. Forgetting just the
|
||||
* index is not enough.
|
||||
*/
|
||||
CacheInvalidateRelcache(userHeapRelation);
|
||||
|
||||
/*
|
||||
* Just as with indisvalid = false we need to make sure indisready
|
||||
* is false is visible for everyone.
|
||||
*/
|
||||
CommitTransactionCommand();
|
||||
StartTransactionCommand();
|
||||
|
||||
/*
|
||||
* Wait till everyone that saw indisready = true finished so we can
|
||||
* finally really remove the index. The logic here is the same as
|
||||
* above.
|
||||
*/
|
||||
old_lockholders = GetLockConflicts(&heaplocktag, AccessExclusiveLock);
|
||||
|
||||
while (VirtualTransactionIdIsValid(*old_lockholders))
|
||||
{
|
||||
VirtualXactLock(*old_lockholders, true);
|
||||
old_lockholders++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Re-open relations to allow us to complete our actions.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user