ObjectCache.php 14 KB

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