Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Fix busted logic for parallel lock grouping in TopoSort().
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 29 Jul 2019 22:49:04 +0000 (18:49 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 29 Jul 2019 22:49:04 +0000 (18:49 -0400)
A "break" statement erroneously left behind by commit a1c1af2a1
caused TopoSort to do the wrong thing if a lock's wait list
contained multiple members of the same locking group.

Because parallel workers don't normally need any locks not already
taken by their leader, this is very hard --- maybe impossible ---
to hit in production.  Still, if it did happen, the queries involved
in an otherwise-resolvable deadlock would block until canceled.

In addition to removing the bogus "break", add an Assert showing
that the conflicting uses of the beforeConstraints[] array (for both
counts and flags) don't overlap, and add some commentary explaining
why not; because it's not obvious without explanation, IMHO.

Original report and patch from Rui Hai Jiang; additional assert
and commentary by me.  Back-patch to 9.6 where the bug came in.

Discussion: https://postgr.es/m/CAEri+mLd3bpHLyW+a9pSe1y=aEkeuJpwBSwvo-+m4n7-ceRmXw@mail.gmail.com

src/backend/storage/lmgr/deadlock.c

index 9abc9d778f6a3365b6f5cf1e932c9ee28cf58aae..f17ef9377a69fc6f6613f58eb80136b30636a4dd 100644 (file)
@@ -922,6 +922,12 @@ TopoSort(LOCK *lock,
         * in the same lock group on the queue, set their number of
         * beforeConstraints to -1 to indicate that they should be emitted
         * with their groupmates rather than considered separately.
+        *
+        * In this loop and the similar one just below, it's critical that we
+        * consistently select the same representative member of any one lock
+        * group, so that all the constraints are associated with the same
+        * proc, and the -1's are only associated with not-representative
+        * members.  We select the last one in the topoProcs array.
         */
        proc = constraints[i].waiter;
        Assert(proc != NULL);
@@ -940,7 +946,6 @@ TopoSort(LOCK *lock,
                    Assert(beforeConstraints[j] <= 0);
                    beforeConstraints[j] = -1;
                }
-               break;
            }
        }
 
@@ -977,6 +982,7 @@ TopoSort(LOCK *lock,
        if (kk < 0)
            continue;
 
+       Assert(beforeConstraints[jj] >= 0);
        beforeConstraints[jj]++;    /* waiter must come before */
        /* add this constraint to list of after-constraints for blocker */
        constraints[i].pred = jj;