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
This commit is contained in:
parent
2bc631d261
commit
97f004694c
2 changed files with 57 additions and 5 deletions
|
|
@ -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() ) ) {
|
||||
|
|
|
|||
|
|
@ -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="(.*?)"(?:/>|>(.*?)(</(?:mw:)?editsection>))#';
|
||||
|
||||
// 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 ) ),
|
||||
|
|
|
|||
Loading…
Reference in a new issue