From: Aaron Schulz Date: Tue, 22 Sep 2015 19:07:05 +0000 (-0700) Subject: Added DeleteLinksJob to support purging backlinks via job runners X-Git-Tag: 1.31.0-rc.0~9645^2 X-Git-Url: http://git.cyclocoop.org/fichier?a=commitdiff_plain;h=c0cb80beac069254edf9f77b7ce149af6568be23;p=lhc%2Fweb%2Fwiklou.git Added DeleteLinksJob to support purging backlinks via job runners * This jobs should only be constructed via relevant Content object, e.g. the result of enqueueUpdate() being called on a DataUpdate returned by Content::getSecondaryUpdates(). * Also modified LinksDeletionUpdate to support a $pageId parameter. * LinksDeletionUpdate can now be enqueued to a DeleteLinksJob. Change-Id: I650dcf0bd172ede0d61357ec158a4704ae1f2033 --- diff --git a/autoload.php b/autoload.php index f1b0a6cae7..7f0ef42a90 100644 --- a/autoload.php +++ b/autoload.php @@ -314,6 +314,7 @@ $wgAutoloadLocalClasses = array( 'DeleteDefaultMessages' => __DIR__ . '/maintenance/deleteDefaultMessages.php', 'DeleteEqualMessages' => __DIR__ . '/maintenance/deleteEqualMessages.php', 'DeleteFileOp' => __DIR__ . '/includes/filebackend/FileOp.php', + 'DeleteLinksJob' => __DIR__ . '/includes/jobqueue/jobs/DeleteLinksJob.php', 'DeleteLogFormatter' => __DIR__ . '/includes/logging/DeleteLogFormatter.php', 'DeleteOldRevisions' => __DIR__ . '/maintenance/deleteOldRevisions.php', 'DeleteOrphanedRevisions' => __DIR__ . '/maintenance/deleteOrphanedRevisions.php', diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index 6b5155aaee..30882265ab 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -6672,6 +6672,7 @@ $wgHooks = array(); */ $wgJobClasses = array( 'refreshLinks' => 'RefreshLinksJob', + 'deleteLinks' => 'DeleteLinksJob', 'htmlCacheUpdate' => 'HTMLCacheUpdateJob', 'sendMail' => 'EmaillingJob', 'enotifNotify' => 'EnotifNotifyJob', diff --git a/includes/deferred/LinksDeletionUpdate.php b/includes/deferred/LinksDeletionUpdate.php index b7cc70e050..d7848400ef 100644 --- a/includes/deferred/LinksDeletionUpdate.php +++ b/includes/deferred/LinksDeletionUpdate.php @@ -19,49 +19,48 @@ * * @file */ - /** * Update object handling the cleanup of links tables after a page was deleted. **/ -class LinksDeletionUpdate extends SqlDataUpdate { - /** @var WikiPage The WikiPage that was deleted */ - protected $mPage; +class LinksDeletionUpdate extends SqlDataUpdate implements EnqueueableDataUpdate { + /** @var WikiPage */ + protected $page; + /** @var integer */ + protected $pageId; /** - * Constructor - * * @param WikiPage $page Page we are updating + * @param integer|null $pageId ID of the page we are updating [optional] * @throws MWException */ - function __construct( WikiPage $page ) { + function __construct( WikiPage $page, $pageId = null ) { parent::__construct( false ); // no implicit transaction - $this->mPage = $page; - - if ( !$page->exists() ) { + $this->page = $page; + if ( $page->exists() ) { + $this->pageId = $page->getId(); + } elseif ( $pageId ) { + $this->pageId = $pageId; + } else { throw new MWException( "Page ID not known, perhaps the page doesn't exist?" ); } } - /** - * Do some database updates after deletion - */ public function doUpdate() { - $title = $this->mPage->getTitle(); - $id = $this->mPage->getId(); + # Page may already be deleted, so don't just getId() + $id = $this->pageId; # Delete restrictions for it $this->mDb->delete( 'page_restrictions', array( 'pr_page' => $id ), __METHOD__ ); # Fix category table counts - $cats = array(); - $res = $this->mDb->select( 'categorylinks', 'cl_to', array( 'cl_from' => $id ), __METHOD__ ); - - foreach ( $res as $row ) { - $cats[] = $row->cl_to; - } - - $this->mPage->updateCategoryCounts( array(), $cats ); + $cats = $this->mDb->selectFieldValues( + 'categorylinks', + 'cl_to', + array( 'cl_from' => $id ), + __METHOD__ + ); + $this->page->updateCategoryCounts( array(), $cats ); # If using cascading deletes, we can skip some explicit deletes if ( !$this->mDb->cascadingDeletes() ) { @@ -79,6 +78,7 @@ class LinksDeletionUpdate extends SqlDataUpdate { # 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', 'rc_id', @@ -102,4 +102,16 @@ class LinksDeletionUpdate extends SqlDataUpdate { } } } + + public function getAsJobSpecification() { + return array( + 'wiki' => $this->mDb->getWikiID(), + 'job' => new JobSpecification( + 'deleteLinks', + array( 'pageId' => $this->page->getId() ), + array( 'removeDuplicates' => true ), + $this->page->getTitle() + ) + ); + } } diff --git a/includes/jobqueue/jobs/DeleteLinksJob.php b/includes/jobqueue/jobs/DeleteLinksJob.php new file mode 100644 index 0000000000..b24109bb74 --- /dev/null +++ b/includes/jobqueue/jobs/DeleteLinksJob.php @@ -0,0 +1,57 @@ +removeDuplicates = true; + } + + function run() { + if ( is_null( $this->title ) ) { + $this->setLastError( "deleteLinks: Invalid title" ); + return false; + } + + $pageId = $this->params['pageId']; + if ( WikiPage::newFromID( $pageId, WikiPage::READ_LATEST ) ) { + // The page was restored somehow or something went wrong + $this->setLastError( "deleteLinks: Page #$pageId exists" ); + return false; + } + + $page = WikiPage::factory( $this->title ); // title when deleted + $update = new LinksDeletionUpdate( $page, $pageId ); + DataUpdate::runUpdates( array( $update ) ); + + return true; + } +} diff --git a/includes/page/WikiPage.php b/includes/page/WikiPage.php index 2fde8324c3..708a8755e5 100644 --- a/includes/page/WikiPage.php +++ b/includes/page/WikiPage.php @@ -2931,12 +2931,15 @@ class WikiPage implements Page, IDBAccessObject { * may already return null when the page proper was deleted. */ public function doDeleteUpdates( $id, Content $content = null ) { - // update site status + // Update site status DeferredUpdates::addUpdate( new SiteStatsUpdate( 0, 1, - (int)$this->isCountable(), -1 ) ); - // remove secondary indexes, etc + // Delete pagelinks, update secondary indexes, etc $updates = $this->getDeletionUpdates( $content ); - DataUpdate::runUpdates( $updates, 'enqueue' ); + // Make sure an enqueued jobs run after commit so they see the deletion + wfGetDB( DB_MASTER )->onTransactionIdle( function() use ( $updates ) { + DataUpdate::runUpdates( $updates, 'enqueue' ); + } ); // Reparse any pages transcluding this page LinksUpdate::queueRecursiveJobsForTable( $this->mTitle, 'templatelinks' ); diff --git a/tests/phpunit/includes/page/WikiPageTest.php b/tests/phpunit/includes/page/WikiPageTest.php index ec08ef4b72..a21fc8a7f3 100644 --- a/tests/phpunit/includes/page/WikiPageTest.php +++ b/tests/phpunit/includes/page/WikiPageTest.php @@ -292,6 +292,12 @@ class WikiPageTest extends MediaWikiLangTestCase { "Title::exists should return false after page was deleted" ); + // Run the job queue + JobQueueGroup::destroySingletons(); + $jobs = new RunJobs; + $jobs->loadParamsAndArgs( null, array( 'quiet' => true ), null ); + $jobs->execute(); + # ------------------------ $dbr = wfGetDB( DB_SLAVE ); $res = $dbr->select( 'pagelinks', '*', array( 'pl_from' => $id ) ); @@ -312,8 +318,16 @@ class WikiPageTest extends MediaWikiLangTestCase { ); $id = $page->getId(); + // Similar to MovePage logic + wfGetDB( DB_MASTER )->delete( 'page', array( 'page_id' => $id ), __METHOD__ ); $page->doDeleteUpdates( $id ); + // Run the job queue + JobQueueGroup::destroySingletons(); + $jobs = new RunJobs; + $jobs->loadParamsAndArgs( null, array( 'quiet' => true ), null ); + $jobs->execute(); + # ------------------------ $dbr = wfGetDB( DB_SLAVE ); $res = $dbr->select( 'pagelinks', '*', array( 'pl_from' => $id ) );