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/objectcache/ObjectCache.php | |
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/objectcache/ObjectCache.php')
-rw-r--r-- | includes/objectcache/ObjectCache.php | 209 |
1 files changed, 36 insertions, 173 deletions
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 ); |