80 public const CONSTRUCTOR_OPTIONS = [
81 MainConfigNames::SQLiteDataDir,
82 MainConfigNames::UpdateRowsPerQuery,
83 MainConfigNames::MemCachedServers,
84 MainConfigNames::MemCachedPersistent,
85 MainConfigNames::MemCachedTimeout,
86 MainConfigNames::CachePrefix,
87 MainConfigNames::ObjectCaches,
88 MainConfigNames::MainCacheType,
89 MainConfigNames::MessageCacheType,
90 MainConfigNames::ParserCacheType,
97 private $instances = [];
98 private string $domainId;
100 private $dbLoadBalancerFactory;
111 callable $dbLoadBalancerFactory,
115 $this->options = $options;
116 $this->stats = $stats;
117 $this->logger = $loggerSpi;
118 $this->dbLoadBalancerFactory = $dbLoadBalancerFactory;
119 $this->domainId = $domainId;
131 private function getDefaultKeyspace(): string {
133 if ( is_string( $cachePrefix ) && $cachePrefix !==
'' ) {
137 return $this->domainId;
146 private function newFromId( $id ):
BagOStuff {
148 $id = $this->getAnythingId();
151 if ( !isset( $this->options->get( MainConfigNames::ObjectCaches )[$id] ) ) {
158 return self::makeLocalServerCache( $this->getDefaultKeyspace() );
161 throw new InvalidArgumentException(
"Invalid object cache type \"$id\" requested. " .
162 "It is not present in \$wgObjectCaches." );
165 return $this->newFromParams( $this->options->get( MainConfigNames::ObjectCaches )[$id] );
175 if ( !isset( $this->instances[$id] ) ) {
176 $this->instances[$id] = $this->newFromId( $id );
179 return $this->instances[$id];
198 $logger = $this->logger->getLogger(
$params[
'loggroup'] ??
'objectcache' );
202 'keyspace' => $this->getDefaultKeyspace(),
203 'asyncHandler' => [ DeferredUpdates::class,
'addCallableUpdate' ],
204 'reportDupes' =>
true,
205 'stats' => $this->stats,
208 if ( isset(
$params[
'factory'] ) ) {
211 return call_user_func(
$params[
'factory'], ...$args );
214 if ( !isset(
$params[
'class'] ) ) {
215 throw new InvalidArgumentException(
216 'No "factory" nor "class" provided; got "' . print_r(
$params,
true ) .
'"'
223 if ( is_a( $class, SqlBagOStuff::class,
true ) ) {
224 $this->prepareSqlBagOStuffFromParams(
$params );
228 if ( is_subclass_of( $class, MemcachedBagOStuff::class ) ) {
229 $this->prepareMemcachedBagOStuffFromParams(
$params );
233 if ( is_a( $class, MultiWriteBagOStuff::class,
true ) ) {
234 $this->prepareMultiWriteBagOStuffFromParams(
$params );
236 if ( is_a( $class, RESTBagOStuff::class,
true ) ) {
237 $this->prepareRESTBagOStuffFromParams(
$params );
243 private function prepareSqlBagOStuffFromParams( array &
$params ): void {
245 throw new InvalidArgumentException(
246 'globalKeyLB in $wgObjectCaches is no longer supported' );
248 if ( isset(
$params[
'server'] ) && !isset(
$params[
'servers'] ) ) {
252 if ( isset(
$params[
'servers'] ) ) {
254 foreach (
$params[
'servers'] as &$server ) {
255 if ( $server[
'type'] ===
'sqlite' && !isset( $server[
'dbDirectory'] ) ) {
256 $server[
'dbDirectory'] = $this->options->get( MainConfigNames::SQLiteDataDir );
259 } elseif ( isset(
$params[
'cluster'] ) ) {
261 $dbLbFactory = $this->dbLoadBalancerFactory;
262 $params[
'loadBalancerCallback'] =
static function () use ( $cluster, $dbLbFactory ) {
263 return $dbLbFactory()->getExternalLB( $cluster );
265 $params += [
'dbDomain' => false ];
267 $dbLbFactory = $this->dbLoadBalancerFactory;
268 $params[
'loadBalancerCallback'] =
static function () use ( $dbLbFactory ) {
269 return $dbLbFactory()->getMainLb();
271 $params += [
'dbDomain' => false ];
273 $params += [
'writeBatchSize' => $this->options->get( MainConfigNames::UpdateRowsPerQuery ) ];
276 private function prepareMemcachedBagOStuffFromParams( array &
$params ): void {
279 'persistent' => $this->options->get(
MainConfigNames::MemCachedPersistent ),
284 private function prepareMultiWriteBagOStuffFromParams( array &
$params ): void {
287 '@phan-var array{caches:array[]} $params';
288 foreach (
$params[
'caches'] ?? [] as $i => $cacheInfo ) {
291 $params[
'caches'][$i] = $this->newFromParams( $cacheInfo );
295 private function prepareRESTBagOStuffFromParams( array &
$params ): void {
321 $cache = $this->getInstance(
$fallback );
331 $this->instances = [];
338 private static function getLocalServerCacheClass() {
339 if ( self::$localServerCacheClass !==
null ) {
340 return self::$localServerCacheClass;
342 if ( function_exists(
'apcu_fetch' ) ) {
344 if ( PHP_SAPI !==
'cli' || ini_get(
'apc.enable_cli' ) ) {
345 return APCUBagOStuff::class;
348 } elseif ( function_exists(
'wincache_ucache_get' ) ) {
349 return WinCacheBagOStuff::class;
352 return EmptyBagOStuff::class;
363 $this->options->get( MainConfigNames::MainCacheType ),
364 $this->options->get( MainConfigNames::MessageCacheType ),
365 $this->options->get( MainConfigNames::ParserCacheType )
367 foreach ( $candidates as $candidate ) {
371 $class = self::getLocalServerCacheClass();
372 if ( $class !== EmptyBagOStuff::class ) {
380 $services = MediaWikiServices::getInstance();
382 if ( $services->isServiceDisabled(
'DBLoadBalancer' ) ) {
385 } elseif ( $services->isStorageDisabled() ) {
416 'reportDupes' =>
false,
418 'keyspace' => $keyspace,
420 $class = self::getLocalServerCacheClass();
437 $id = $this->getAnythingId();
438 return $this->isDatabaseId( $id );
441 if ( !isset( $this->options->get( MainConfigNames::ObjectCaches )[$id] ) ) {
444 $cache = $this->options->get( MainConfigNames::ObjectCaches )[$id];
445 if ( ( $cache[
'class'] ??
'' ) === SqlBagOStuff::class ) {
459 return $this->getInstance(
460 $this->options->get( MainConfigNames::MainCacheType )