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
This commit is contained in:
Aaron Schulz 2019-04-23 16:59:35 -07:00
parent 05c510ff9c
commit d2331a6823
2 changed files with 46 additions and 15 deletions

View file

@ -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;
}

View file

@ -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();
} );
}