10000 Re-think guts of DROP INDEX CONCURRENTLY. · danielcode/postgres@5da1c4b · GitHub
[go: up one dir, main page]

Skip to content

Commit 5da1c4b

Browse files
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
1 parent 0237b39 commit 5da1c4b

File tree

1 file changed

+90
-14
lines changed

1 file changed

+90
-14
lines changed

src/backend/catalog/index.c

Lines changed: 90 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1318,6 +1318,10 @@ index_drop(Oid indexId, bool concurrent)
13181318
* table lock strong enough to prevent all queries on the table from
13191319
* proceeding until we commit and send out a shared-cache-inval notice
13201320
* that will make them update their index lists.
1321+
*
1322+
* In the concurrent case we make sure that nobody can be looking at the
1323+
* indexes by dropping the index in multiple steps, so we don't need a full
1324+
* AccessExclusiveLock yet.
13211325
*/
13221326
heapId = IndexGetRelation(indexId, false);
13231327
if (concurrent)
@@ -1338,7 +1342,19 @@ index_drop(Oid indexId, bool concurrent)
13381342

13391343
/*
13401344
* Drop Index concurrently is similar in many ways to creating an index
1341-
* concurrently, so some actions are similar to DefineIndex()
1345+
* concurrently, so some actions are similar to DefineIndex() just in the
1346+
* reverse order.
1347+
*
1348+
* First we unset indisvalid so queries starting afterwards don't use the
1349+
* index to answer queries anymore. We have to keep indisready = true
1350+
* so transactions that are still scanning the index can continue to
1351+
* see valid index contents. E.g. when they are using READ COMMITTED mode,
1352+
* and another transactions that started later commits makes changes and
1353+
* commits, they need to see those new tuples in the index.
1354+
*
1355+
* After all transactions that could possibly have used it for queries
1356+
* ended we can unset indisready and wait till nobody could be updating it
1357+
* anymore.
13421358
*/
13431359
if (concurrent)
13441360
{
@@ -1357,21 +1373,21 @@ index_drop(Oid indexId, bool concurrent)
13571373
elog(ERROR, "cache lookup failed for index %u", indexId);
13581374
indexForm = (Form_pg_index) GETSTRUCT(tuple);
13591375

1360-
indexForm->indisvalid = false; /* make unusable for queries */
1361-
indexForm->indisready = false; /* make invisible to changes */
1376+
/*
1377+
* If indisready == true we leave it set so the index still gets
1378+
* maintained by pre-existing transactions. We only need to ensure
1379+
* that indisvalid is false.
1380+
*/
1381+
if (indexForm->indisvalid)
1382+
{
1383+
indexForm->indisvalid = false; /* make unusable for new queries */
13621384

1363-
simple_heap_update(indexRelation, &tuple->t_self, tuple);
1364-
CatalogUpdateIndexes(indexRelation, tuple);
1385+
simple_heap_update(indexRelation, &tuple->t_self, tuple);
1386+
CatalogUpdateIndexes(indexRelation, tuple);
1387+
}
13651388

13661389
heap_close(indexRelation, RowExclusiveLock);
13671390

1368-
/*
1369-
* Invalidate the relcache for the table, so that after this
1370-
* transaction we will refresh the index list. Forgetting just the
1371-
* index is not enough.
1372-
*/
1373-
CacheInvalidateRelcache(userHeapRelation);
1374-
13751391
/* save lockrelid and locktag for below, then close but keep locks */
13761392
heaprelid = userHeapRelation->rd_lockInfo.lockRelId;
13771393
SET_LOCKTAG_RELATION(heaplocktag, heaprelid.dbId, heaprelid.relId);
@@ -1383,8 +1399,8 @@ index_drop(Oid indexId, bool concurrent)
13831399
/*
13841400
* For a concurrent drop, it's important to make the catalog entries
13851401
* visible to other transactions before we drop the index. The index
1386-
* will be marked not indisvalid, so that no one else tries to either
1387-
* insert into it or use it for queries.
1402+
* will be marked not indisvalid, so that no one else tries to use it
1403+
* for queries.
13881404
*
13891405
* We must commit our current transaction so that the index update
13901406
* becomes visible; then start another. Note that all the data
@@ -1430,6 +1446,66 @@ index_drop(Oid indexId, bool concurrent)
14301446
old_lockholders++;
14311447
}
14321448

1449+
/*
1450+
* Now we are sure that nobody uses the index for queries, they just
1451+
* might have it opened for updating it. So now we can unset
1452+
* indisready and wait till nobody could update the index anymore.
1453+
*/
1454+
indexRelation = heap_open(IndexRelationId, RowExclusiveLock);
1455+
1456+
userHeapRelation = heap_open(heapId, ShareUpdateExclusiveLock);
1457+
userIndexRelation = index_open(indexId, ShareUpdateExclusiveLock);
1458+
1459+
tuple = SearchSysCacheCopy1(INDEXRELID,
1460+
ObjectIdGetDatum(indexId));
1461+
if (!HeapTupleIsValid(tuple))
1462+
elog(ERROR, "cache lookup failed for index %u", indexId);
1463+
indexForm = (Form_pg_index) GETSTRUCT(tuple);
1464+
1465+
Assert(indexForm->indisvalid == false);
1466+
if (indexForm->indisready)
1467+
{
1468+
indexForm->indisready = false; /* don't update index anymore */
1469+
1470+
simple_heap_update(indexRelation, &tuple->t_self, tuple);
1471+
CatalogUpdateIndexes(indexRelation, tuple);
1472+
}
1473+
1474+
heap_close(indexRelation, RowExclusiveLock);
1475+
1476+
/*
1477+
* Close the relations again, though still holding session lock.
1478+
*/
1479+
heap_close(userHeapRelation, NoLock);
1480+
index_close(userIndexRelation, NoLock);
1481+
1482+
/*
1483+
* Invalidate the relcache for the table, so that aft 57AE er this
1484+
* transaction we will refresh the index list. Forgetting just the
1485+
* index is not enough.
1486+
*/
1487+
CacheInvalidateRelcache(userHeapRelation);
1488+
1489+
/*
1490+
* Just as with indisvalid = false we need to make sure indisready
1491+
* is false is visible for everyone.
1492+
*/
1493+
CommitTransactionCommand();
1494+
StartTransactionCommand();
1495+
1496+
/*
1497+
* Wait till everyone that saw indisready = true finished so we can
1498+
* finally really remove the index. The logic here is the same as
1499+
* above.
1500+
*/
1501+
old_lockholders = GetLockConflicts(&heaplocktag, AccessExclusiveLock);
1502+
1503+
while (VirtualTransactionIdIsValid(*old_lockholders))
1504+
{
1505+
VirtualXactLock(*old_lockholders, true);
1506+
old_lockholders++;
1507+
}
1508+
14331509
/*
14341510
* Re-open relations to allow us to complete our actions.
14351511
*

0 commit comments

Comments
 (0)
0