Skip to content

Commit 1b5d797

Browse files
committed
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 <aaklychkov@mail.ru> Reviewed-by: Fabrízio de Royes Mello <fabriziomello@gmail.com> Discussion: https://www.postgresql.org/message-id/flat/1531767486.432607658@f357.i.mail.ru
1 parent b4721f3 commit 1b5d797

File tree

6 files changed

+36
-21
lines changed

6 files changed

+36
-21
lines changed

doc/src/sgml/mvcc.sgml

+6-6
Original file line numberDiff line numberDiff line change
@@ -926,10 +926,10 @@ ERROR: could not serialize access due to read/write dependencies among transact
926926
<para>
927927
Acquired by <command>VACUUM</command> (without <option>FULL</option>),
928928
<command>ANALYZE</command>, <command>CREATE INDEX CONCURRENTLY</command>,
929-
<command>CREATE STATISTICS</command> and
930-
<command>ALTER TABLE VALIDATE</command> and other
931-
<command>ALTER TABLE</command> variants (for full details see
932-
<xref linkend="sql-altertable"/>).
929+
<command>CREATE STATISTICS</command>, and certain <command>ALTER
930+
INDEX</command> and <command>ALTER TABLE</command> variants (for full
931+
details see <xref linkend="sql-alterindex"/> and <xref
932+
linkend="sql-altertable"/>).
933933
</para>
934934
</listitem>
935935
</varlistentry>
@@ -970,7 +970,7 @@ ERROR: could not serialize access due to read/write dependencies among transact
970970
</para>
971971

972972
<para>
973-
Acquired by <command>CREATE TRIGGER</command> and many forms of
973+
Acquired by <command>CREATE TRIGGER</command> and some forms of
974974
<command>ALTER TABLE</command> (see <xref linkend="sql-altertable"/>).
975975
</para>
976976
</listitem>
@@ -1020,7 +1020,7 @@ ERROR: could not serialize access due to read/write dependencies among transact
10201020
<command>CLUSTER</command>, <command>VACUUM FULL</command>,
10211021
and <command>REFRESH MATERIALIZED VIEW</command> (without
10221022
<option>CONCURRENTLY</option>)
1023-
commands. Many forms of <command>ALTER TABLE</command> also acquire
1023+
commands. Many forms of <command>ALTER INDEX</command> and <command>ALTER TABLE</command> also acquire
10241024
a lock at this level. This is also the default lock mode for
10251025
<command>LOCK TABLE</command> statements that do not specify
10261026
a mode explicitly.

doc/src/sgml/ref/alter_index.sgml

+8-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@ ALTER INDEX ALL IN TABLESPACE <replaceable class="parameter">name</replaceable>
3939

4040
<para>
4141
<command>ALTER INDEX</command> changes the definition of an existing index.
42-
There are several subforms:
42+
There are several subforms described below. Note that the lock level required
43+
may differ for each subform. An <literal>ACCESS EXCLUSIVE</literal> lock is held
44+
unless explicitly noted. When multiple subcommands are listed, the lock
45+
held will be the strictest one required from any subcommand.
4346

4447
<variablelist>
4548

@@ -53,6 +56,10 @@ ALTER INDEX ALL IN TABLESPACE <replaceable class="parameter">name</replaceable>
5356
or <literal>EXCLUDE</literal>), the constraint is renamed as well.
5457
There is no effect on the stored data.
5558
</para>
59+
<para>
60+
Renaming an index acquires a <literal>SHARE UPDATE EXCLUSIVE</literal>
61+
lock.
62+
</para>
5663
</listitem>
5764
</varlistentry>
5865

src/backend/commands/cluster.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -1661,14 +1661,14 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
16611661
snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u",
16621662
OIDOldHeap);
16631663
RenameRelationInternal(newrel->rd_rel->reltoastrelid,
1664-
NewToastName, true);
1664+
NewToastName, true, false);
16651665

16661666
/* ... and its valid index too. */
16671667
snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u_index",
16681668
OIDOldHeap);
16691669

16701670
RenameRelationInternal(toastidx,
1671-
NewToastName, true);
1671+
NewToastName, true, true);
16721672
}
16731673
relation_close(newrel, NoLock);
16741674
}

src/backend/commands/tablecmds.c

+17-10
Original file line numberDiff line numberDiff line change
@@ -3044,7 +3044,7 @@ rename_constraint_internal(Oid myrelid,
30443044
|| con->contype == CONSTRAINT_UNIQUE
30453045
|| con->contype == CONSTRAINT_EXCLUSION))
30463046
/* rename the index; this renames the constraint as well */
3047-
RenameRelationInternal(con->conindid, newconname, false);
3047+
RenameRelationInternal(con->conindid, newconname, false, true);
30483048
else
30493049
RenameConstraintById(constraintOid, newconname);
30503050

@@ -3112,6 +3112,7 @@ RenameConstraint(RenameStmt *stmt)
31123112
ObjectAddress
31133113
RenameRelation(RenameStmt *stmt)
31143114
{
3115+
bool is_index = stmt->renameType == OBJECT_INDEX;
31153116
Oid relid;
31163117
ObjectAddress address;
31173118

@@ -3123,7 +3124,8 @@ RenameRelation(RenameStmt *stmt)
31233124
* Lock level used here should match RenameRelationInternal, to avoid lock
31243125
* escalation.
31253126
*/
3126-
relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
3127+
relid = RangeVarGetRelidExtended(stmt->relation,
3128+
is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock,
31273129
stmt->missing_ok ? RVR_MISSING_OK : 0,
31283130
RangeVarCallbackForAlterRelation,
31293131
(void *) stmt);
@@ -3137,7 +3139,7 @@ RenameRelation(RenameStmt *stmt)
31373139
}
31383140

31393141
/* Do the work */
3140-
RenameRelationInternal(relid, stmt->newname, false);
3142+
RenameRelationInternal(relid, stmt->newname, false, is_index);
31413143

31423144
ObjectAddressSet(address, RelationRelationId, relid);
31433145

@@ -3148,7 +3150,7 @@ RenameRelation(RenameStmt *stmt)
31483150
* RenameRelationInternal - change the name of a relation
31493151
*/
31503152
void
3151-
RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal)
3153+
RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
31523154
{
31533155
Relation targetrelation;
31543156
Relation relrelation; /* for RELATION relation */
@@ -3157,11 +3159,16 @@ RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal)
31573159
Oid namespaceId;
31583160

31593161
/*
3160-
* Grab an exclusive lock on the target table, index, sequence, view,
3161-
* materialized view, or foreign table, which we will NOT release until
3162-
* end of transaction.
3162+
* Grab a lock on the target relation, which we will NOT release until end
3163+
* of transaction. We need at least a self-exclusive lock so that
3164+
* concurrent DDL doesn't overwrite the rename if they start updating
3165+
* while still seeing the old version. The lock also guards against
3166+
* triggering relcache reloads in concurrent sessions, which might not
3167+
* handle this information changing under them. For indexes, we can use a
3168+
* reduced lock level because RelationReloadIndexInfo() handles indexes
3169+
* specially.
31633170
*/
3164-
targetrelation = relation_open(myrelid, AccessExclusiveLock);
3171+
targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
31653172
namespaceId = RelationGetNamespace(targetrelation);
31663173

31673174
/*
@@ -3214,7 +3221,7 @@ RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal)
32143221
}
32153222

32163223
/*
3217-
* Close rel, but keep exclusive lock!
3224+
* Close rel, but keep lock!
32183225
*/
32193226
relation_close(targetrelation, NoLock);
32203227
}
@@ -7076,7 +7083,7 @@ ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
70767083
ereport(NOTICE,
70777084
(errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
70787085
indexName, constraintName)));
7079-
RenameRelationInternal(index_oid, constraintName, false);
7086+
RenameRelationInternal(index_oid, constraintName, false, true);
70807087
}
70817088

70827089
/* Extra checks needed if making primary key */

src/backend/commands/typecmds.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -3255,7 +3255,7 @@ RenameType(RenameStmt *stmt)
32553255
* RenameRelationInternal will call RenameTypeInternal automatically.
32563256
*/
32573257
if (typTup->typtype == TYPTYPE_COMPOSITE)
3258-
RenameRelationInternal(typTup->typrelid, newTypeName, false);
3258+
RenameRelationInternal(typTup->typrelid, newTypeName, false, false);
32593259
else
32603260
RenameTypeInternal(typeOid, newTypeName,
32613261
typTup->typnamespace);

src/include/commands/tablecmds.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ extern ObjectAddress RenameConstraint(RenameStmt *stmt);
6767
extern ObjectAddress RenameRelation(RenameStmt *stmt);
6868

6969
extern void RenameRelationInternal(Oid myrelid,
70-
const char *newrelname, bool is_internal);
70+
const char *newrelname, bool is_internal,
71+
bool is_index);
7172

7273
extern void find_composite_type_dependencies(Oid typeOid,
7374
Relation origRelation,

0 commit comments

Comments
 (0)