Allow relation extension lock to conflict among parallel group members.
authorAmit Kapila <akapila@postgresql.org>
Fri, 20 Mar 2020 02:50:56 +0000 (08:20 +0530)
committerAmit Kapila <akapila@postgresql.org>
Fri, 20 Mar 2020 02:50:56 +0000 (08:20 +0530)
This is required as it is no safer for two related processes to extend the
same relation at a time than for unrelated processes to do the same.  We
don't acquire a heavyweight lock on any other object after relation
extension lock which means such a lock can never participate in the
deadlock cycle.  So, avoid checking wait edges from this lock.

This provides an infrastructure to allow parallel operations like insert,
copy, etc. which were earlier not possible as parallel group members won't
conflict for relation extension lock.

Author: Dilip Kumar, Amit Kapila
Reviewed-by: Amit Kapila, Kuntal Ghosh and Sawada Masahiko
Discussion: https://postgr.es/m/CAD21AoCmT3cFQUN4aVvzy5chw7DuzXrJCbrjTU05B+Ss=Gn1LA@mail.gmail.com

src/backend/storage/lmgr/deadlock.c
src/backend/storage/lmgr/lock.c
src/backend/storage/lmgr/proc.c
src/include/storage/lock.h

index f8c5df08e691fd0f3d0a25d886ffb0f77a6c2083..59060b6080f203f1b745d176f36a966f006de816 100644 (file)
@@ -555,6 +555,14 @@ FindLockCycleRecurseMember(PGPROC *checkProc,
        int                     numLockModes,
                                lm;
 
+       /*
+        * The relation extension lock can never participate in actual deadlock
+        * cycle.  See Assert in LockAcquireExtended.  So, there is no advantage
+        * in checking wait edges from it.
+        */
+       if (LOCK_LOCKTAG(*lock) == LOCKTAG_RELATION_EXTEND)
+               return false;
+
        lockMethodTable = GetLocksMethodTable(lock);
        numLockModes = lockMethodTable->numLockModes;
        conflictMask = lockMethodTable->conflictTab[checkProc->waitLockMode];
index be44913e2a0989491629f2f723f20b55332a6ab9..5f3261734298aff5062ac3aa22389b05a567e03a 100644 (file)
@@ -1469,6 +1469,16 @@ LockCheckConflicts(LockMethod lockMethodTable,
                return true;
        }
 
+       /*
+        * The relation extension lock conflict even between the group members.
+        */
+       if (LOCK_LOCKTAG(*lock) == LOCKTAG_RELATION_EXTEND)
+       {
+               PROCLOCK_PRINT("LockCheckConflicts: conflicting (group)",
+                                          proclock);
+               return true;
+       }
+
        /*
         * Locks held in conflicting modes by members of our own lock group are
         * not real conflicts; we can subtract those out and see if we still have
index eb321f72ea472f0606ce60b97a751586361c8ab7..fa07ddf4f750457ed7b3902209d5daa2014b8c95 100644 (file)
@@ -1077,7 +1077,13 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
 
        /*
         * If group locking is in use, locks held by members of my locking group
-        * need to be included in myHeldLocks.
+        * need to be included in myHeldLocks.  This is not required for relation
+        * extension lock which conflict among group members. However, including
+        * them in myHeldLocks will give group members the priority to get those
+        * locks as compared to other backends which are also trying to acquire
+        * those locks.  OTOH, we can avoid giving priority to group members for
+        * that kind of locks, but there doesn't appear to be a clear advantage of
+        * the same.
         */
        if (leader != NULL)
        {
index fc0a7128807f52db2dde645a4a147c7b697583c3..a89e54dbb097f7b8ab7685e5823293525692ccbc 100644 (file)
@@ -301,6 +301,7 @@ typedef struct LOCK
 } LOCK;
 
 #define LOCK_LOCKMETHOD(lock) ((LOCKMETHODID) (lock).tag.locktag_lockmethodid)
+#define LOCK_LOCKTAG(lock) ((LockTagType) (lock).tag.locktag_type)
 
 
 /*