From: Aaron Schulz Date: Thu, 12 Feb 2015 23:03:24 +0000 (-0800) Subject: Removed doCascadeProtectionUpdates method to avoid DB writes on page views X-Git-Tag: 1.31.0-rc.0~12304^2 X-Git-Url: http://git.cyclocoop.org/%24image?a=commitdiff_plain;h=df5ef8b5d78d060bb41661a65284a3dccc49ac87;hp=f921aaa135d23e8d8c6e1337328ac3c7792548d6;p=lhc%2Fweb%2Fwiklou.git Removed doCascadeProtectionUpdates method to avoid DB writes on page views * Use special prioritized refreshLinksJobs instead, which triggers when transcluded pages are changed * Also added a triggerOpportunisticLinksUpdate() method to handle dynamic transcludes bug: T89389 Change-Id: Iea952d4d2e660b7957eafb5f73fc87fab347dbe7 --- diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index d4cdf9e8c8..bddcceceea 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -6422,6 +6422,7 @@ $wgJobClasses = array( 'PublishStashedFile' => 'PublishStashedFileJob', 'ThumbnailRender' => 'ThumbnailRenderJob', 'recentChangesUpdate' => 'RecentChangesUpdateJob', + 'refreshLinksPrioritized' => 'RefreshLinksJob', // for cascading protection 'null' => 'NullJob' ); diff --git a/includes/cache/BacklinkCache.php b/includes/cache/BacklinkCache.php index c6d9a185d4..c3f455ec5c 100644 --- a/includes/cache/BacklinkCache.php +++ b/includes/cache/BacklinkCache.php @@ -487,4 +487,55 @@ class BacklinkCache { return array( 'numRows' => $numRows, 'batches' => $batches ); } + + /** + * Get a Title iterator for cascade-protected template/file use backlinks + * + * @return TitleArray + * @since 1.25 + */ + public function getCascadeProtectedLinks() { + $dbr = $this->getDB(); + + // @todo: use UNION without breaking tests that use temp tables + $resSets = array(); + $resSets[] = $dbr->select( + array( 'templatelinks', 'page_restrictions', 'page' ), + array( 'page_namespace', 'page_title', 'page_id' ), + array( + 'tl_namespace' => $this->title->getNamespace(), + 'tl_title' => $this->title->getDBkey(), + 'tl_from = pr_page', + 'pr_cascade' => 1, + 'page_id = tl_from' + ), + __METHOD__, + array( 'DISTINCT' ) + ); + if ( $this->title->getNamespace() == NS_FILE ) { + $resSets[] = $dbr->select( + array( 'imagelinks', 'page_restrictions', 'page' ), + array( 'page_namespace', 'page_title', 'page_id' ), + array( + 'il_to' => $this->title->getDBkey(), + 'il_from = pr_page', + 'pr_cascade' => 1, + 'page_id = il_from' + ), + __METHOD__, + array( 'DISTINCT' ) + ); + } + + // Combine and de-duplicate the results + $mergedRes = array(); + foreach ( $resSets as $res ) { + foreach ( $res as $row ) { + $mergedRes[$row->page_id] = $row; + } + } + + return TitleArray::newFromResult( + new FakeResultWrapper( array_values( $mergedRes ) ) ); + } } diff --git a/includes/deferred/HTMLCacheUpdate.php b/includes/deferred/HTMLCacheUpdate.php index e02cfbc7ff..862ac2725a 100644 --- a/includes/deferred/HTMLCacheUpdate.php +++ b/includes/deferred/HTMLCacheUpdate.php @@ -43,7 +43,6 @@ class HTMLCacheUpdate implements DeferrableUpdate { } public function doUpdate() { - $job = new HTMLCacheUpdateJob( $this->mTitle, array( @@ -63,6 +62,5 @@ class HTMLCacheUpdate implements DeferrableUpdate { $job->run(); // just do the purge query now } ); } - } } diff --git a/includes/deferred/LinksUpdate.php b/includes/deferred/LinksUpdate.php index 9c377dff41..e4f00e7544 100644 --- a/includes/deferred/LinksUpdate.php +++ b/includes/deferred/LinksUpdate.php @@ -228,12 +228,24 @@ class LinksUpdate extends SqlDataUpdate { * Which means do LinksUpdate on all pages that include the current page, * using the job queue. */ - function queueRecursiveJobs() { + protected function queueRecursiveJobs() { self::queueRecursiveJobsForTable( $this->mTitle, 'templatelinks' ); if ( $this->mTitle->getNamespace() == NS_FILE ) { // Process imagelinks in case the title is or was a redirect self::queueRecursiveJobsForTable( $this->mTitle, 'imagelinks' ); } + + $bc = $this->mTitle->getBacklinkCache(); + // Get jobs for cascade-protected backlinks for a high priority queue. + // If meta-templates change to using a new template, the new template + // should be implicitly protected as soon as possible, if applicable. + // These jobs duplicate a subset of the above ones, but can run sooner. + // Which ever runs first generally no-ops the other one. + $jobs = array(); + foreach ( $bc->getCascadeProtectedLinks() as $title ) { + $jobs[] = new RefreshLinksJob( $title, array( 'prioritize' => true ) ); + } + JobQueueGroup::singleton()->push( $jobs ); } /** @@ -253,6 +265,7 @@ class LinksUpdate extends SqlDataUpdate { "refreshlinks:{$table}:{$title->getPrefixedText()}" ) ); + JobQueueGroup::singleton()->push( $job ); JobQueueGroup::singleton()->deduplicateRootJob( $job ); } diff --git a/includes/jobqueue/jobs/RefreshLinksJob.php b/includes/jobqueue/jobs/RefreshLinksJob.php index 5d95792cf9..1252b0b5e2 100644 --- a/includes/jobqueue/jobs/RefreshLinksJob.php +++ b/includes/jobqueue/jobs/RefreshLinksJob.php @@ -39,6 +39,10 @@ class RefreshLinksJob extends Job { function __construct( $title, $params = '' ) { parent::__construct( 'refreshLinks', $title, $params ); + // A separate type is used just for cascade-protected backlinks + if ( !empty( $this->params['prioritize'] ) ) { + $this->command .= 'Prioritized'; + } // Base backlink update jobs and per-title update jobs can be de-duplicated. // If template A changes twice before any jobs run, a clean queue will have: // (A base, A base) @@ -100,6 +104,10 @@ class RefreshLinksJob extends Job { return true; } + /** + * @param Title $title + * @return bool + */ protected function runForTitle( Title $title = null ) { $linkCache = LinkCache::singleton(); $linkCache->clear(); diff --git a/includes/page/Article.php b/includes/page/Article.php index 59f2ae7289..83c3241813 100644 --- a/includes/page/Article.php +++ b/includes/page/Article.php @@ -707,7 +707,7 @@ class Article implements Page { } # Get the ParserOutput actually *displayed* here. - # Note that $this->mParserOutput is the *current* version output. + # Note that $this->mParserOutput is the *current*/oldid version output. $pOutput = ( $outputDone instanceof ParserOutput ) ? $outputDone // object fetched by hook : $this->mParserOutput; diff --git a/includes/page/WikiPage.php b/includes/page/WikiPage.php index d30f589e74..fe61f6f3e2 100644 --- a/includes/page/WikiPage.php +++ b/includes/page/WikiPage.php @@ -3378,70 +3378,35 @@ class WikiPage implements Page, IDBAccessObject { } /** - * Updates cascading protections + * Opportunistically enqueue link update jobs given fresh parser output if useful * - * @param ParserOutput $parserOutput ParserOutput object for the current version + * @param ParserOutput $parserOutput Current version page output + * @return bool Whether a job was pushed + * @since 1.25 */ - public function doCascadeProtectionUpdates( ParserOutput $parserOutput ) { - if ( wfReadOnly() || !$this->mTitle->areRestrictionsCascading() ) { - return; - } - - // templatelinks or imagelinks tables may have become out of sync, - // especially if using variable-based transclusions. - // For paranoia, check if things have changed and if - // so apply updates to the database. This will ensure - // that cascaded protections apply as soon as the changes - // are visible. - - // Get templates from templatelinks and images from imagelinks - $id = $this->getId(); - - $dbLinks = array(); - - $dbr = wfGetDB( DB_SLAVE ); - $res = $dbr->select( array( 'templatelinks' ), - array( 'tl_namespace', 'tl_title' ), - array( 'tl_from' => $id ), - __METHOD__ - ); - - foreach ( $res as $row ) { - $dbLinks["{$row->tl_namespace}:{$row->tl_title}"] = true; + public function triggerOpportunisticLinksUpdate( ParserOutput $parserOutput ) { + if ( wfReadOnly() ) { + return false; } - $dbr = wfGetDB( DB_SLAVE ); - $res = $dbr->select( array( 'imagelinks' ), - array( 'il_to' ), - array( 'il_from' => $id ), - __METHOD__ - ); - - foreach ( $res as $row ) { - $dbLinks[NS_FILE . ":{$row->il_to}"] = true; + if ( $this->mTitle->areRestrictionsCascading() ) { + // If the page is cascade protecting, the links should really be up-to-date + $params = array( 'prioritize' => true ); + } elseif ( $parserOutput->hasDynamicContent() ) { + // Assume the output contains time/random based magic words + $params = array(); + } else { + // If the inclusions are deterministic, the edit-triggered link jobs are enough + return false; } - // Get templates and images from parser output. - $poLinks = array(); - foreach ( $parserOutput->getTemplates() as $ns => $templates ) { - foreach ( $templates as $dbk => $id ) { - $poLinks["$ns:$dbk"] = true; - } - } - foreach ( $parserOutput->getImages() as $dbk => $id ) { - $poLinks[NS_FILE . ":$dbk"] = true; + // Check if the last link refresh was before page_touched + if ( $this->getLinksTimestamp() < $this->getTouched() ) { + JobQueueGroup::singleton()->push( new RefreshLinksJob( $this->mTitle, $params ) ); + return true; } - // Get the diff - $links_diff = array_diff_key( $poLinks, $dbLinks ); - - if ( count( $links_diff ) > 0 ) { - // Whee, link updates time. - // Note: we are only interested in links here. We don't need to get - // other DataUpdate items from the parser output. - $u = new LinksUpdate( $this->mTitle, $parserOutput, false ); - $u->doUpdate(); - } + return false; } /** diff --git a/includes/parser/ParserOutput.php b/includes/parser/ParserOutput.php index e9e72beca8..da7842af19 100644 --- a/includes/parser/ParserOutput.php +++ b/includes/parser/ParserOutput.php @@ -879,6 +879,22 @@ class ParserOutput extends CacheTime { $this->mLimitReportData[$key] = $value; } + /** + * Check whether the cache TTL was lowered due to dynamic content + * + * When content is determined by more than hard state (e.g. page edits), + * such as template/file transclusions based on the current timestamp or + * extension tags that generate lists based on queries, this return true. + * + * @return bool + * @since 1.25 + */ + public function hasDynamicContent() { + global $wgParserCacheExpireTime; + + return $this->getCacheExpiry() < $wgParserCacheExpireTime; + } + /** * Get or set the prevent-clickjacking flag * diff --git a/includes/poolcounter/PoolWorkArticleView.php b/includes/poolcounter/PoolWorkArticleView.php index da20f94159..54cbb27603 100644 --- a/includes/poolcounter/PoolWorkArticleView.php +++ b/includes/poolcounter/PoolWorkArticleView.php @@ -159,7 +159,7 @@ class PoolWorkArticleView extends PoolCounterWork { } if ( $isCurrent ) { - $this->page->doCascadeProtectionUpdates( $this->parserOutput ); + $this->page->triggerOpportunisticLinksUpdate( $this->parserOutput ); } return true;