From 8f829de5f04070e3254113590a5b192156dc85da Mon Sep 17 00:00:00 2001 From: Aaron Schulz Date: Thu, 19 Oct 2017 13:10:14 -0700 Subject: [PATCH] Add action/user tracking to link refresh jobs Change-Id: Ie7261eacddb869988b005ba2f17968df88c7003e --- includes/api/ApiPurge.php | 1 + includes/deferred/DataUpdate.php | 27 ++++++++++++++++++++++ includes/deferred/LinksUpdate.php | 25 ++++++++++++++++---- includes/filerepo/file/LocalFile.php | 7 +++++- includes/jobqueue/jobs/RefreshLinksJob.php | 8 +++++++ includes/page/WikiPage.php | 16 +++++++++---- 6 files changed, 73 insertions(+), 11 deletions(-) diff --git a/includes/api/ApiPurge.php b/includes/api/ApiPurge.php index 35f93e076a..4b8ce7fce1 100644 --- a/includes/api/ApiPurge.php +++ b/includes/api/ApiPurge.php @@ -93,6 +93,7 @@ class ApiPurge extends ApiBase { $updates = $content->getSecondaryDataUpdates( $title, null, $forceRecursiveLinkUpdate, $p_result ); foreach ( $updates as $update ) { + $update->setCause( 'api-purge', $this->getUser()->getName() ); DeferredUpdates::addUpdate( $update, DeferredUpdates::PRESEND ); } diff --git a/includes/deferred/DataUpdate.php b/includes/deferred/DataUpdate.php index d2d8bd7a31..ed9a7462f7 100644 --- a/includes/deferred/DataUpdate.php +++ b/includes/deferred/DataUpdate.php @@ -28,6 +28,10 @@ abstract class DataUpdate implements DeferrableUpdate { /** @var mixed Result from LBFactory::getEmptyTransactionTicket() */ protected $ticket; + /** @var string Short update cause action description */ + protected $causeAction = 'unknown'; + /** @var string Short update cause user description */ + protected $causeAgent = 'unknown'; public function __construct() { // noop @@ -41,6 +45,29 @@ abstract class DataUpdate implements DeferrableUpdate { $this->ticket = $ticket; } + /** + * @param string $action Action type + * @param string $user User name + */ + public function setCause( $action, $user ) { + $this->causeAction = $action; + $this->causeAgent = $user; + } + + /** + * @return string + */ + public function getCauseAction() { + return $this->causeAction; + } + + /** + * @return string + */ + public function getCauseAgent() { + return $this->causeAgent; + } + /** * Convenience method, calls doUpdate() on every DataUpdate in the array. * diff --git a/includes/deferred/LinksUpdate.php b/includes/deferred/LinksUpdate.php index dfe89ba3fa..c27826d50d 100644 --- a/includes/deferred/LinksUpdate.php +++ b/includes/deferred/LinksUpdate.php @@ -306,10 +306,13 @@ class LinksUpdate extends DataUpdate implements EnqueueableDataUpdate { * using the job queue. */ protected function queueRecursiveJobs() { - self::queueRecursiveJobsForTable( $this->mTitle, 'templatelinks' ); + $action = $this->getCauseAction(); + $agent = $this->getCauseAgent(); + + self::queueRecursiveJobsForTable( $this->mTitle, 'templatelinks', $action, $agent ); if ( $this->mTitle->getNamespace() == NS_FILE ) { // Process imagelinks in case the title is or was a redirect - self::queueRecursiveJobsForTable( $this->mTitle, 'imagelinks' ); + self::queueRecursiveJobsForTable( $this->mTitle, 'imagelinks', $action, $agent ); } $bc = $this->mTitle->getBacklinkCache(); @@ -320,7 +323,13 @@ class LinksUpdate extends DataUpdate implements EnqueueableDataUpdate { // Which ever runs first generally no-ops the other one. $jobs = []; foreach ( $bc->getCascadeProtectedLinks() as $title ) { - $jobs[] = RefreshLinksJob::newPrioritized( $title, [] ); + $jobs[] = RefreshLinksJob::newPrioritized( + $title, + [ + 'causeAction' => $action, + 'causeAgent' => $agent + ] + ); } JobQueueGroup::singleton()->push( $jobs ); } @@ -330,8 +339,12 @@ class LinksUpdate extends DataUpdate implements EnqueueableDataUpdate { * * @param Title $title Title to do job for * @param string $table Table to use (e.g. 'templatelinks') + * @param string $action Triggering action + * @param string $userName Triggering user name */ - public static function queueRecursiveJobsForTable( Title $title, $table ) { + public static function queueRecursiveJobsForTable( + Title $title, $table, $action = 'unknown', $userName = 'unknown' + ) { if ( $title->getBacklinkCache()->hasLinks( $table ) ) { $job = new RefreshLinksJob( $title, @@ -340,7 +353,7 @@ class LinksUpdate extends DataUpdate implements EnqueueableDataUpdate { 'recursive' => true, ] + Job::newRootJobParams( // "overall" refresh links job info "refreshlinks:{$table}:{$title->getPrefixedText()}" - ) + ) + [ 'causeAction' => $action, 'causeAgent' => $userName ] ); JobQueueGroup::singleton()->push( $job ); @@ -1156,6 +1169,8 @@ class LinksUpdate extends DataUpdate implements EnqueueableDataUpdate { 'useRecursiveLinksUpdate' => $this->mRecursive, 'triggeringUser' => $userInfo, 'triggeringRevisionId' => $triggeringRevisionId, + 'causeAction' => $this->getCauseAction(), + 'causeAgent' => $this->getCauseAgent() ], [ 'removeDuplicates' => true ], $this->getTitle() diff --git a/includes/filerepo/file/LocalFile.php b/includes/filerepo/file/LocalFile.php index a36bec3cda..3271c966b8 100644 --- a/includes/filerepo/file/LocalFile.php +++ b/includes/filerepo/file/LocalFile.php @@ -1646,7 +1646,12 @@ class LocalFile extends File { ); } else { # Update backlink pages pointing to this title if created - LinksUpdate::queueRecursiveJobsForTable( $this->getTitle(), 'imagelinks' ); + LinksUpdate::queueRecursiveJobsForTable( + $this->getTitle(), + 'imagelinks', + 'upload-image', + $user->getName() + ); } $this->prerenderThumbnails(); diff --git a/includes/jobqueue/jobs/RefreshLinksJob.php b/includes/jobqueue/jobs/RefreshLinksJob.php index 51e964d54f..8854c6560f 100644 --- a/includes/jobqueue/jobs/RefreshLinksJob.php +++ b/includes/jobqueue/jobs/RefreshLinksJob.php @@ -53,6 +53,7 @@ class RefreshLinksJob extends Job { // Multiple pages per job make matches unlikely !( isset( $params['pages'] ) && count( $params['pages'] ) != 1 ) ); + $this->params += [ 'causeAction' => 'unknown', 'causeAgent' => 'unknown' ]; } /** @@ -102,6 +103,9 @@ class RefreshLinksJob extends Job { // Carry over information for de-duplication $extraParams = $this->getRootJobParams(); $extraParams['triggeredRecursive'] = true; + // Carry over cause information for logging + $extraParams['causeAction'] = $this->params['causeAction']; + $extraParams['causeAgent'] = $this->params['causeAgent']; // Convert this into no more than $wgUpdateRowsPerJob RefreshLinks per-title // jobs and possibly a recursive RefreshLinks job for the rest of the backlinks $jobs = BacklinkJobUtils::partitionBacklinkJob( @@ -254,6 +258,8 @@ class RefreshLinksJob extends Job { $lbFactory->commitAndWaitForReplication( __METHOD__, $ticket ); foreach ( $updates as $update ) { + // Carry over cause in case so the update can do extra logging + $update->setCause( $this->params['causeAction'], $this->params['causeAgent'] ); // FIXME: This code probably shouldn't be here? // Needed by things like Echo notifications which need // to know which user caused the links update @@ -288,6 +294,8 @@ class RefreshLinksJob extends Job { public function getDeduplicationInfo() { $info = parent::getDeduplicationInfo(); + unset( $info['causeAction'] ); + unset( $info['causeAgent'] ); if ( is_array( $info['params'] ) ) { // For per-pages jobs, the job title is that of the template that changed // (or similar), so remove that since it ruins duplicate detection diff --git a/includes/page/WikiPage.php b/includes/page/WikiPage.php index d0a04c0a31..e875df5ea8 100644 --- a/includes/page/WikiPage.php +++ b/includes/page/WikiPage.php @@ -2171,6 +2171,7 @@ class WikiPage implements Page, IDBAccessObject { $this->getTitle(), null, $recursive, $editInfo->output ); foreach ( $updates as $update ) { + $update->setCause( 'edit-page', $user->getName() ); if ( $update instanceof LinksUpdate ) { $update->setRevision( $revision ); $update->setTriggeringUser( $user ); @@ -2913,7 +2914,7 @@ class WikiPage implements Page, IDBAccessObject { $dbw->endAtomic( __METHOD__ ); - $this->doDeleteUpdates( $id, $content, $revision ); + $this->doDeleteUpdates( $id, $content, $revision, $user ); Hooks::run( 'ArticleDeleteComplete', [ &$wikiPageBeforeDelete, @@ -2964,8 +2965,11 @@ class WikiPage implements Page, IDBAccessObject { * the required updates. This may be needed because $this->getContent() * may already return null when the page proper was deleted. * @param Revision|null $revision The latest page revision + * @param User|null $user The user that caused the deletion */ - public function doDeleteUpdates( $id, Content $content = null, Revision $revision = null ) { + public function doDeleteUpdates( + $id, Content $content = null, Revision $revision = null, User $user = null + ) { try { $countable = $this->isCountable(); } catch ( Exception $ex ) { @@ -2983,12 +2987,14 @@ class WikiPage implements Page, IDBAccessObject { DeferredUpdates::addUpdate( $update ); } + $causeAgent = $user ? $user->getName() : 'unknown'; // Reparse any pages transcluding this page - LinksUpdate::queueRecursiveJobsForTable( $this->mTitle, 'templatelinks' ); - + LinksUpdate::queueRecursiveJobsForTable( + $this->mTitle, 'templatelinks', 'delete-page', $causeAgent ); // Reparse any pages including this image if ( $this->mTitle->getNamespace() == NS_FILE ) { - LinksUpdate::queueRecursiveJobsForTable( $this->mTitle, 'imagelinks' ); + LinksUpdate::queueRecursiveJobsForTable( + $this->mTitle, 'imagelinks', 'delete-page', $causeAgent ); } // Clear caches -- 2.20.1