]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - includes/objectcache/ObjectCache.php
MediaWiki 1.30.2-scripts2
[autoinstalls/mediawiki.git] / includes / objectcache / ObjectCache.php
1 <?php
2 /**
3  * Functions to get cache objects.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  * http://www.gnu.org/copyleft/gpl.html
19  *
20  * @file
21  * @ingroup Cache
22  */
23
24 use MediaWiki\Logger\LoggerFactory;
25 use MediaWiki\MediaWikiServices;
26
27 /**
28  * Functions to get cache objects
29  *
30  * The word "cache" has two main dictionary meanings, and both
31  * are used in this factory class. They are:
32  *
33  *   - a) Cache (the computer science definition).
34  *        A place to store copies or computations on existing data for
35  *        higher access speeds.
36  *   - b) Storage.
37  *        A place to store lightweight data that is not canonically
38  *        stored anywhere else (e.g. a "hoard" of objects).
39  *
40  * The former should always use strongly consistent stores, so callers don't
41  * have to deal with stale reads. The latter may be eventually consistent, but
42  * callers can use BagOStuff:READ_LATEST to see the latest available data.
43  *
44  * Primary entry points:
45  *
46  * - ObjectCache::getMainWANInstance()
47  *   Purpose: Memory cache.
48  *   Stored in the local data-center's main cache (keyspace different from local-cluster cache).
49  *   Delete events are broadcasted to other DCs main cache. See WANObjectCache for details.
50  *
51  * - ObjectCache::getLocalServerInstance( $fallbackType )
52  *   Purpose: Memory cache for very hot keys.
53  *   Stored only on the individual web server (typically APC or APCu for web requests,
54  *   and EmptyBagOStuff in CLI mode).
55  *   Not replicated to the other servers.
56  *
57  * - ObjectCache::getLocalClusterInstance()
58  *   Purpose: Memory storage for per-cluster coordination and tracking.
59  *   A typical use case would be a rate limit counter or cache regeneration mutex.
60  *   Stored centrally within the local data-center. Not replicated to other DCs.
61  *   Configured by $wgMainCacheType.
62  *
63  * - ObjectCache::getMainStashInstance()
64  *   Purpose: Ephemeral global storage.
65  *   Stored centrally within the primary data-center.
66  *   Changes are applied there first and replicated to other DCs (best-effort).
67  *   To retrieve the latest value (e.g. not from a replica DB), use BagOStuff::READ_LATEST.
68  *   This store may be subject to LRU style evictions.
69  *
70  * - ObjectCache::getInstance( $cacheType )
71  *   Purpose: Special cases (like tiered memory/disk caches).
72  *   Get a specific cache type by key in $wgObjectCaches.
73  *
74  * All the above cache instances (BagOStuff and WANObjectCache) have their makeKey()
75  * method scoped to the *current* wiki ID. Use makeGlobalKey() to avoid this scoping
76  * when using keys that need to be shared amongst wikis.
77  *
78  * @ingroup Cache
79  */
80 class ObjectCache {
81         /** @var BagOStuff[] Map of (id => BagOStuff) */
82         public static $instances = [];
83         /** @var WANObjectCache[] Map of (id => WANObjectCache) */
84         public static $wanInstances = [];
85
86         /**
87          * Get a cached instance of the specified type of cache object.
88          *
89          * @param string $id A key in $wgObjectCaches.
90          * @return BagOStuff
91          */
92         public static function getInstance( $id ) {
93                 if ( !isset( self::$instances[$id] ) ) {
94                         self::$instances[$id] = self::newFromId( $id );
95                 }
96
97                 return self::$instances[$id];
98         }
99
100         /**
101          * Get a cached instance of the specified type of WAN cache object.
102          *
103          * @since 1.26
104          * @param string $id A key in $wgWANObjectCaches.
105          * @return WANObjectCache
106          */
107         public static function getWANInstance( $id ) {
108                 if ( !isset( self::$wanInstances[$id] ) ) {
109                         self::$wanInstances[$id] = self::newWANCacheFromId( $id );
110                 }
111
112                 return self::$wanInstances[$id];
113         }
114
115         /**
116          * Create a new cache object of the specified type.
117          *
118          * @param string $id A key in $wgObjectCaches.
119          * @return BagOStuff
120          * @throws InvalidArgumentException
121          */
122         public static function newFromId( $id ) {
123                 global $wgObjectCaches;
124
125                 if ( !isset( $wgObjectCaches[$id] ) ) {
126                         // Always recognize these ones
127                         if ( $id === CACHE_NONE ) {
128                                 return new EmptyBagOStuff();
129                         } elseif ( $id === 'hash' ) {
130                                 return new HashBagOStuff();
131                         }
132
133                         throw new InvalidArgumentException( "Invalid object cache type \"$id\" requested. " .
134                                 "It is not present in \$wgObjectCaches." );
135                 }
136
137                 return self::newFromParams( $wgObjectCaches[$id] );
138         }
139
140         /**
141          * Get the default keyspace for this wiki.
142          *
143          * This is either the value of the `CachePrefix` configuration variable,
144          * or (if the former is unset) the `DBname` configuration variable, with
145          * `DBprefix` (if defined).
146          *
147          * @return string
148          */
149         public static function getDefaultKeyspace() {
150                 global $wgCachePrefix;
151
152                 $keyspace = $wgCachePrefix;
153                 if ( is_string( $keyspace ) && $keyspace !== '' ) {
154                         return $keyspace;
155                 }
156
157                 return wfWikiID();
158         }
159
160         /**
161          * Create a new cache object from parameters.
162          *
163          * @param array $params Must have 'factory' or 'class' property.
164          *  - factory: Callback passed $params that returns BagOStuff.
165          *  - class: BagOStuff subclass constructed with $params.
166          *  - loggroup: Alias to set 'logger' key with LoggerFactory group.
167          *  - .. Other parameters passed to factory or class.
168          * @return BagOStuff
169          * @throws InvalidArgumentException
170          */
171         public static function newFromParams( $params ) {
172                 if ( isset( $params['loggroup'] ) ) {
173                         $params['logger'] = LoggerFactory::getInstance( $params['loggroup'] );
174                 } else {
175                         $params['logger'] = LoggerFactory::getInstance( 'objectcache' );
176                 }
177                 if ( !isset( $params['keyspace'] ) ) {
178                         $params['keyspace'] = self::getDefaultKeyspace();
179                 }
180                 if ( isset( $params['factory'] ) ) {
181                         return call_user_func( $params['factory'], $params );
182                 } elseif ( isset( $params['class'] ) ) {
183                         $class = $params['class'];
184                         // Automatically set the 'async' update handler
185                         $params['asyncHandler'] = isset( $params['asyncHandler'] )
186                                 ? $params['asyncHandler']
187                                 : 'DeferredUpdates::addCallableUpdate';
188                         // Enable reportDupes by default
189                         $params['reportDupes'] = isset( $params['reportDupes'] )
190                                 ? $params['reportDupes']
191                                 : true;
192                         // Do b/c logic for SqlBagOStuff
193                         if ( is_a( $class, SqlBagOStuff::class, true ) ) {
194                                 if ( isset( $params['server'] ) && !isset( $params['servers'] ) ) {
195                                         $params['servers'] = [ $params['server'] ];
196                                         unset( $params['server'] );
197                                 }
198                                 // In the past it was not required to set 'dbDirectory' in $wgObjectCaches
199                                 if ( isset( $params['servers'] ) ) {
200                                         foreach ( $params['servers'] as &$server ) {
201                                                 if ( $server['type'] === 'sqlite' && !isset( $server['dbDirectory'] ) ) {
202                                                         $server['dbDirectory'] = MediaWikiServices::getInstance()
203                                                                 ->getMainConfig()->get( 'SQLiteDataDir' );
204                                                 }
205                                         }
206                                 }
207                         }
208
209                         // Do b/c logic for MemcachedBagOStuff
210                         if ( is_subclass_of( $class, MemcachedBagOStuff::class ) ) {
211                                 if ( !isset( $params['servers'] ) ) {
212                                         $params['servers'] = $GLOBALS['wgMemCachedServers'];
213                                 }
214                                 if ( !isset( $params['debug'] ) ) {
215                                         $params['debug'] = $GLOBALS['wgMemCachedDebug'];
216                                 }
217                                 if ( !isset( $params['persistent'] ) ) {
218                                         $params['persistent'] = $GLOBALS['wgMemCachedPersistent'];
219                                 }
220                                 if ( !isset( $params['timeout'] ) ) {
221                                         $params['timeout'] = $GLOBALS['wgMemCachedTimeout'];
222                                 }
223                         }
224                         return new $class( $params );
225                 } else {
226                         throw new InvalidArgumentException( "The definition of cache type \""
227                                 . print_r( $params, true ) . "\" lacks both "
228                                 . "factory and class parameters." );
229                 }
230         }
231
232         /**
233          * Factory function for CACHE_ANYTHING (referenced from DefaultSettings.php)
234          *
235          * CACHE_ANYTHING means that stuff has to be cached, not caching is not an option.
236          * If a caching method is configured for any of the main caches ($wgMainCacheType,
237          * $wgMessageCacheType, $wgParserCacheType), then CACHE_ANYTHING will effectively
238          * be an alias to the configured cache choice for that.
239          * If no cache choice is configured (by default $wgMainCacheType is CACHE_NONE),
240          * then CACHE_ANYTHING will forward to CACHE_DB.
241          *
242          * @param array $params
243          * @return BagOStuff
244          */
245         public static function newAnything( $params ) {
246                 global $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType;
247                 $candidates = [ $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType ];
248                 foreach ( $candidates as $candidate ) {
249                         $cache = false;
250                         if ( $candidate !== CACHE_NONE && $candidate !== CACHE_ANYTHING ) {
251                                 $cache = self::getInstance( $candidate );
252                                 // CACHE_ACCEL might default to nothing if no APCu
253                                 // See includes/ServiceWiring.php
254                                 if ( !( $cache instanceof EmptyBagOStuff ) ) {
255                                         return $cache;
256                                 }
257                         }
258                 }
259
260                 if ( MediaWikiServices::getInstance()->isServiceDisabled( 'DBLoadBalancer' ) ) {
261                         // The LoadBalancer is disabled, probably because
262                         // MediaWikiServices::disableStorageBackend was called.
263                         $candidate = CACHE_NONE;
264                 } else {
265                         $candidate = CACHE_DB;
266                 }
267
268                 return self::getInstance( $candidate );
269         }
270
271         /**
272          * Factory function for CACHE_ACCEL (referenced from DefaultSettings.php)
273          *
274          * This will look for any APC or APCu style server-local cache.
275          * A fallback cache can be specified if none is found.
276          *
277          *     // Direct calls
278          *     ObjectCache::getLocalServerInstance( $fallbackType );
279          *
280          *     // From $wgObjectCaches via newFromParams()
281          *     ObjectCache::getLocalServerInstance( [ 'fallback' => $fallbackType ] );
282          *
283          * @param int|string|array $fallback Fallback cache or parameter map with 'fallback'
284          * @return BagOStuff
285          * @throws InvalidArgumentException
286          * @since 1.27
287          */
288         public static function getLocalServerInstance( $fallback = CACHE_NONE ) {
289                 $cache = MediaWikiServices::getInstance()->getLocalServerObjectCache();
290                 if ( $cache instanceof EmptyBagOStuff ) {
291                         if ( is_array( $fallback ) ) {
292                                 $fallback = isset( $fallback['fallback'] ) ? $fallback['fallback'] : CACHE_NONE;
293                         }
294                         $cache = self::getInstance( $fallback );
295                 }
296
297                 return $cache;
298         }
299
300         /**
301          * Create a new cache object of the specified type.
302          *
303          * @since 1.26
304          * @param string $id A key in $wgWANObjectCaches.
305          * @return WANObjectCache
306          * @throws UnexpectedValueException
307          */
308         public static function newWANCacheFromId( $id ) {
309                 global $wgWANObjectCaches, $wgObjectCaches;
310
311                 if ( !isset( $wgWANObjectCaches[$id] ) ) {
312                         throw new UnexpectedValueException(
313                                 "Cache type \"$id\" requested is not present in \$wgWANObjectCaches." );
314                 }
315
316                 $params = $wgWANObjectCaches[$id];
317                 if ( !isset( $wgObjectCaches[$params['cacheId']] ) ) {
318                         throw new UnexpectedValueException(
319                                 "Cache type \"{$params['cacheId']}\" is not present in \$wgObjectCaches." );
320                 }
321                 $params['store'] = $wgObjectCaches[$params['cacheId']];
322
323                 return self::newWANCacheFromParams( $params );
324         }
325
326         /**
327          * Create a new cache object of the specified type.
328          *
329          * @since 1.28
330          * @param array $params
331          * @return WANObjectCache
332          * @throws UnexpectedValueException
333          */
334         public static function newWANCacheFromParams( array $params ) {
335                 $erGroup = MediaWikiServices::getInstance()->getEventRelayerGroup();
336                 foreach ( $params['channels'] as $action => $channel ) {
337                         $params['relayers'][$action] = $erGroup->getRelayer( $channel );
338                         $params['channels'][$action] = $channel;
339                 }
340                 $params['cache'] = self::newFromParams( $params['store'] );
341                 if ( isset( $params['loggroup'] ) ) {
342                         $params['logger'] = LoggerFactory::getInstance( $params['loggroup'] );
343                 } else {
344                         $params['logger'] = LoggerFactory::getInstance( 'objectcache' );
345                 }
346                 $class = $params['class'];
347
348                 return new $class( $params );
349         }
350
351         /**
352          * Get the main cluster-local cache object.
353          *
354          * @since 1.27
355          * @return BagOStuff
356          */
357         public static function getLocalClusterInstance() {
358                 global $wgMainCacheType;
359
360                 return self::getInstance( $wgMainCacheType );
361         }
362
363         /**
364          * Get the main WAN cache object.
365          *
366          * @since 1.26
367          * @return WANObjectCache
368          * @deprecated Since 1.28 Use MediaWikiServices::getMainWANObjectCache()
369          */
370         public static function getMainWANInstance() {
371                 return MediaWikiServices::getInstance()->getMainWANObjectCache();
372         }
373
374         /**
375          * Get the cache object for the main stash.
376          *
377          * Stash objects are BagOStuff instances suitable for storing light
378          * weight data that is not canonically stored elsewhere (such as RDBMS).
379          * Stashes should be configured to propagate changes to all data-centers.
380          *
381          * Callers should be prepared for:
382          *   - a) Writes to be slower in non-"primary" (e.g. HTTP GET/HEAD only) DCs
383          *   - b) Reads to be eventually consistent, e.g. for get()/getMulti()
384          * In general, this means avoiding updates on idempotent HTTP requests and
385          * avoiding an assumption of perfect serializability (or accepting anomalies).
386          * Reads may be eventually consistent or data might rollback as nodes flap.
387          * Callers can use BagOStuff:READ_LATEST to see the latest available data.
388          *
389          * @return BagOStuff
390          * @since 1.26
391          * @deprecated Since 1.28 Use MediaWikiServices::getMainObjectStash
392          */
393         public static function getMainStashInstance() {
394                 return MediaWikiServices::getInstance()->getMainObjectStash();
395         }
396
397         /**
398          * Clear all the cached instances.
399          */
400         public static function clear() {
401                 self::$instances = [];
402                 self::$wanInstances = [];
403         }
404 }