From: Aaron Schulz Date: Tue, 23 Apr 2019 23:59:35 +0000 (-0700) Subject: Reinstate small category refresh logic in LinksDeletionUpdate X-Git-Tag: 1.34.0-rc.0~1881^2 X-Git-Url: https://git.heureux-cyclage.org/?a=commitdiff_plain;h=d2331a68230d9e1793422f16fcd029f27f55e2cb;p=lhc%2Fweb%2Fwiklou.git Reinstate small category refresh logic in LinksDeletionUpdate Add new Category::refreshCountsIfSmall() method that will do a non-locking SELECT with LIMIT before deciding whether to do a full locking SELECT and refresh. Call this from LinksDeletionUpdate. Bug: T18036 Change-Id: I9de8311565988453b8e29a7f3d95d758182fcec1 --- diff --git a/includes/Category.php b/includes/Category.php index 77f1212fcf..34ac0e1936 100644 --- a/includes/Category.php +++ b/includes/Category.php @@ -41,6 +41,8 @@ class Category { const LOAD_ONLY = 0; const LAZY_INIT_ROW = 1; + const ROW_COUNT_SMALL = 100; + private function __construct() { } @@ -431,28 +433,57 @@ class Category { * @since 1.32 */ public function refreshCountsIfEmpty() { + return $this->refreshCountsIfSmall( 0 ); + } + + /** + * Call refreshCounts() if there are few entries in the categorylinks table + * + * Due to lock errors or other failures, the precomputed counts can get out of sync, + * making it hard to know when to delete the category row without checking the + * categorylinks table. + * + * This method will do a non-locking select first to reduce contention. + * + * @param int $maxSize Only refresh if there are this or less many backlinks + * @return bool Whether links were refreshed + * @since 1.34 + */ + public function refreshCountsIfSmall( $maxSize = self::ROW_COUNT_SMALL ) { $dbw = wfGetDB( DB_MASTER ); + $dbw->startAtomic( __METHOD__ ); - $hasLink = $dbw->selectField( + $typeOccurances = $dbw->selectFieldValues( 'categorylinks', - '1', + 'cl_type', [ 'cl_to' => $this->getName() ], - __METHOD__ + __METHOD__, + [ 'LIMIT' => $maxSize + 1 ] ); - if ( !$hasLink ) { - $this->refreshCounts(); // delete any category table entry - return true; + if ( !$typeOccurances ) { + $doRefresh = true; // delete any category table entry + } elseif ( count( $typeOccurances ) <= $maxSize ) { + $countByType = array_count_values( $typeOccurances ); + $doRefresh = !$dbw->selectField( + 'category', + '1', + [ + 'cat_title' => $this->getName(), + 'cat_pages' => $countByType['page'] ?? 0, + 'cat_subcats' => $countByType['subcat'] ?? 0, + 'cat_files' => $countByType['file'] ?? 0 + ], + __METHOD__ + ); + } else { + $doRefresh = false; // category is too big } - $hasBadRow = $dbw->selectField( - 'category', - '1', - [ 'cat_title' => $this->getName(), 'cat_pages <= 0' ], - __METHOD__ - ); - if ( $hasBadRow ) { - $this->refreshCounts(); // clean up this row + $dbw->endAtomic( __METHOD__ ); + + if ( $doRefresh ) { + $this->refreshCounts(); // update the row return true; } diff --git a/includes/deferred/LinksDeletionUpdate.php b/includes/deferred/LinksDeletionUpdate.php index 0e24134770..8bc9dfa589 100644 --- a/includes/deferred/LinksDeletionUpdate.php +++ b/includes/deferred/LinksDeletionUpdate.php @@ -73,7 +73,7 @@ class LinksDeletionUpdate extends LinksUpdate implements EnqueueableDataUpdate { // T166757: do the update after the main job DB commit DeferredUpdates::addCallableUpdate( function () use ( $title ) { $cat = Category::newFromName( $title->getDBkey() ); - $cat->refreshCountsIfEmpty(); + $cat->refreshCountsIfSmall(); } ); }