From: Aaron Schulz Date: Tue, 30 Aug 2016 19:35:08 +0000 (-0700) Subject: Adapt the ParserOutput cache TTL when including special pages X-Git-Tag: 1.31.0-rc.0~5819^2 X-Git-Url: http://git.cyclocoop.org/%22.%20generer_url_ecrire%28%22sites_tous%22%2C%22%22%29.%20%22?a=commitdiff_plain;h=97f004694c2688e8682d31f866d18b97653d7965;p=lhc%2Fweb%2Fwiklou.git Adapt the ParserOutput cache TTL when including special pages For simple pages that transclude special pages, like user pages including Special:PrefixIndex, the TTL is allowed to drop to 15 seconds if the page parses fast enough. Bug: T139893 Change-Id: If41885ded648d68352fe3d06336d98aa0ab53966 --- diff --git a/includes/parser/Parser.php b/includes/parser/Parser.php index 035baacf0e..b4d9c7002b 100644 --- a/includes/parser/Parser.php +++ b/includes/parser/Parser.php @@ -395,7 +395,8 @@ class Parser { * @param int $revid Number to pass in {{REVISIONID}} * @return ParserOutput A ParserOutput */ - public function parse( $text, Title $title, ParserOptions $options, + public function parse( + $text, Title $title, ParserOptions $options, $linestart = true, $clearState = true, $revid = null ) { /** @@ -462,6 +463,10 @@ class Parser { } } + # Done parsing! Compute runtime adaptive expiry if set + $this->mOutput->finalizeAdaptiveCacheExpiry(); + + # Warn if too many heavyweight parser functions were used if ( $this->mExpensiveFunctionCount > $this->mOptions->getExpensiveParserFunctionLimit() ) { $this->limitationWarn( 'expensive-parserfunction', $this->mExpensiveFunctionCount, @@ -3144,14 +3149,17 @@ class Parser { $context->setUser( User::newFromName( '127.0.0.1', false ) ); } $context->setLanguage( $this->mOptions->getUserLangObj() ); - $ret = SpecialPageFactory::capturePath( $title, $context, $this->getLinkRenderer() ); + $ret = SpecialPageFactory::capturePath( + $title, $context, $this->getLinkRenderer() ); if ( $ret ) { $text = $context->getOutput()->getHTML(); $this->mOutput->addOutputPageMetadata( $context->getOutput() ); $found = true; $isHTML = true; if ( $specialPage && $specialPage->maxIncludeCacheTime() !== false ) { - $this->mOutput->updateCacheExpiry( $specialPage->maxIncludeCacheTime() ); + $this->mOutput->updateRuntimeAdaptiveExpiry( + $specialPage->maxIncludeCacheTime() + ); } } } elseif ( MWNamespace::isNonincludable( $title->getNamespace() ) ) { diff --git a/includes/parser/ParserOutput.php b/includes/parser/ParserOutput.php index f0528125b0..9dfa97cb5a 100644 --- a/includes/parser/ParserOutput.php +++ b/includes/parser/ParserOutput.php @@ -209,9 +209,21 @@ class ParserOutput extends CacheTime { /** @var integer|null Assumed rev ID for {{REVISIONID}} if no revision is set */ private $mSpeculativeRevId; + /** @var integer Upper bound of expiry based on parse duration */ + private $mMaxAdaptiveExpiry = INF; + const EDITSECTION_REGEX = '#<(?:mw:)?editsection page="(.*?)" section="(.*?)"(?:/>|>(.*?)())#'; + // finalizeAdaptiveCacheExpiry() uses TTL = MAX( m * PARSE_TIME + b, MIN_AR_TTL) + // Current values imply that m=3933.333333 and b=-333.333333 + // See https://www.nngroup.com/articles/website-response-times/ + const PARSE_FAST_SEC = .100; // perceived "fast" page parse + const PARSE_SLOW_SEC = 1.0; // perceived "slow" page parse + const FAST_AR_TTL = 60; // adaptive TTL for "fast" pages + const SLOW_AR_TTL = 3600; // adaptive TTL for "slow" pages + const MIN_AR_TTL = 15; // min adaptive TTL (for sanity, pool counter, and edit stashing) + public function __construct( $text = '', $languageLinks = [], $categoryLinks = [], $unused = false, $titletext = '' ) { @@ -1037,9 +1049,41 @@ class ParserOutput extends CacheTime { } /** - * Save space for serialization by removing useless values - * @return array + * Lower the runtime adaptive TTL to at most this value + * + * @param integer $ttl + * @since 1.28 + */ + public function updateRuntimeAdaptiveExpiry( $ttl ) { + $this->mMaxAdaptiveExpiry = min( $ttl, $this->mMaxAdaptiveExpiry ); + $this->updateCacheExpiry( $ttl ); + } + + /** + * Call this when parsing is done to lower the TTL based on low parse times + * + * @since 1.28 */ + public function finalizeAdaptiveCacheExpiry() { + if ( is_infinite( $this->mMaxAdaptiveExpiry ) ) { + return; // not set + } + + $runtime = $this->getTimeSinceStart( 'wall' ); + if ( is_float( $runtime ) ) { + $slope = ( self::SLOW_AR_TTL - self::FAST_AR_TTL ) + / ( self::PARSE_SLOW_SEC - self::PARSE_FAST_SEC ); + // SLOW_AR_TTL = PARSE_SLOW_SEC * $slope + $point + $point = self::SLOW_AR_TTL - self::PARSE_SLOW_SEC * $slope; + + $adaptiveTTL = min( + max( $slope * $runtime + $point, self::MIN_AR_TTL ), + $this->mMaxAdaptiveExpiry + ); + $this->updateCacheExpiry( $adaptiveTTL ); + } + } + public function __sleep() { return array_diff( array_keys( get_object_vars( $this ) ),