Re: Concurrent deadlock scenario with DROP INDEX on partitioned index
Jimmy Yih <jyih@vmware.com>
From: Jimmy Yih <jyih@vmware.com>
To: Tom Lane <tgl@sss.pgh.pa.us>
Cc: "pgsql-hackers@postgresql.org" <pgsql-hackers@postgresql.org>, Gaurab Dey <gaurabd@vmware.com>
Date: 2022-03-16T18:20:10Z
Lists: pgsql-hackers
Attachments
- v2-0001-Fix-concurrent-deadlock-scenario-with-DROP-INDEX-.patch (application/octet-stream) patch v2-0001
Tom Lane <tgl@sss.pgh.pa.us> wrote: > 1. RangeVarCallbackForDropRelation can get called more than once > during a lookup (in case of concurrent rename and suchlike). > If so it needs to be prepared to drop the lock(s) it got last time. > You have not implemented any such logic. This doesn't seem hard > to fix, just store the OID list into struct DropRelationCallbackState. Fixed in attached patch. We added the OID list to the DropRelationCallbackState as you suggested. > 2. I'm not sure you have fully thought through the interaction > with the subsequent "is_partition" stanza. If the target is > an intermediate partitioned index, don't we need to acquire lock > on its parent index before starting to lock children? (It may > be that that stanza is already in the wrong place relative to > the table-locking stanza it currently follows, but not sure.) It's not required to acquire lock on a possible parent index because of the restriction where we can only run DROP INDEX on the top-most partitioned index. > 3. Calling find_all_inheritors with lockmode NoLock, and then > locking those relation OIDs later, is both unsafe and code-wasteful. > Just tell find_all_inheritors to get the locks you want. Fixed in attached patch. > 4. This code will invoke find_all_inheritors on InvalidOid if > IndexGetRelation fails. It needs to be within the if-test > just above. Fixed in attached patch. > 5. Reading classform again at this point in the function is > not merely inefficient, but outright wrong, because we > already released the syscache entry. Use the local variable. Fixed in attached patch. Added another local variable is_partitioned_index to store the classform value. The main reason we need the classform is because the existing relkind and expected_relkind local variables would only show RELKIND_INDEX whereas we needed exactly RELKIND_PARTITIONED_INDEX. > 6. You've not paid enough attention to updating existing comments, > particularly the header comment for RangeVarCallbackForDropRelation. Fixed in attached patch. Updated the header comment and aggregated our introduced comment to another relative comment slightly above the proposed locking section. > Actually though, maybe you *don't* want to do this in > RangeVarCallbackForDropRelation. Because of point 2, it might be > better to run find_all_inheritors after we've successfully > identified and locked the direct target relation, ie do it back > in RemoveRelations. I've not thought hard about that, but it's > attractive if only because it'd mean you don't have to fix point 1. We think that RangeVarCallbackForDropRelation is probably the most correct function to place the fix. It would look a bit out-of-place being in RemoveRelations seeing how there's already relative DROP INDEX code in RangeVarCallbackForDropRelation. With point 2 explained and point 1 addressed, the fix seems to look okay in RangeVarCallbackForDropRelation. Thanks for the feedback. Attached new patch with feedback addressed. -- Jimmy Yih (VMware) Gaurab Dey (VMware)
Commits
-
Fix risk of deadlock failure while dropping a partitioned index.
- d8d378d5100f 12.11 landed
- b8ae17fd9fdf 11.16 landed
- 7b6ec86532c2 15.0 landed
- 2241e5ceda2f 13.7 landed
- 05ccf974cd75 14.3 landed