Create ParserCacheFactory.

* Makes ParserCache take the root of the key
  as a constructor argument
* Introduces a ParserCacheFactory

Next steps:
- convert FlaggedRevs to using this.
- cleanup

This assumes that we wouldn't want to differentiate
the parser cache settings per use-case, as it is now
for default vs flaggedrevs caches. There are only two settings:
$wgParserCacheType - name of the BagOStuff to use
$wgParserCacheExpireTime - the expiration time.
I think if we wanted to have different settings for different
caches, we could add that as a next step.

Bug: T263583
Change-Id: I188772da541a95c95a5ecece7c7dd748395506c2
This commit is contained in:
Petr Pchelko 2020-09-25 13:02:03 -07:00
parent b21adfc7f8
commit fec48eb5a4
7 changed files with 163 additions and 11 deletions

View file

@ -1103,6 +1103,7 @@ $wgAutoloadLocalClasses = [
'MediaWiki\\Languages\\LanguageNameUtils' => __DIR__ . '/includes/language/LanguageNameUtils.php',
'MediaWiki\\Mail\\Emailer' => __DIR__ . '/includes/mail/Emailer.php',
'MediaWiki\\Mail\\IEmailer' => __DIR__ . '/includes/mail/IEmailer.php',
'MediaWiki\\Parser\\ParserCacheFactory' => __DIR__ . '/includes/parser/ParserCacheFactory.php',
'MediaWiki\\ProcOpenError' => __DIR__ . '/includes/exception/ProcOpenError.php',
'MediaWiki\\ShellDisabledError' => __DIR__ . '/includes/exception/ShellDisabledError.php',
'MediaWiki\\Skins\\Hook\\SkinAfterPortletHook' => __DIR__ . '/includes/skins/Hook/SkinAfterPortletHook.php',

View file

@ -54,6 +54,7 @@ use MediaWiki\Page\ContentModelChangeFactory;
use MediaWiki\Page\MergeHistoryFactory;
use MediaWiki\Page\MovePageFactory;
use MediaWiki\Page\WikiPageFactory;
use MediaWiki\Parser\ParserCacheFactory;
use MediaWiki\Permissions\PermissionManager;
use MediaWiki\Preferences\PreferencesFactory;
use MediaWiki\Revision\ContributionsLookup;
@ -1038,6 +1039,14 @@ class MediaWikiServices extends ServiceContainer {
return $this->getService( 'ParserCache' );
}
/**
* @since 1.36
* @return ParserCacheFactory
*/
public function getParserCacheFactory() : ParserCacheFactory {
return $this->getService( 'ParserCacheFactory' );
}
/**
* @since 1.32
* @return ParserFactory

View file

@ -86,6 +86,7 @@ use MediaWiki\Page\MergeHistoryFactory;
use MediaWiki\Page\MovePageFactory;
use MediaWiki\Page\PageCommandFactory;
use MediaWiki\Page\WikiPageFactory;
use MediaWiki\Parser\ParserCacheFactory;
use MediaWiki\Permissions\PermissionManager;
use MediaWiki\Preferences\DefaultPreferencesFactory;
use MediaWiki\Preferences\PreferencesFactory;
@ -866,15 +867,19 @@ return [
},
'ParserCache' => function ( MediaWikiServices $services ) : ParserCache {
return $services->getParserCacheFactory()
->getInstance( ParserCacheFactory::DEFAULT_NAME );
},
'ParserCacheFactory' => function ( MediaWikiServices $services ) : ParserCacheFactory {
$config = $services->getMainConfig();
$cache = ObjectCache::getInstance( $config->get( 'ParserCacheType' ) );
wfDebugLog( 'caches', 'parser: ' . get_class( $cache ) );
return new ParserCache(
return new ParserCacheFactory(
$cache,
$config->get( 'CacheEpoch' ),
$services->getHookContainer(),
$services->getStatsdDataFactory()
$services->getStatsdDataFactory(),
LoggerFactory::getInstance( 'ParserCache' )
);
},

View file

@ -50,6 +50,9 @@ class ParserCache {
*/
private const USE_ANYTHING = 3;
/** @var string The name of this ParserCache. Used as a root of the cache key. */
private $name;
/** @var BagOStuff */
private $cache;
@ -87,21 +90,25 @@ class ParserCache {
* @param string $cacheEpoch Anything before this timestamp is invalidated
* @param HookContainer $hookContainer
* @param IBufferingStatsdDataFactory|null $stats
* @throws MWException
* @param string $name
* TODO: $name: drop fallback and move to the first position once FlaggedRevs is migrated
*/
public function __construct(
BagOStuff $cache,
$cacheEpoch,
string $cacheEpoch,
HookContainer $hookContainer,
IBufferingStatsdDataFactory $stats = null
IBufferingStatsdDataFactory $stats = null,
string $name = 'pcache'
) {
$this->cache = $cache;
$this->cacheEpoch = $cacheEpoch;
$this->hookRunner = new HookRunner( $hookContainer );
$this->stats = $stats ?: MediaWikiServices::getInstance()->getStatsdDataFactory();
$this->name = $name;
}
/**
* TODO: make private once FlaggedRevs is migrated to ParserCacheFactory
* @param WikiPage $wikiPage
* @param string $hash
* @return mixed|string
@ -113,16 +120,17 @@ class ParserCache {
$pageid = $wikiPage->getId();
$renderkey = (int)( $wgRequest->getVal( 'action' ) == 'render' );
$key = $this->cache->makeKey( 'pcache', 'idhash', "{$pageid}-{$renderkey}!{$hash}" );
$key = $this->cache->makeKey( $this->name, 'idhash', "{$pageid}-{$renderkey}!{$hash}" );
return $key;
}
/**
* TODO: make private once FlaggedRevs is migrated to ParserCacheFactory
* @param WikiPage $wikiPage
* @return mixed|string
*/
protected function getOptionsKey( WikiPage $wikiPage ) {
return $this->cache->makeKey( 'pcache', 'idoptions', $wikiPage->getId() );
return $this->cache->makeKey( $this->name, 'idoptions', $wikiPage->getId() );
}
/**
@ -177,7 +185,7 @@ class ParserCache {
private function incrementStats( WikiPage $wikiPage, $metricSuffix ) {
$contentModel = str_replace( '.', '_', $wikiPage->getContentModel() );
$metricSuffix = str_replace( '.', '_', $metricSuffix );
$this->stats->increment( 'pcache.' . $contentModel . '.' . $metricSuffix );
$this->stats->increment( "{$this->name}.{$contentModel}.{$metricSuffix}" );
}
/**

View file

@ -0,0 +1,97 @@
<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
* @ingroup Cache Parser
*/
namespace MediaWiki\Parser;
use BagOStuff;
use IBufferingStatsdDataFactory;
use MediaWiki\HookContainer\HookContainer;
use ParserCache;
use Psr\Log\LoggerInterface;
/**
* Returns an instance of the ParserCache by its name.
* @since 1.36
* @package MediaWiki\Parser
*/
class ParserCacheFactory {
/** @var string name of ParserCache for the default parser */
public const DEFAULT_NAME = 'pcache';
/** @var BagOStuff */
private $cacheBackend;
/** @var string */
private $cacheEpoch;
/** @var HookContainer */
private $hookContainer;
/** @var IBufferingStatsdDataFactory */
private $stats;
/** @var LoggerInterface */
private $logger;
/** @var array */
private $instanceCache = [];
/**
* @param BagOStuff $cacheBackend
* @param string $cacheEpoch
* @param HookContainer $hookContainer
* @param IBufferingStatsdDataFactory $stats
* @param LoggerInterface $logger
*/
public function __construct(
BagOStuff $cacheBackend,
string $cacheEpoch,
HookContainer $hookContainer,
IBufferingStatsdDataFactory $stats,
LoggerInterface $logger
) {
$this->cacheBackend = $cacheBackend;
$this->cacheEpoch = $cacheEpoch;
$this->hookContainer = $hookContainer;
$this->stats = $stats;
$this->logger = $logger;
}
/**
* Get a ParserCache instance by $name.
* @param string $name
* @return ParserCache
*/
public function getInstance( string $name ) : ParserCache {
if ( !isset( $this->instanceCache[$name] ) ) {
$this->logger->debug( "Creating ParserCache instance for {$name}" );
$this->instanceCache[$name] = new ParserCache(
$this->cacheBackend,
$this->cacheEpoch,
$this->hookContainer,
$this->stats,
$name
);
}
return $this->instanceCache[$name];
}
}

View file

@ -92,7 +92,7 @@ trait FactoryArgTestTrait {
$pos = $param->getPosition();
$type = $param->getType();
if ( !$type ) {
if ( !$type || $type->getName() === 'string' ) {
// Optimistically assume a string is okay
return "some unlikely string $pos";
}

View file

@ -0,0 +1,32 @@
<?php
use MediaWiki\Parser\ParserCacheFactory;
/**
* @covers \MediaWiki\Parser\ParserCacheFactory
*/
class ParserCacheFactoryTest extends MediaWikiUnitTestCase {
use FactoryArgTestTrait;
protected static function getFactoryClass() {
return ParserCacheFactory::class;
}
protected static function getInstanceClass() {
return ParserCache::class;
}
protected static function getFactoryMethodName() {
return 'getInstance';
}
protected static function getExtraClassArgCount() {
// +1 $name
// -1 $logger
return 0;
}
protected function getIgnoredParamNames() {
return [ 'hookContainer', 'logger' ];
}
}