/** How long memcached locks last */
const LOCK_TTL = 30;
+ /**
+ * Lifetime for cache, for keys stored in $wanCache, in seconds.
+ * @var int
+ */
+ const WAN_TTL = IExpiringStore::TTL_DAY;
+
/**
* Process cache of loaded messages that are defined in MediaWiki namespace
*
*/
protected $mDisable;
- /**
- * Lifetime for cache, used by object caching.
- * Set on construction, see __construct().
- */
- protected $mExpiry;
-
/**
* Message cache has its own parser which it uses to transform messages
* @var ParserOptions
protected $contLang;
/**
- * Singleton instance
- *
- * @var MessageCache $instance
+ * Track which languages have been loaded by load().
+ * @var array
*/
- private static $instance;
+ private $loadedLanguages = [];
/**
- * Get the signleton instance of this class
+ * Get the singleton instance of this class
*
+ * @deprecated in 1.34 inject an instance of this class instead of using global state
* @since 1.18
* @return MessageCache
*/
public static function singleton() {
- if ( self::$instance === null ) {
- global $wgUseDatabaseMessages, $wgMsgCacheExpiry, $wgUseLocalMessageCache;
- $services = MediaWikiServices::getInstance();
- self::$instance = new self(
- $services->getMainWANObjectCache(),
- wfGetMessageCacheStorage(),
- $wgUseLocalMessageCache
- ? $services->getLocalServerObjectCache()
- : new EmptyBagOStuff(),
- $wgUseDatabaseMessages,
- $wgMsgCacheExpiry,
- $services->getContentLanguage()
- );
- }
-
- return self::$instance;
- }
-
- /**
- * Destroy the singleton instance
- *
- * @since 1.18
- */
- public static function destroyInstance() {
- self::$instance = null;
+ return MediaWikiServices::getInstance()->getMessageCache();
}
/**
* @param BagOStuff $clusterCache
* @param BagOStuff $serverCache
* @param bool $useDB Whether to look for message overrides (e.g. MediaWiki: pages)
- * @param int $expiry Lifetime for cache. @see $mExpiry.
* @param Language|null $contLang Content language of site
*/
public function __construct(
BagOStuff $clusterCache,
BagOStuff $serverCache,
$useDB,
- $expiry,
Language $contLang = null
) {
$this->wanCache = $wanCache;
$this->cache = new MapCacheLRU( 5 ); // limit size for sanity
$this->mDisable = !$useDB;
- $this->mExpiry = $expiry;
$this->contLang = $contLang ?? MediaWikiServices::getInstance()->getContentLanguage();
}
}
# Don't do double loading...
- if ( $this->cache->has( $code ) && $mode != self::FOR_UPDATE ) {
+ if ( isset( $this->loadedLanguages[$code] ) && $mode != self::FOR_UPDATE ) {
return true;
}
$this->overridable = array_flip( Language::getMessageKeysFor( $code ) );
- // T208897 array_flip can fail and return null
- if ( is_null( $this->overridable ) ) {
- LoggerFactory::getInstance( 'MessageCache' )->error(
- __METHOD__ . ': $this->overridable is null',
- [
- 'message_keys' => Language::getMessageKeysFor( $code ),
- 'code' => $code
- ]
- );
- }
-
# 8 lines of code just to say (once) that message cache is disabled
if ( $this->mDisable ) {
static $shownDisabled = false;
wfDebugLog( 'MessageCacheError', __METHOD__ . ": Failed to load $code\n" );
# This used to throw an exception, but that led to nasty side effects like
# the whole wiki being instantly down if the memcached server died
+ } else {
+ # All good, just record the success
+ $this->loadedLanguages[$code] = true;
}
if ( !$this->cache->has( $code ) ) { // sanity
// Set the text for small software-defined messages in the main cache map
$revisionStore = MediaWikiServices::getInstance()->getRevisionStore();
$revQuery = $revisionStore->getQueryInfo( [ 'page', 'user' ] );
+
+ // T231196: MySQL/MariaDB (10.1.37) can sometimes irrationally decide that querying `actor` then
+ // `revision` then `page` is somehow better than starting with `page`. Tell it not to reorder the
+ // query (and also reorder it ourselves because as generated by RevisionStore it'll have
+ // `revision` first rather than `page`).
+ $revQuery['joins']['revision'] = $revQuery['joins']['page'];
+ unset( $revQuery['joins']['page'] );
+ // It isn't actually necesssary to reorder $revQuery['tables'] as Database does the right thing
+ // when join conditions are given for all joins, but Gergő is wary of relying on that so pull
+ // `page` to the start.
+ $revQuery['tables'] = array_merge(
+ [ 'page' ],
+ array_diff( $revQuery['tables'], [ 'page' ] )
+ );
+
$res = $dbr->select(
$revQuery['tables'],
$revQuery['fields'],
'page_latest = rev_id' // get the latest revision only
] ),
__METHOD__ . "($code)-small",
- [],
+ [ 'STRAIGHT_JOIN' ],
$revQuery['joins']
);
foreach ( $res as $row ) {
# messages larger than $wgMaxMsgCacheEntrySize, since those are only
# stored and fetched from memcache.
$cache['HASH'] = md5( serialize( $cache ) );
- $cache['EXPIRY'] = wfTimestamp( TS_MW, time() + $this->mExpiry );
+ $cache['EXPIRY'] = wfTimestamp( TS_MW, time() + self::WAN_TTL );
unset( $cache['EXCESSIVE'] ); // only needed for hash
return $cache;
$this->wanCache->set(
$this->bigMessageCacheKey( $cache['HASH'], $title ),
' ' . $newTextByTitle[$title],
- $this->mExpiry
+ self::WAN_TTL
);
}
// Mark this cache as definitely being "latest" (non-volatile) so
$fname = __METHOD__;
return $this->srvCache->getWithSetCallback(
$this->srvCache->makeKey( 'messages-big', $hash, $dbKey ),
- IExpiringStore::TTL_MINUTE,
+ BagOStuff::TTL_HOUR,
function () use ( $code, $dbKey, $hash, $fname ) {
return $this->wanCache->getWithSetCallback(
$this->bigMessageCacheKey( $hash, $dbKey ),
- $this->mExpiry,
+ self::WAN_TTL,
function ( $oldValue, &$ttl, &$setOpts ) use ( $dbKey, $code, $fname ) {
// Try loading the message from the database
$dbr = wfGetDB( DB_REPLICA );
$this->wanCache->touchCheckKey( $this->getCheckKey( $code ) );
}
$this->cache->clear();
+ $this->loadedLanguages = [];
}
/**