diff options
author | Derick Alangi <alangiderick@gmail.com> | 2023-09-07 16:12:17 +0100 |
---|---|---|
committer | Derick Alangi <alangiderick@gmail.com> | 2024-03-19 12:38:39 +0300 |
commit | d372626b97e83ec1f82b88e75fbaadf99ad2f89b (patch) | |
tree | eed4c9df0653d4a6d4d08792dcfcbb2a2cad08f6 /includes | |
parent | 39af55050450d08fb51a0a358a24bd204a21d7e1 (diff) | |
download | mediawikicore-d372626b97e83ec1f82b88e75fbaadf99ad2f89b.tar.gz mediawikicore-d372626b97e83ec1f82b88e75fbaadf99ad2f89b.zip |
objectcache: Introduce `ObjectCacheFactory` MW service
ObjectCache is already doing a lot of factory pattern logic like
creating instances of the various BagOStuff, this should really be
the responsibility of the factory servicet.
This patch introduces a proper factory (ObjectCacheFactory) to handle
the responsibility of creating various instances of BagOStuff. Since
`newFromParams()` is a static function that gets passed in configuration
of $wgObjectCaches, that can stay that way (to keep supporting how we do
this in prod today).
Technical Breaking Change: `ObjectCache::makeLocalServerCache()` now has
a parameter and requires it but there are no callers of this method outside
MW core hence it is safe to change (and this patch update all callers) to
work correctly. Cache prefix is gotten from global state because sometimes
at this stage, the services container is not available.
Bug: T358346
Change-Id: I3179a387486377c6a575d173f39f82870c49c321
Diffstat (limited to 'includes')
-rw-r--r-- | includes/MainConfigSchema.php | 3 | ||||
-rw-r--r-- | includes/MediaWikiServices.php | 9 | ||||
-rw-r--r-- | includes/ServiceWiring.php | 40 | ||||
-rw-r--r-- | includes/config-schema.php | 6 | ||||
-rw-r--r-- | includes/objectcache/ObjectCache.php | 209 | ||||
-rw-r--r-- | includes/objectcache/ObjectCacheFactory.php | 295 | ||||
-rw-r--r-- | includes/registration/ExtensionRegistry.php | 3 |
7 files changed, 357 insertions, 208 deletions
diff --git a/includes/MainConfigSchema.php b/includes/MainConfigSchema.php index bf85fdd462a5..7274f55e8185 100644 --- a/includes/MainConfigSchema.php +++ b/includes/MainConfigSchema.php @@ -3959,9 +3959,6 @@ class MainConfigSchema { CACHE_NONE => [ 'class' => EmptyBagOStuff::class, 'reportDupes' => false ], CACHE_DB => [ 'class' => SqlBagOStuff::class, 'loggroup' => 'SQLBagOStuff' ], - CACHE_ANYTHING => [ 'factory' => 'ObjectCache::newAnything' ], - CACHE_ACCEL => [ 'factory' => 'ObjectCache::getLocalServerInstance' ], - 'memcached-php' => [ 'class' => MemcachedPhpBagOStuff::class, 'loggroup' => 'memcached' ], 'memcached-pecl' => [ 'class' => MemcachedPeclBagOStuff::class, 'loggroup' => 'memcached' ], 'hash' => [ 'class' => HashBagOStuff::class, 'reportDupes' => false ], diff --git a/includes/MediaWikiServices.php b/includes/MediaWikiServices.php index ba80dcfda3a9..090eb0cc3b27 100644 --- a/includes/MediaWikiServices.php +++ b/includes/MediaWikiServices.php @@ -188,6 +188,7 @@ use MessageCache; use MimeAnalyzer; use MWLBFactory; use ObjectCache; +use ObjectCacheFactory; use OldRevisionImporter; use ParserCache; use ParserFactory; @@ -1462,6 +1463,14 @@ class MediaWikiServices extends ServiceContainer { } /** + * @since 1.42 + * @return ObjectCacheFactory + */ + public function getObjectCacheFactory(): ObjectCacheFactory { + return $this->getService( 'ObjectCacheFactory' ); + } + + /** * ObjectFactory is intended for instantiating "handlers" from declarative definitions, * such as Action API modules, special pages, or REST API handlers. * diff --git a/includes/ServiceWiring.php b/includes/ServiceWiring.php index 3280d61980dc..dcc72b364a51 100644 --- a/includes/ServiceWiring.php +++ b/includes/ServiceWiring.php @@ -1180,7 +1180,7 @@ return [ }, 'LocalServerObjectCache' => static function ( MediaWikiServices $services ): BagOStuff { - return ObjectCache::makeLocalServerCache(); + return $services->getObjectCacheFactory()->getInstance( CACHE_ACCEL ); }, 'LockManagerGroupFactory' => static function ( MediaWikiServices $services ): LockManagerGroupFactory { @@ -1206,14 +1206,7 @@ return [ $mainConfig = $services->getMainConfig(); $id = $mainConfig->get( MainConfigNames::MainStash ); - $params = $mainConfig->get( MainConfigNames::ObjectCaches )[$id] ?? null; - if ( !$params ) { - throw new ConfigException( - "\$wgObjectCaches must have \"$id\" set (via \$wgMainStash)" - ); - } - - $store = ObjectCache::newFromParams( $params, $services ); + $store = $services->getObjectCacheFactory()->getInstance( $id ); $store->getLogger()->debug( 'MainObjectStash using store {class}', [ 'class' => get_class( $store ) ] ); @@ -1294,15 +1287,7 @@ return [ $mainConfig = $services->getMainConfig(); $id = $mainConfig->get( MainConfigNames::MicroStashType ); - - $params = $mainConfig->get( MainConfigNames::ObjectCaches )[$id] ?? null; - if ( !$params ) { - throw new ConfigException( - "\$wgObjectCaches must have \"$id\" set (via \$wgMicroStashType)" - ); - } - - $store = ObjectCache::newFromParams( $params, $services ); + $store = $services->getObjectCacheFactory()->getInstance( $id ); $store->getLogger()->debug( 'MicroStash using store {class}', [ 'class' => get_class( $store ) @@ -1390,6 +1375,17 @@ return [ ); }, + 'ObjectCacheFactory' => static function ( MediaWikiServices $services ): ObjectCacheFactory { + return new ObjectCacheFactory( + new ServiceOptions( + ObjectCacheFactory::CONSTRUCTOR_OPTIONS, + $services->getMainConfig() + ), + $services->getStatsFactory(), + LoggerFactory::getProvider() + ); + }, + 'ObjectFactory' => static function ( MediaWikiServices $services ): ObjectFactory { return new ObjectFactory( $services ); }, @@ -2584,13 +2580,7 @@ return [ '_LocalClusterCache' => static function ( MediaWikiServices $services ): BagOStuff { $mainConfig = $services->getMainConfig(); $id = $mainConfig->get( MainConfigNames::MainCacheType ); - $params = $mainConfig->get( MainConfigNames::ObjectCaches )[$id] ?? null; - if ( !$params ) { - throw new ConfigException( - "\$wgObjectCaches must have \"$id\" set (via \$wgMainCacheType)" - ); - } - return ObjectCache::newFromParams( $params, $services ); + return $services->getObjectCacheFactory()->getInstance( $id ); }, '_MediaWikiTitleCodec' => static function ( MediaWikiServices $services ): MediaWikiTitleCodec { diff --git a/includes/config-schema.php b/includes/config-schema.php index c8bcd37d671f..4a9356f6b447 100644 --- a/includes/config-schema.php +++ b/includes/config-schema.php @@ -471,12 +471,6 @@ return [ 'class' => 'SqlBagOStuff', 'loggroup' => 'SQLBagOStuff', ], - -1 => [ - 'factory' => 'ObjectCache::newAnything', - ], - 3 => [ - 'factory' => 'ObjectCache::getLocalServerInstance', - ], 'memcached-php' => [ 'class' => 'MemcachedPhpBagOStuff', 'loggroup' => 'memcached', diff --git a/includes/objectcache/ObjectCache.php b/includes/objectcache/ObjectCache.php index 91ea0a57daee..129bb46d1268 100644 --- a/includes/objectcache/ObjectCache.php +++ b/includes/objectcache/ObjectCache.php @@ -21,12 +21,7 @@ * @ingroup Cache */ -use MediaWiki\Deferred\DeferredUpdates; -use MediaWiki\Http\Telemetry; -use MediaWiki\Logger\LoggerFactory; -use MediaWiki\MainConfigNames; use MediaWiki\MediaWikiServices; -use MediaWiki\WikiMap\WikiMap; /** * Functions to get cache objects @@ -66,7 +61,10 @@ use MediaWiki\WikiMap\WikiMap; * @ingroup Cache */ class ObjectCache { - /** @var BagOStuff[] Map of (id => BagOStuff) */ + /** + * @deprecated Use ObjectCacheFactory instead. + * @var BagOStuff[] Map of (id => BagOStuff) + */ public static $instances = []; /** @@ -78,157 +76,26 @@ class ObjectCache { /** * Get a cached instance of the specified type of cache object. * - * @param string|int $id A key in $wgObjectCaches. - * @return BagOStuff - */ - public static function getInstance( $id ) { - if ( !isset( self::$instances[$id] ) ) { - self::$instances[$id] = self::newFromId( $id ); - } - - return self::$instances[$id]; - } - - /** - * Create a new cache object of the specified type. + * @deprecated Use ObjectCacheFactory::getInstance instead. * * @param string|int $id A key in $wgObjectCaches. * @return BagOStuff - * @throws InvalidArgumentException */ - private static function newFromId( $id ) { - global $wgObjectCaches; - - if ( !isset( $wgObjectCaches[$id] ) ) { - // Always recognize these ones - if ( $id === CACHE_NONE ) { - return new EmptyBagOStuff(); - } elseif ( $id === CACHE_HASH ) { - return new HashBagOStuff(); - } - - throw new InvalidArgumentException( "Invalid object cache type \"$id\" requested. " . - "It is not present in \$wgObjectCaches." ); - } - - return self::newFromParams( $wgObjectCaches[$id] ); + public static function getInstance( $id ) { + return MediaWikiServices::getInstance()->getObjectCacheFactory()->getInstance( $id ); } /** - * Get the default keyspace for this wiki. + * @see ObjectCacheFactory::newFromParams() * - * This is either the value of the `CachePrefix` configuration variable, - * or (if the former is unset) the `DBname` configuration variable, with - * `DBprefix` (if defined). - * - * @return string - */ - private static function getDefaultKeyspace() { - global $wgCachePrefix; - - $keyspace = $wgCachePrefix; - if ( is_string( $keyspace ) && $keyspace !== '' ) { - return $keyspace; - } - - return WikiMap::getCurrentWikiDbDomain()->getId(); - } - - /** - * Create a new cache object from parameters. + * @deprecated since 1.42, Use ObjectCacheFactory::newFromParams instead. + * @param array $params * - * @param array $params Must have 'factory' or 'class' property. - * - factory: Callback passed $params that returns BagOStuff. - * - class: BagOStuff subclass constructed with $params. - * - loggroup: Alias to set 'logger' key with LoggerFactory group. - * - .. Other parameters passed to factory or class. - * @param MediaWikiServices|null $services [internal] * @return BagOStuff */ - public static function newFromParams( array $params, MediaWikiServices $services = null ) { - $services ??= MediaWikiServices::getInstance(); - $conf = $services->getMainConfig(); - - // Apply default parameters and resolve the logger instance - $params += [ - 'logger' => LoggerFactory::getInstance( $params['loggroup'] ?? 'objectcache' ), - 'keyspace' => self::getDefaultKeyspace(), - 'asyncHandler' => [ DeferredUpdates::class, 'addCallableUpdate' ], - 'reportDupes' => true, - 'stats' => $services->getStatsFactory(), - ]; - - if ( isset( $params['factory'] ) ) { - $args = $params['args'] ?? [ $params ]; - - return call_user_func( $params['factory'], ...$args ); - } - - if ( !isset( $params['class'] ) ) { - throw new InvalidArgumentException( - 'No "factory" nor "class" provided; got "' . print_r( $params, true ) . '"' - ); - } - - $class = $params['class']; - - // Normalization and DI for SqlBagOStuff - if ( is_a( $class, SqlBagOStuff::class, true ) ) { - if ( isset( $params['globalKeyLB'] ) ) { - throw new InvalidArgumentException( - 'globalKeyLB in $wgObjectCaches is no longer supported' ); - } - if ( isset( $params['server'] ) && !isset( $params['servers'] ) ) { - $params['servers'] = [ $params['server'] ]; - unset( $params['server'] ); - } - if ( isset( $params['servers'] ) ) { - // In the past it was not required to set 'dbDirectory' in $wgObjectCaches - foreach ( $params['servers'] as &$server ) { - if ( $server['type'] === 'sqlite' && !isset( $server['dbDirectory'] ) ) { - $server['dbDirectory'] = $conf->get( MainConfigNames::SQLiteDataDir ); - } - } - } elseif ( isset( $params['cluster'] ) ) { - $cluster = $params['cluster']; - $params['loadBalancerCallback'] = static function () use ( $services, $cluster ) { - return $services->getDBLoadBalancerFactory()->getExternalLB( $cluster ); - }; - $params += [ 'dbDomain' => false ]; - } else { - $params['loadBalancerCallback'] = static function () use ( $services ) { - return $services->getDBLoadBalancer(); - }; - $params += [ 'dbDomain' => false ]; - } - $params += [ 'writeBatchSize' => $conf->get( MainConfigNames::UpdateRowsPerQuery ) ]; - } - - // Normalization and DI for MemcachedBagOStuff - if ( is_subclass_of( $class, MemcachedBagOStuff::class ) ) { - $params += [ - 'servers' => $conf->get( MainConfigNames::MemCachedServers ), - 'persistent' => $conf->get( MainConfigNames::MemCachedPersistent ), - 'timeout' => $conf->get( MainConfigNames::MemCachedTimeout ), - ]; - } - - // Normalization and DI for MultiWriteBagOStuff - if ( is_a( $class, MultiWriteBagOStuff::class, true ) ) { - // Phan warns about foreach with non-array because it - // thinks any key can be Closure|IBufferingStatsdDataFactory - '@phan-var array{caches:array[]} $params'; - foreach ( $params['caches'] ?? [] as $i => $cacheInfo ) { - // Ensure logger, keyspace, asyncHandler, etc are injected just as if - // one of these was configured without MultiWriteBagOStuff. - $params['caches'][$i] = self::newFromParams( $cacheInfo, $services ); - } - } - if ( is_a( $class, RESTBagOStuff::class, true ) ) { - $params['telemetry'] = Telemetry::getInstance(); - } - - return new $class( $params ); + public static function newFromParams( array $params ) { + return MediaWikiServices::getInstance()->getObjectCacheFactory() + ->newFromParams( $params ); } /** @@ -241,18 +108,23 @@ class ObjectCache { * If no cache choice is configured (by default $wgMainCacheType is CACHE_NONE), * then CACHE_ANYTHING will forward to CACHE_DB. * - * @param array $params + * @deprecated since 1.42, + * Use ObjectCacheFactory::newInstance( ObjectCache::getAnythingId() ); + * * @return BagOStuff */ - public static function newAnything( $params ) { - return self::getInstance( self::getAnythingId() ); + public static function newAnything() { + return MediaWikiServices::getInstance()->getObjectCacheFactory() + ->getInstance( self::getAnythingId() ); } /** + * @internal Used by ObjectCacheFactory and ObjectCache. + * * Get the ID that will be used for CACHE_ANYTHING * @return string|int */ - private static function getAnythingId() { + public static function getAnythingId() { global $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType; $candidates = [ $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType ]; foreach ( $candidates as $candidate ) { @@ -284,16 +156,8 @@ class ObjectCache { } /** - * Factory function for CACHE_ACCEL (referenced from configuration) - * - * This will look for any APC or APCu style server-local cache. - * A fallback cache can be specified if none is found. - * - * // Direct calls - * ObjectCache::getLocalServerInstance( $fallbackType ); - * - * // From $wgObjectCaches via newFromParams() - * ObjectCache::getLocalServerInstance( [ 'fallback' => $fallbackType ] ); + * @deprecated since 1.42, Use ObjectCacheFactory::getLocaServerInstance() + * @see ObjectCacheFactory::getLocalServerInstance() * * @param int|string|array $fallback Fallback cache or parameter map with 'fallback' * @return BagOStuff @@ -301,15 +165,8 @@ class ObjectCache { * @since 1.27 */ public static function getLocalServerInstance( $fallback = CACHE_NONE ) { - $cache = MediaWikiServices::getInstance()->getLocalServerObjectCache(); - if ( $cache instanceof EmptyBagOStuff ) { - if ( is_array( $fallback ) ) { - $fallback = $fallback['fallback'] ?? CACHE_NONE; - } - $cache = self::getInstance( $fallback ); - } - - return $cache; + return MediaWikiServices::getInstance()->getObjectCacheFactory() + ->getLocalServerInstance( $fallback ); } /** @@ -337,7 +194,7 @@ class ObjectCache { if ( ( $cache['class'] ?? '' ) === SqlBagOStuff::class ) { return true; } - if ( ( $cache['factory'] ?? '' ) === 'ObjectCache::newAnything' ) { + if ( $id === CACHE_ANYTHING ) { $id = self::getAnythingId(); return self::isDatabaseId( $id ); } @@ -345,10 +202,12 @@ class ObjectCache { } /** + * @deprecated since 1.42, Use ObjectCacheFactory::clear() instead. + * * Clear all the cached instances. */ public static function clear() { - self::$instances = []; + MediaWikiServices::getInstance()->getObjectCacheFactory()->clear(); } /** @@ -362,14 +221,18 @@ class ObjectCache { * and thus must remain fairly standalone so as to not cause initialization * of the MediaWikiServices singleton. * + * @internal For use by ServiceWiring and ExtensionRegistry. There are use + * cases whereby we want to build up local server cache without service + * wiring available. * @since 1.35 + * @param string|false $cachePrefix * @return BagOStuff */ - public static function makeLocalServerCache(): BagOStuff { + public static function makeLocalServerCache( $cachePrefix ): BagOStuff { $params = [ 'reportDupes' => false, // Even simple caches must use a keyspace (T247562) - 'keyspace' => self::getDefaultKeyspace(), + 'keyspace' => $cachePrefix, ]; $class = self::getLocalServerCacheClass(); return new $class( $params ); diff --git a/includes/objectcache/ObjectCacheFactory.php b/includes/objectcache/ObjectCacheFactory.php new file mode 100644 index 000000000000..ebcbda18003e --- /dev/null +++ b/includes/objectcache/ObjectCacheFactory.php @@ -0,0 +1,295 @@ +<?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 + */ + +use MediaWiki\Config\ServiceOptions; +use MediaWiki\Http\Telemetry; +use MediaWiki\Logger\Spi; +use MediaWiki\MainConfigNames; +use MediaWiki\MediaWikiServices; +use MediaWiki\WikiMap\WikiMap; +use Wikimedia\Stats\StatsFactory; + +/** + * Factory for cache objects as configured in the ObjectCaches setting. + * + * @ingroup Cache + * @since 1.42 + */ +class ObjectCacheFactory { + /** + * @internal For use by ServiceWiring.php + * @var array + */ + public const CONSTRUCTOR_OPTIONS = [ + MainConfigNames::SQLiteDataDir, + MainConfigNames::UpdateRowsPerQuery, + MainConfigNames::MemCachedServers, + MainConfigNames::MemCachedPersistent, + MainConfigNames::MemCachedTimeout, + MainConfigNames::CachePrefix, + MainConfigNames::ObjectCaches, + MainConfigNames::MainCacheType, + ]; + + private ServiceOptions $options; + private StatsFactory $stats; + private Spi $logger; + /** @var BagOStuff[] */ + private $instances = []; + + public function __construct( + ServiceOptions $options, + StatsFactory $stats, + Spi $loggerSpi + ) { + $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS ); + $this->options = $options; + $this->stats = $stats; + $this->logger = $loggerSpi; + } + + /** + * Get the default keyspace for this wiki. + * + * This is either the value of the `CachePrefix` configuration variable, + * or (if the former is unset) the `DBname` configuration variable, with + * `DBprefix` (if defined). + * + * @return string + */ + private function getDefaultKeyspace(): string { + $cachePrefix = $this->options->get( MainConfigNames::CachePrefix ); + if ( is_string( $cachePrefix ) && $cachePrefix !== '' ) { + return $cachePrefix; + } + + return WikiMap::getCurrentWikiDbDomain()->getId(); + } + + /** + * Create a new cache object of the specified type. + * + * @param string|int $id A key in $wgObjectCaches. + * @return BagOStuff + * @throws InvalidArgumentException + */ + private function newFromId( $id ): BagOStuff { + if ( $id === CACHE_ANYTHING ) { + $id = ObjectCache::getAnythingId(); + } + + if ( !isset( $this->options->get( MainConfigNames::ObjectCaches )[$id] ) ) { + // Always recognize these ones + if ( $id === CACHE_NONE ) { + return new EmptyBagOStuff(); + } elseif ( $id === CACHE_HASH ) { + return new HashBagOStuff(); + } elseif ( $id === CACHE_ACCEL ) { + return ObjectCache::makeLocalServerCache( + $this->options->get( MainConfigNames::CachePrefix ) + ); + } + + throw new InvalidArgumentException( "Invalid object cache type \"$id\" requested. " . + "It is not present in \$wgObjectCaches." ); + } + + return $this->newFromParams( $this->options->get( MainConfigNames::ObjectCaches )[$id] ); + } + + /** + * Get a cached instance of the specified type of cache object. + * + * @param string|int $id A key in $wgObjectCaches. + * @return BagOStuff + */ + public function getInstance( $id ): BagOStuff { + if ( !isset( $this->instances[$id] ) ) { + $this->instances[$id] = $this->newFromId( $id ); + } + + return $this->instances[$id]; + } + + /** + * @internal Using this method directly outside of MediaWiki core + * is discouraged. Use getInstance() instead and supply the ID + * of the cache instance to be looked up. + * + * Create a new cache object from parameters specification supplied. + * + * @param array $params Must have 'factory' or 'class' property. + * - factory: Callback passed $params that returns BagOStuff. + * - class: BagOStuff subclass constructed with $params. + * - loggroup: Alias to set 'logger' key with LoggerFactory group. + * - .. Other parameters passed to factory or class. + * + * @return BagOStuff + */ + public function newFromParams( array $params ): BagOStuff { + $logger = $this->logger->getLogger( $params['loggroup'] ?? 'objectcache' ); + // Apply default parameters and resolve the logger instance + $params += [ + 'logger' => $logger, + 'keyspace' => $this->getDefaultKeyspace(), + 'asyncHandler' => [ DeferredUpdates::class, 'addCallableUpdate' ], + 'reportDupes' => true, + 'stats' => $this->stats, + ]; + + if ( isset( $params['factory'] ) ) { + $args = $params['args'] ?? [ $params ]; + + return call_user_func( $params['factory'], ...$args ); + } + + if ( !isset( $params['class'] ) ) { + throw new InvalidArgumentException( + 'No "factory" nor "class" provided; got "' . print_r( $params, true ) . '"' + ); + } + + $class = $params['class']; + + // Normalization and DI for SqlBagOStuff + if ( is_a( $class, SqlBagOStuff::class, true ) ) { + $this->prepareSqlBagOStuffFromParams( $params ); + } + + // Normalization and DI for MemcachedBagOStuff + if ( is_subclass_of( $class, MemcachedBagOStuff::class ) ) { + $this->prepareMemcachedBagOStuffFromParams( $params ); + } + + // Normalization and DI for MultiWriteBagOStuff + if ( is_a( $class, MultiWriteBagOStuff::class, true ) ) { + $this->prepareMultiWriteBagOStuffFromParams( $params ); + } + if ( is_a( $class, RESTBagOStuff::class, true ) ) { + $this->prepareRESTBagOStuffFromParams( $params ); + } + + return new $class( $params ); + } + + private function prepareSqlBagOStuffFromParams( array &$params ): void { + if ( isset( $params['globalKeyLB'] ) ) { + throw new InvalidArgumentException( + 'globalKeyLB in $wgObjectCaches is no longer supported' ); + } + if ( isset( $params['server'] ) && !isset( $params['servers'] ) ) { + $params['servers'] = [ $params['server'] ]; + unset( $params['server'] ); + } + if ( isset( $params['servers'] ) ) { + // In the past it was not required to set 'dbDirectory' in $wgObjectCaches + foreach ( $params['servers'] as &$server ) { + if ( $server['type'] === 'sqlite' && !isset( $server['dbDirectory'] ) ) { + $server['dbDirectory'] = $this->options->get( MainConfigNames::SQLiteDataDir ); + } + } + } elseif ( isset( $params['cluster'] ) ) { + $cluster = $params['cluster']; + $params['loadBalancerCallback'] = static function () use ( $cluster ) { + return MediaWikiServices::getInstance()->getDBLoadBalancerFactory() + ->getExternalLB( $cluster ); + }; + $params += [ 'dbDomain' => false ]; + } else { + $params['loadBalancerCallback'] = static function () { + return MediaWikiServices::getInstance()->getDBLoadBalancer(); + }; + $params += [ 'dbDomain' => false ]; + } + $params += [ 'writeBatchSize' => $this->options->get( MainConfigNames::UpdateRowsPerQuery ) ]; + } + + private function prepareMemcachedBagOStuffFromParams( array &$params ): void { + $params += [ + 'servers' => $this->options->get( MainConfigNames::MemCachedServers ), + 'persistent' => $this->options->get( MainConfigNames::MemCachedPersistent ), + 'timeout' => $this->options->get( MainConfigNames::MemCachedTimeout ), + ]; + } + + private function prepareMultiWriteBagOStuffFromParams( array &$params ): void { + // Phan warns about foreach with non-array because it + // thinks any key can be Closure|IBufferingStatsdDataFactory + '@phan-var array{caches:array[]} $params'; + foreach ( $params['caches'] ?? [] as $i => $cacheInfo ) { + // Ensure logger, keyspace, asyncHandler, etc are injected just as if + // one of these was configured without MultiWriteBagOStuff. + $params['caches'][$i] = $this->newFromParams( $cacheInfo ); + } + } + + private function prepareRESTBagOStuffFromParams( array &$params ): void { + $params['telemetry'] = Telemetry::getInstance(); + } + + /** + * Factory function for CACHE_ACCEL (referenced from configuration) + * + * This will look for any APC or APCu style server-local cache. + * A fallback cache can be specified if none is found. + * + * // Direct calls + * ObjectCache::getLocalServerInstance( $fallbackType ); + * + * // From $wgObjectCaches via newFromParams() + * ObjectCache::getLocalServerInstance( [ 'fallback' => $fallbackType ] ); + * + * @param int|string|array $fallback Fallback cache or parameter map with 'fallback' + * @return BagOStuff + * @throws InvalidArgumentException + */ + public function getLocalServerInstance( $fallback = CACHE_NONE ): BagOStuff { + $cache = $this->getInstance( CACHE_ACCEL ); + if ( $cache instanceof EmptyBagOStuff ) { + if ( is_array( $fallback ) ) { + $fallback = $fallback['fallback'] ?? CACHE_NONE; + } + $cache = $this->getInstance( $fallback ); + } + + return $cache; + } + + /** + * Clear all the cached instances. + */ + public function clear(): void { + $this->instances = []; + } + + /** + * @internal For tests ONLY. + * + * @param string|int $cacheId + * @param BagOStuff $cache + * @return void + */ + public function setInstanceForTesting( $cacheId, BagOStuff $cache ): void { + if ( !defined( 'MW_PHPUNIT_TEST' ) ) { + throw new LogicException( __METHOD__ . ' can not be called outside of tests' ); + } + $this->instances[$cacheId] = $cache; + } +} diff --git a/includes/registration/ExtensionRegistry.php b/includes/registration/ExtensionRegistry.php index 2eedfe6dd949..90cd08cb334d 100644 --- a/includes/registration/ExtensionRegistry.php +++ b/includes/registration/ExtensionRegistry.php @@ -244,9 +244,10 @@ class ExtensionRegistry { private function getCache(): BagOStuff { if ( !$this->cache ) { + global $wgCachePrefix; // Can't call MediaWikiServices here, as we must not cause services // to be instantiated before extensions have loaded. - return ObjectCache::makeLocalServerCache(); + return ObjectCache::makeLocalServerCache( $wgCachePrefix ); } return $this->cache; |