Category: Lock the category row before the categorylinks rows
authorBrad Jorsch <bjorsch@wikimedia.org>
Tue, 12 Jun 2018 15:28:25 +0000 (11:28 -0400)
committerBrad Jorsch <bjorsch@wikimedia.org>
Tue, 12 Jun 2018 15:28:25 +0000 (11:28 -0400)
We've noticed a large increase in deadlocks between
LinksDeletionUpdate deleting categorylinks rows and
Category::refreshCounts() trying to update the category table.

My best guess as to what's going on there is that LinksDeletionUpdate
locks the category row via the call to WikiPage::updateCategoryCounts()
then the categorylinks rows via its own deletions, while Category first
locks the categorylinks rows (in share mode) and then the category row
when it tries to update or delete it.

To break the deadlock, let's have Category do a SELECT FOR UPDATE on the
category row first before it locks the categorylinks rows.

Bug: T195397
Change-Id: Ie11baadf2ff0ba2afbc86b10bc523525c570a490

includes/Category.php

index 46b86d8..2cf44b8 100644 (file)
@@ -335,6 +335,16 @@ class Category {
 
                $dbw->startAtomic( __METHOD__ );
 
+               // Lock the `category` row before locking `categorylinks` rows to try
+               // to avoid deadlocks with LinksDeletionUpdate (T195397)
+               $dbw->selectField(
+                       'category',
+                       1,
+                       [ 'cat_title' => $this->mName ],
+                       __METHOD__,
+                       [ 'FOR UPDATE' ]
+               );
+
                // Lock all the `categorylinks` records and gaps for this category;
                // this is a separate query due to postgres/oracle limitations
                $dbw->selectRowCount(