Skip to content

Commit 258cef1

Browse files
committed
Fix possibile deadlock when dropping partitions.
heap_drop_with_catalog and RangeVarCallbackForDropRelation should lock the parent before locking the target relation. Amit Langote Discussion: http://postgr.es/m/29588799-a8ce-b0a2-3dae-f39ff6d35922@lab.ntt.co.jp
1 parent feffa0e commit 258cef1

File tree

2 files changed

+47
-13
lines changed

2 files changed

+47
-13
lines changed

src/backend/catalog/heap.c

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1759,29 +1759,33 @@ void
17591759
heap_drop_with_catalog(Oid relid)
17601760
{
17611761
Relation rel;
1762+
HeapTuple tuple;
17621763
Oid parentOid;
17631764
Relation parent = NULL;
17641765

17651766
/*
1766-
* Open and lock the relation.
1767+
* To drop a partition safely, we must grab exclusive lock on its parent,
1768+
* because another backend might be about to execute a query on the parent
1769+
* table. If it relies on previously cached partition descriptor, then
1770+
* it could attempt to access the just-dropped relation as its partition.
1771+
* We must therefore take a table lock strong enough to prevent all
1772+
* queries on the table from proceeding until we commit and send out a
1773+
* shared-cache-inval notice that will make them update their index lists.
17671774
*/
1768-
rel = relation_open(relid, AccessExclusiveLock);
1769-
1770-
/*
1771-
* If the relation is a partition, we must grab exclusive lock on its
1772-
* parent because we need to update its partition descriptor. We must take
1773-
* a table lock strong enough to prevent all queries on the parent from
1774-
* proceeding until we commit and send out a shared-cache-inval notice
1775-
* that will make them update their partition descriptor. Sometimes, doing
1776-
* this is cycles spent uselessly, especially if the parent will be
1777-
* dropped as part of the same command anyway.
1778-
*/
1779-
if (rel->rd_rel->relispartition)
1775+
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
1776+
if (((Form_pg_class) GETSTRUCT(tuple))->relispartition)
17801777
{
17811778
parentOid = get_partition_parent(relid);
17821779
parent = heap_open(parentOid, AccessExclusiveLock);
17831780
}
17841781

1782+
ReleaseSysCache(tuple);
1783+
1784+
/*
1785+
* Open and lock the relation.
1786+
*/
1787+
rel = relation_open(relid, AccessExclusiveLock);
1788+
17851789
/*
17861790
* There can no longer be anyone *else* touching the relation, but we
17871791
* might still have open queries or cursors, or pending trigger events, in

src/backend/commands/tablecmds.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ struct DropRelationCallbackState
271271
{
272272
char relkind;
273273
Oid heapOid;
274+
Oid partParentOid;
274275
bool concurrent;
275276
};
276277

@@ -1049,6 +1050,7 @@ RemoveRelations(DropStmt *drop)
10491050
/* Look up the appropriate relation using namespace search. */
10501051
state.relkind = relkind;
10511052
state.heapOid = InvalidOid;
1053+
state.partParentOid = InvalidOid;
10521054
state.concurrent = drop->concurrent;
10531055
relOid = RangeVarGetRelidExtended(rel, lockmode, true,
10541056
false,
@@ -1078,6 +1080,8 @@ RemoveRelations(DropStmt *drop)
10781080
/*
10791081
* Before acquiring a table lock, check whether we have sufficient rights.
10801082
* In the case of DROP INDEX, also try to lock the table before the index.
1083+
* Also, if the table to be dropped is a partition, we try to lock the parent
1084+
* first.
10811085
*/
10821086
static void
10831087
RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
@@ -1087,6 +1091,7 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
10871091
struct DropRelationCallbackState *state;
10881092
char relkind;
10891093
char expected_relkind;
1094+
bool is_partition;
10901095
Form_pg_class classform;
10911096
LOCKMODE heap_lockmode;
10921097

@@ -1106,6 +1111,17 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
11061111
state->heapOid = InvalidOid;
11071112
}
11081113

1114+
/*
1115+
* Similarly, if we previously locked some other partition's heap, and
1116+
* the name we're looking up no longer refers to that relation, release
1117+
* the now-useless lock.
1118+
*/
1119+
if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1120+
{
1121+
UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
1122+
state->partParentOid = InvalidOid;
1123+
}
1124+
11091125
/* Didn't find a relation, so no need for locking or permission checks. */
11101126
if (!OidIsValid(relOid))
11111127
return;
@@ -1114,6 +1130,7 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
11141130
if (!HeapTupleIsValid(tuple))
11151131
return; /* concurrently dropped, so nothing to do */
11161132
classform = (Form_pg_class) GETSTRUCT(tuple);
1133+
is_partition = classform->relispartition;
11171134

11181135
/*
11191136
* Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
@@ -1157,6 +1174,19 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
11571174
if (OidIsValid(state->heapOid))
11581175
LockRelationOid(state->heapOid, heap_lockmode);
11591176
}
1177+
1178+
/*
1179+
* Similarly, if the relation is a partition, we must acquire lock on its
1180+
* parent before locking the partition. That's because queries lock the
1181+
* parent before its partitions, so we risk deadlock it we do it the other
1182+
* way around.
1183+
*/
1184+
if (is_partition && relOid != oldRelOid)
1185+
{
1186+
state->partParentOid = get_partition_parent(relOid);
1187+
if (OidIsValid(state->partParentOid))
1188+
LockRelationOid(state->partParentOid, AccessExclusiveLock);
1189+
}
11601190
}
11611191

11621192
/*

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy