From: Aaron Schulz Date: Sat, 21 May 2016 10:06:23 +0000 (-0700) Subject: Make LinksDeletionUpdate use query batching X-Git-Tag: 1.31.0-rc.0~6727^2 X-Git-Url: http://git.cyclocoop.org/%22%20.%20generer_url_ecrire%28%22auteur_infos%22%2C%20%22id_auteur=%24id%22%29%20.%20%22?a=commitdiff_plain;h=4c700c08b859dd90c0d07bc654511dffc36c9291;p=lhc%2Fweb%2Fwiklou.git Make LinksDeletionUpdate use query batching Bug: T95501 Change-Id: Ib0c911b030ae17cb7b0729be223b89d64c8c4b3f --- diff --git a/includes/deferred/LinksDeletionUpdate.php b/includes/deferred/LinksDeletionUpdate.php index 65a8c0e0b1..d294fd2c42 100644 --- a/includes/deferred/LinksDeletionUpdate.php +++ b/includes/deferred/LinksDeletionUpdate.php @@ -42,48 +42,95 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate } elseif ( $pageId ) { $this->pageId = $pageId; } else { - throw new MWException( "Page ID not known, perhaps the page doesn't exist?" ); + throw new InvalidArgumentException( "Page ID not known. Page doesn't exist?" ); } } public function doUpdate() { - # Page may already be deleted, so don't just getId() + $config = RequestContext::getMain()->getConfig(); + $batchSize = $config->get( 'UpdateRowsPerQuery' ); + + // Page may already be deleted, so don't just getId() $id = $this->pageId; // Make sure all links update threads see the changes of each other. // This handles the case when updates have to batched into several COMMITs. $scopedLock = LinksUpdate::acquirePageLock( $this->mDb, $id ); - # Delete restrictions for it + // Delete restrictions for it $this->mDb->delete( 'page_restrictions', [ 'pr_page' => $id ], __METHOD__ ); - # Fix category table counts + // Fix category table counts $cats = $this->mDb->selectFieldValues( 'categorylinks', 'cl_to', [ 'cl_from' => $id ], __METHOD__ ); - $this->page->updateCategoryCounts( [], $cats ); + $catBatches = array_chunk( $cats, $batchSize ); + foreach ( $catBatches as $catBatch ) { + $this->page->updateCategoryCounts( [], $catBatch ); + if ( count( $catBatches ) > 1 ) { + $this->mDb->commit( __METHOD__, 'flush' ); + wfGetLBFactory()->waitForReplication( [ 'wiki' => $this->mDb->getWikiID() ] ); + } + } - # If using cascading deletes, we can skip some explicit deletes + // If using cascading deletes, we can skip some explicit deletes if ( !$this->mDb->cascadingDeletes() ) { - # Delete outgoing links - $this->mDb->delete( 'pagelinks', [ 'pl_from' => $id ], __METHOD__ ); - $this->mDb->delete( 'imagelinks', [ 'il_from' => $id ], __METHOD__ ); - $this->mDb->delete( 'categorylinks', [ 'cl_from' => $id ], __METHOD__ ); - $this->mDb->delete( 'templatelinks', [ 'tl_from' => $id ], __METHOD__ ); - $this->mDb->delete( 'externallinks', [ 'el_from' => $id ], __METHOD__ ); - $this->mDb->delete( 'langlinks', [ 'll_from' => $id ], __METHOD__ ); - $this->mDb->delete( 'iwlinks', [ 'iwl_from' => $id ], __METHOD__ ); + // Delete outgoing links + $this->batchDeleteByPK( + 'pagelinks', + [ 'pl_from' => $id ], + [ 'pl_from', 'pl_namespace', 'pl_title' ], + $batchSize + ); + $this->batchDeleteByPK( + 'imagelinks', + [ 'il_from' => $id ], + [ 'il_from', 'il_to' ], + $batchSize + ); + $this->batchDeleteByPK( + 'categorylinks', + [ 'cl_from' => $id ], + [ 'cl_from', 'cl_to' ], + $batchSize + ); + $this->batchDeleteByPK( + 'templatelinks', + [ 'tl_from' => $id ], + [ 'tl_from', 'tl_namespace', 'tl_title' ], + $batchSize + ); + $this->batchDeleteByPK( + 'externallinks', + [ 'el_from' => $id ], + [ 'el_id' ], + $batchSize + ); + $this->batchDeleteByPK( + 'langlinks', + [ 'il_from' => $id ], + [ 'il_from', 'll_lang' ], + $batchSize + ); + $this->batchDeleteByPK( + 'iwlinks', + [ 'il_from' => $id ], + [ 'iwl_from', 'iwl_prefix', 'iwl_title' ], + $batchSize + ); + // Delete any redirect entry or page props entries $this->mDb->delete( 'redirect', [ 'rd_from' => $id ], __METHOD__ ); $this->mDb->delete( 'page_props', [ 'pp_page' => $id ], __METHOD__ ); } - # If using cleanup triggers, we can skip some manual deletes + // If using cleanup triggers, we can skip some manual deletes if ( !$this->mDb->cleanupTriggers() ) { $title = $this->page->getTitle(); - # Find recentchanges entries to clean up... - $rcIdsForTitle = $this->mDb->selectFieldValues( 'recentchanges', + // Find recentchanges entries to clean up... + $rcIdsForTitle = $this->mDb->selectFieldValues( + 'recentchanges', 'rc_id', [ 'rc_type != ' . RC_LOG, @@ -92,16 +139,21 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate ], __METHOD__ ); - $rcIdsForPage = $this->mDb->selectFieldValues( 'recentchanges', + $rcIdsForPage = $this->mDb->selectFieldValues( + 'recentchanges', 'rc_id', [ 'rc_type != ' . RC_LOG, 'rc_cur_id' => $id ], __METHOD__ ); - # T98706: delete PK to avoid lock contention with RC delete log insertions - $rcIds = array_merge( $rcIdsForTitle, $rcIdsForPage ); - if ( $rcIds ) { - $this->mDb->delete( 'recentchanges', [ 'rc_id' => $rcIds ], __METHOD__ ); + // T98706: delete by PK to avoid lock contention with RC delete log insertions + $rcIdBatches = array_chunk( array_merge( $rcIdsForTitle, $rcIdsForPage ), $batchSize ); + foreach ( $rcIdBatches as $rcIdBatch ) { + $this->mDb->delete( 'recentchanges', [ 'rc_id' => $rcIdBatch ], __METHOD__ ); + if ( count( $rcIdBatches ) > 1 ) { + $this->mDb->commit( __METHOD__, 'flush' ); + wfGetLBFactory()->waitForReplication( [ 'wiki' => $this->mDb->getWikiID() ] ); + } } } @@ -111,6 +163,26 @@ class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate } ); } + private function batchDeleteByPK( $table, array $conds, array $pk, $bSize ) { + $dbw = $this->mDb; // convenience + $res = $dbw->select( $table, $pk, $conds, __METHOD__ ); + + $pkDeleteConds = []; + foreach ( $res as $row ) { + $pkDeleteConds[] = $this->mDb->makeList( (array)$row, LIST_AND ); + if ( count( $pkDeleteConds ) >= $bSize ) { + $dbw->delete( $table, $dbw->makeList( $pkDeleteConds, LIST_OR ), __METHOD__ ); + $dbw->commit( __METHOD__, 'flush' ); + wfGetLBFactory()->waitForReplication( [ 'wiki' => $dbw->getWikiID() ] ); + $pkDeleteConds = []; + } + } + + if ( $pkDeleteConds ) { + $dbw->delete( $table, $dbw->makeList( $pkDeleteConds, LIST_OR ), __METHOD__ ); + } + } + public function getAsJobSpecification() { return [ 'wiki' => $this->mDb->getWikiID(),