diff options
author | Peter Eisentraut | 2018-10-25 07:33:17 +0000 |
---|---|---|
committer | Peter Eisentraut | 2018-11-14 16:09:54 +0000 |
commit | 1b5d797cd4f7133ff0d18e123fcf41c67a5a7b0b (patch) | |
tree | 955a8bd049710d8e3df7c99bfcb49f07b074535c /src | |
parent | b4721f39505b56dd7b556aef5428a0850230ca59 (diff) |
Lower lock level for renaming indexes
Change lock level for renaming index (either ALTER INDEX or implicitly
via some other commands) from AccessExclusiveLock to
ShareUpdateExclusiveLock.
One reason we need a strong lock for relation renaming is that the
name change causes a rebuild of the relcache entry. Concurrent
sessions that have the relation open might not be able to handle the
relcache entry changing underneath them. Therefore, we need to lock
the relation in a way that no one can have the relation open
concurrently. But for indexes, the relcache handles reloads specially
in RelationReloadIndexInfo() in a way that keeps changes in the
relcache entry to a minimum. As long as no one keeps pointers to
rd_amcache and rd_options around across possible relcache flushes,
which is the case, this ought to be safe.
We also want to use a self-exclusive lock for correctness, so that
concurrent DDL doesn't overwrite the rename if they start updating
while still seeing the old version. Therefore, we use
ShareUpdateExclusiveLock, which is already used by other DDL commands
that want to operate in a concurrent manner.
The reason this is interesting at all is that renaming an index is a
typical part of a concurrent reindexing workflow (CREATE INDEX
CONCURRENTLY new + DROP INDEX CONCURRENTLY old + rename back). And
indeed a future built-in REINDEX CONCURRENTLY might rely on the ability
to do concurrent renames as well.
Reviewed-by: Andrey Klychkov <[email protected]>
Reviewed-by: FabrÃzio de Royes Mello <[email protected]>
Discussion: https://www.postgresql.org/message-id/flat/[email protected]
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/commands/cluster.c | 4 | ||||
-rw-r--r-- | src/backend/commands/tablecmds.c | 27 | ||||
-rw-r--r-- | src/backend/commands/typecmds.c | 2 | ||||
-rw-r--r-- | src/include/commands/tablecmds.h | 3 |
4 files changed, 22 insertions, 14 deletions
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 68be4709771..5ecd2565b4d 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -1661,14 +1661,14 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u", OIDOldHeap); RenameRelationInternal(newrel->rd_rel->reltoastrelid, - NewToastName, true); + NewToastName, true, false); /* ... and its valid index too. */ snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u_index", OIDOldHeap); RenameRelationInternal(toastidx, - NewToastName, true); + NewToastName, true, true); } relation_close(newrel, NoLock); } diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 946119fa860..73da6c39c22 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -3044,7 +3044,7 @@ rename_constraint_internal(Oid myrelid, || con->contype == CONSTRAINT_UNIQUE || con->contype == CONSTRAINT_EXCLUSION)) /* rename the index; this renames the constraint as well */ - RenameRelationInternal(con->conindid, newconname, false); + RenameRelationInternal(con->conindid, newconname, false, true); else RenameConstraintById(constraintOid, newconname); @@ -3112,6 +3112,7 @@ RenameConstraint(RenameStmt *stmt) ObjectAddress RenameRelation(RenameStmt *stmt) { + bool is_index = stmt->renameType == OBJECT_INDEX; Oid relid; ObjectAddress address; @@ -3123,7 +3124,8 @@ RenameRelation(RenameStmt *stmt) * Lock level used here should match RenameRelationInternal, to avoid lock * escalation. */ - relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock, + relid = RangeVarGetRelidExtended(stmt->relation, + is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock, stmt->missing_ok ? RVR_MISSING_OK : 0, RangeVarCallbackForAlterRelation, (void *) stmt); @@ -3137,7 +3139,7 @@ RenameRelation(RenameStmt *stmt) } /* Do the work */ - RenameRelationInternal(relid, stmt->newname, false); + RenameRelationInternal(relid, stmt->newname, false, is_index); ObjectAddressSet(address, RelationRelationId, relid); @@ -3148,7 +3150,7 @@ RenameRelation(RenameStmt *stmt) * RenameRelationInternal - change the name of a relation */ void -RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal) +RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index) { Relation targetrelation; Relation relrelation; /* for RELATION relation */ @@ -3157,11 +3159,16 @@ RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal) Oid namespaceId; /* - * Grab an exclusive lock on the target table, index, sequence, view, - * materialized view, or foreign table, which we will NOT release until - * end of transaction. + * Grab a lock on the target relation, which we will NOT release until end + * of transaction. We need at least a self-exclusive lock so that + * concurrent DDL doesn't overwrite the rename if they start updating + * while still seeing the old version. The lock also guards against + * triggering relcache reloads in concurrent sessions, which might not + * handle this information changing under them. For indexes, we can use a + * reduced lock level because RelationReloadIndexInfo() handles indexes + * specially. */ - targetrelation = relation_open(myrelid, AccessExclusiveLock); + targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock); namespaceId = RelationGetNamespace(targetrelation); /* @@ -3214,7 +3221,7 @@ RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal) } /* - * Close rel, but keep exclusive lock! + * Close rel, but keep lock! */ relation_close(targetrelation, NoLock); } @@ -7076,7 +7083,7 @@ ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel, ereport(NOTICE, (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"", indexName, constraintName))); - RenameRelationInternal(index_oid, constraintName, false); + RenameRelationInternal(index_oid, constraintName, false, true); } /* Extra checks needed if making primary key */ diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 66f7c577267..285a0be6436 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -3255,7 +3255,7 @@ RenameType(RenameStmt *stmt) * RenameRelationInternal will call RenameTypeInternal automatically. */ if (typTup->typtype == TYPTYPE_COMPOSITE) - RenameRelationInternal(typTup->typrelid, newTypeName, false); + RenameRelationInternal(typTup->typrelid, newTypeName, false, false); else RenameTypeInternal(typeOid, newTypeName, typTup->typnamespace); diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h index 138de84e832..2afcd5be442 100644 --- a/src/include/commands/tablecmds.h +++ b/src/include/commands/tablecmds.h @@ -67,7 +67,8 @@ extern ObjectAddress RenameConstraint(RenameStmt *stmt); extern ObjectAddress RenameRelation(RenameStmt *stmt); extern void RenameRelationInternal(Oid myrelid, - const char *newrelname, bool is_internal); + const char *newrelname, bool is_internal, + bool is_index); extern void find_composite_type_dependencies(Oid typeOid, Relation origRelation, |