Commit 1b5d797c authored by Peter Eisentraut's avatar Peter Eisentraut

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: default avatarAndrey Klychkov <aaklychkov@mail.ru>
Reviewed-by: default avatarFabrízio de Royes Mello <fabriziomello@gmail.com>
Discussion: https://www.postgresql.org/message-id/flat/1531767486.432607658@f357.i.mail.ru
parent b4721f39
...@@ -926,10 +926,10 @@ ERROR: could not serialize access due to read/write dependencies among transact ...@@ -926,10 +926,10 @@ ERROR: could not serialize access due to read/write dependencies among transact
<para> <para>
Acquired by <command>VACUUM</command> (without <option>FULL</option>), Acquired by <command>VACUUM</command> (without <option>FULL</option>),
<command>ANALYZE</command>, <command>CREATE INDEX CONCURRENTLY</command>, <command>ANALYZE</command>, <command>CREATE INDEX CONCURRENTLY</command>,
<command>CREATE STATISTICS</command> and <command>CREATE STATISTICS</command>, and certain <command>ALTER
<command>ALTER TABLE VALIDATE</command> and other INDEX</command> and <command>ALTER TABLE</command> variants (for full
<command>ALTER TABLE</command> variants (for full details see details see <xref linkend="sql-alterindex"/> and <xref
<xref linkend="sql-altertable"/>). linkend="sql-altertable"/>).
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -970,7 +970,7 @@ ERROR: could not serialize access due to read/write dependencies among transact ...@@ -970,7 +970,7 @@ ERROR: could not serialize access due to read/write dependencies among transact
</para> </para>
<para> <para>
Acquired by <command>CREATE TRIGGER</command> and many forms of Acquired by <command>CREATE TRIGGER</command> and some forms of
<command>ALTER TABLE</command> (see <xref linkend="sql-altertable"/>). <command>ALTER TABLE</command> (see <xref linkend="sql-altertable"/>).
</para> </para>
</listitem> </listitem>
...@@ -1020,7 +1020,7 @@ ERROR: could not serialize access due to read/write dependencies among transact ...@@ -1020,7 +1020,7 @@ ERROR: could not serialize access due to read/write dependencies among transact
<command>CLUSTER</command>, <command>VACUUM FULL</command>, <command>CLUSTER</command>, <command>VACUUM FULL</command>,
and <command>REFRESH MATERIALIZED VIEW</command> (without and <command>REFRESH MATERIALIZED VIEW</command> (without
<option>CONCURRENTLY</option>) <option>CONCURRENTLY</option>)
commands. Many forms of <command>ALTER TABLE</command> also acquire commands. Many forms of <command>ALTER INDEX</command> and <command>ALTER TABLE</command> also acquire
a lock at this level. This is also the default lock mode for a lock at this level. This is also the default lock mode for
<command>LOCK TABLE</command> statements that do not specify <command>LOCK TABLE</command> statements that do not specify
a mode explicitly. a mode explicitly.
......
...@@ -39,7 +39,10 @@ ALTER INDEX ALL IN TABLESPACE <replaceable class="parameter">name</replaceable> ...@@ -39,7 +39,10 @@ ALTER INDEX ALL IN TABLESPACE <replaceable class="parameter">name</replaceable>
<para> <para>
<command>ALTER INDEX</command> changes the definition of an existing index. <command>ALTER INDEX</command> changes the definition of an existing index.
There are several subforms: There are several subforms described below. Note that the lock level required
may differ for each subform. An <literal>ACCESS EXCLUSIVE</literal> lock is held
unless explicitly noted. When multiple subcommands are listed, the lock
held will be the strictest one required from any subcommand.
<variablelist> <variablelist>
...@@ -53,6 +56,10 @@ ALTER INDEX ALL IN TABLESPACE <replaceable class="parameter">name</replaceable> ...@@ -53,6 +56,10 @@ ALTER INDEX ALL IN TABLESPACE <replaceable class="parameter">name</replaceable>
or <literal>EXCLUDE</literal>), the constraint is renamed as well. or <literal>EXCLUDE</literal>), the constraint is renamed as well.
There is no effect on the stored data. There is no effect on the stored data.
</para> </para>
<para>
Renaming an index acquires a <literal>SHARE UPDATE EXCLUSIVE</literal>
lock.
</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
......
...@@ -1661,14 +1661,14 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, ...@@ -1661,14 +1661,14 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u", snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u",
OIDOldHeap); OIDOldHeap);
RenameRelationInternal(newrel->rd_rel->reltoastrelid, RenameRelationInternal(newrel->rd_rel->reltoastrelid,
NewToastName, true); NewToastName, true, false);
/* ... and its valid index too. */ /* ... and its valid index too. */
snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u_index", snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u_index",
OIDOldHeap); OIDOldHeap);
RenameRelationInternal(toastidx, RenameRelationInternal(toastidx,
NewToastName, true); NewToastName, true, true);
} }
relation_close(newrel, NoLock); relation_close(newrel, NoLock);
} }
......
...@@ -3044,7 +3044,7 @@ rename_constraint_internal(Oid myrelid, ...@@ -3044,7 +3044,7 @@ rename_constraint_internal(Oid myrelid,
|| con->contype == CONSTRAINT_UNIQUE || con->contype == CONSTRAINT_UNIQUE
|| con->contype == CONSTRAINT_EXCLUSION)) || con->contype == CONSTRAINT_EXCLUSION))
/* rename the index; this renames the constraint as well */ /* rename the index; this renames the constraint as well */
RenameRelationInternal(con->conindid, newconname, false); RenameRelationInternal(con->conindid, newconname, false, true);
else else
RenameConstraintById(constraintOid, newconname); RenameConstraintById(constraintOid, newconname);
...@@ -3112,6 +3112,7 @@ RenameConstraint(RenameStmt *stmt) ...@@ -3112,6 +3112,7 @@ RenameConstraint(RenameStmt *stmt)
ObjectAddress ObjectAddress
RenameRelation(RenameStmt *stmt) RenameRelation(RenameStmt *stmt)
{ {
bool is_index = stmt->renameType == OBJECT_INDEX;
Oid relid; Oid relid;
ObjectAddress address; ObjectAddress address;
...@@ -3123,7 +3124,8 @@ RenameRelation(RenameStmt *stmt) ...@@ -3123,7 +3124,8 @@ RenameRelation(RenameStmt *stmt)
* Lock level used here should match RenameRelationInternal, to avoid lock * Lock level used here should match RenameRelationInternal, to avoid lock
* escalation. * escalation.
*/ */
relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock, relid = RangeVarGetRelidExtended(stmt->relation,
is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock,
stmt->missing_ok ? RVR_MISSING_OK : 0, stmt->missing_ok ? RVR_MISSING_OK : 0,
RangeVarCallbackForAlterRelation, RangeVarCallbackForAlterRelation,
(void *) stmt); (void *) stmt);
...@@ -3137,7 +3139,7 @@ RenameRelation(RenameStmt *stmt) ...@@ -3137,7 +3139,7 @@ RenameRelation(RenameStmt *stmt)
} }
/* Do the work */ /* Do the work */
RenameRelationInternal(relid, stmt->newname, false); RenameRelationInternal(relid, stmt->newname, false, is_index);
ObjectAddressSet(address, RelationRelationId, relid); ObjectAddressSet(address, RelationRelationId, relid);
...@@ -3148,7 +3150,7 @@ RenameRelation(RenameStmt *stmt) ...@@ -3148,7 +3150,7 @@ RenameRelation(RenameStmt *stmt)
* RenameRelationInternal - change the name of a relation * RenameRelationInternal - change the name of a relation
*/ */
void 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 targetrelation;
Relation relrelation; /* for RELATION relation */ Relation relrelation; /* for RELATION relation */
...@@ -3157,11 +3159,16 @@ RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal) ...@@ -3157,11 +3159,16 @@ RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal)
Oid namespaceId; Oid namespaceId;
/* /*
* Grab an exclusive lock on the target table, index, sequence, view, * Grab a lock on the target relation, which we will NOT release until end
* materialized view, or foreign table, which we will NOT release until * of transaction. We need at least a self-exclusive lock so that
* end of transaction. * 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); namespaceId = RelationGetNamespace(targetrelation);
/* /*
...@@ -3214,7 +3221,7 @@ RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal) ...@@ -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); relation_close(targetrelation, NoLock);
} }
...@@ -7076,7 +7083,7 @@ ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel, ...@@ -7076,7 +7083,7 @@ ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
ereport(NOTICE, ereport(NOTICE,
(errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"", (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
indexName, constraintName))); indexName, constraintName)));
RenameRelationInternal(index_oid, constraintName, false); RenameRelationInternal(index_oid, constraintName, false, true);
} }
/* Extra checks needed if making primary key */ /* Extra checks needed if making primary key */
......
...@@ -3255,7 +3255,7 @@ RenameType(RenameStmt *stmt) ...@@ -3255,7 +3255,7 @@ RenameType(RenameStmt *stmt)
* RenameRelationInternal will call RenameTypeInternal automatically. * RenameRelationInternal will call RenameTypeInternal automatically.
*/ */
if (typTup->typtype == TYPTYPE_COMPOSITE) if (typTup->typtype == TYPTYPE_COMPOSITE)
RenameRelationInternal(typTup->typrelid, newTypeName, false); RenameRelationInternal(typTup->typrelid, newTypeName, false, false);
else else
RenameTypeInternal(typeOid, newTypeName, RenameTypeInternal(typeOid, newTypeName,
typTup->typnamespace); typTup->typnamespace);
......
...@@ -67,7 +67,8 @@ extern ObjectAddress RenameConstraint(RenameStmt *stmt); ...@@ -67,7 +67,8 @@ extern ObjectAddress RenameConstraint(RenameStmt *stmt);
extern ObjectAddress RenameRelation(RenameStmt *stmt); extern ObjectAddress RenameRelation(RenameStmt *stmt);
extern void RenameRelationInternal(Oid myrelid, 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, extern void find_composite_type_dependencies(Oid typeOid,
Relation origRelation, Relation origRelation,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment