use MediaWiki\MediaWikiServices;
use Wikimedia\ScopedCallback;
use MediaWiki\Logger\LoggerFactory;
+use Wikimedia\Rdbms\Database;
/**
* MediaWiki message cache structure version.
$po = ParserOptions::newFromAnon();
$po->setEditSection( false );
$po->setAllowUnsafeRawHtml( false );
+ $po->setWrapOutputClass( false );
return $po;
}
// from malicious sources. As a precaution, disable
// the <html> parser tag when parsing messages.
$this->mParserOptions->setAllowUnsafeRawHtml( false );
+ // Wrapping messages in an extra <div> is probably not expected. If
+ // they're outside the content area they probably shouldn't be
+ // targeted by CSS that's targeting the parser output, and if
+ // they're inside they already are from the outer div.
+ $this->mParserOptions->setWrapOutputClass( false );
}
return $this->mParserOptions;
* @return array|bool The cache array, or false if not in cache.
*/
protected function getLocalCache( $code ) {
- $cacheKey = wfMemcKey( __CLASS__, $code );
+ $cacheKey = $this->srvCache->makeKey( __CLASS__, $code );
return $this->srvCache->get( $cacheKey );
}
* @param array $cache The cache array
*/
protected function saveToLocalCache( $code, $cache ) {
- $cacheKey = wfMemcKey( __CLASS__, $code );
+ $cacheKey = $this->srvCache->makeKey( __CLASS__, $code );
$this->srvCache->set( $cacheKey, $cache );
}
}
if ( !$success ) {
- $cacheKey = wfMemcKey( 'messages', $code ); # Key in memc for messages
+ $cacheKey = $this->clusterCache->makeKey( 'messages', $code ); # Key in memc for messages
# Try the global cache. If it is empty, try to acquire a lock. If
# the lock can't be acquired, wait for the other thread to finish
# and then try the global cache a second time.
protected function loadFromDBWithLock( $code, array &$where, $mode = null ) {
# If cache updates on all levels fail, give up on message overrides.
# This is to avoid easy site outages; see $saveSuccess comments below.
- $statusKey = wfMemcKey( 'messages', $code, 'status' );
+ $statusKey = $this->clusterCache->makeKey( 'messages', $code, 'status' );
$status = $this->clusterCache->get( $statusKey );
if ( $status === 'error' ) {
$where[] = "could not load; method is still globally disabled";
# This lock is non-blocking so stale cache can quickly be used.
# Note that load() will call a blocking getReentrantScopedLock()
# after this if it really need to wait for any current thread.
- $cacheKey = wfMemcKey( 'messages', $code );
+ $cacheKey = $this->clusterCache->makeKey( 'messages', $code );
$scopedLock = $this->getReentrantScopedLock( $cacheKey, 0 );
if ( !$scopedLock ) {
$where[] = 'could not acquire main lock';
protected function loadFromDB( $code, $mode = null ) {
global $wgMaxMsgCacheEntrySize, $wgLanguageCode, $wgAdaptiveMessageCache;
- $dbr = wfGetDB( ( $mode == self::FOR_UPDATE ) ? DB_MASTER : DB_REPLICA );
+ // (T164666) The query here performs really poorly on WMF's
+ // contributions replicas. We don't have a way to say "any group except
+ // contributions", so for the moment let's specify 'api'.
+ // @todo: Get rid of this hack.
+ $dbr = wfGetDB( ( $mode == self::FOR_UPDATE ) ? DB_MASTER : DB_REPLICA, 'api' );
$cache = [];
} else {
# Effectively disallows use of '/' character in NS_MEDIAWIKI for uses
# other than language code.
- $conds[] = 'page_title NOT' . $dbr->buildLike( $dbr->anyString(), '/', $dbr->anyString() );
+ $conds[] = 'page_title NOT' .
+ $dbr->buildLike( $dbr->anyString(), '/', $dbr->anyString() );
}
# Conditions to fetch oversized pages to ignore them
# Conditions to load the remaining pages with their contents
$smallConds = $conds;
- $smallConds[] = 'page_latest=rev_id';
- $smallConds[] = 'rev_text_id=old_id';
$smallConds[] = 'page_len <= ' . intval( $wgMaxMsgCacheEntrySize );
$res = $dbr->select(
[ 'page', 'revision', 'text' ],
[ 'page_title', 'old_id', 'old_text', 'old_flags' ],
$smallConds,
- __METHOD__ . "($code)-small"
+ __METHOD__ . "($code)-small",
+ [],
+ [
+ 'revision' => [ 'JOIN', 'page_latest=rev_id' ],
+ 'text' => [ 'JOIN', 'rev_text_id=old_id' ],
+ ]
);
foreach ( $res as $row ) {
/**
* Updates cache as necessary when message page is changed
*
- * @param string $title Message cache key with initial uppercase letter.
+ * @param string $title Message cache key with initial uppercase letter
* @param string|bool $text New contents of the page (false if deleted)
*/
public function replace( $title, $text ) {
function () use ( $title, $msg, $code ) {
global $wgContLang, $wgMaxMsgCacheEntrySize;
// Allow one caller at a time to avoid race conditions
- $scopedLock = $this->getReentrantScopedLock( wfMemcKey( 'messages', $code ) );
+ $scopedLock = $this->getReentrantScopedLock(
+ $this->clusterCache->makeKey( 'messages', $code )
+ );
if ( !$scopedLock ) {
LoggerFactory::getInstance( 'MessageCache' )->error(
__METHOD__ . ': could not acquire lock to update {title} ({code})',
$page->loadPageData( $page::READ_LATEST );
$text = $this->getMessageTextFromContent( $page->getContent() );
// Check if an individual cache key should exist and update cache accordingly
- $titleKey = $this->wanCache->makeKey(
- 'messages-big', $this->mCache[$code]['HASH'], $title );
if ( is_string( $text ) && strlen( $text ) > $wgMaxMsgCacheEntrySize ) {
+ $titleKey = $this->bigMessageCacheKey( $this->mCache[$code]['HASH'], $title );
$this->wanCache->set( $titleKey, ' ' . $text, $this->mExpiry );
}
// Mark this cache as definitely being "latest" (non-volatile) so
// Relay the purge. Touching this check key expires cache contents
// and local cache (APC) validation hash across all datacenters.
- $this->wanCache->touchCheckKey( wfMemcKey( 'messages', $code ) );
+ $this->wanCache->touchCheckKey( $this->wanCache->makeKey( 'messages', $code ) );
// Also delete cached sidebar... just in case it is affected
// @TODO: shouldn't this be $code === $wgLanguageCode?
if ( $code === 'en' ) {
$codes = [ $code ];
}
foreach ( $codes as $code ) {
- $this->wanCache->delete( wfMemcKey( 'sidebar', $code ) );
+ $this->wanCache->delete( $this->wanCache->makeKey( 'sidebar', $code ) );
}
// Purge the message in the message blob store
*/
protected function saveToCaches( array $cache, $dest, $code = false ) {
if ( $dest === 'all' ) {
- $cacheKey = wfMemcKey( 'messages', $code );
+ $cacheKey = $this->clusterCache->makeKey( 'messages', $code );
$success = $this->clusterCache->set( $cacheKey, $cache );
$this->setValidationHash( $code, $cache );
} else {
$value = $this->wanCache->get(
$this->wanCache->makeKey( 'messages', $code, 'hash', 'v1' ),
$curTTL,
- [ wfMemcKey( 'messages', $code ) ]
+ [ $this->wanCache->makeKey( 'messages', $code ) ]
);
if ( $value ) {
* some callers require this behavior. LanguageConverter::parseCachedTable()
* and self::get() are some examples in core.
*
- * @param string $title Message cache key with initial uppercase letter.
- * @param string $code Code denoting the language to try.
+ * @param string $title Message cache key with initial uppercase letter
+ * @param string $code Code denoting the language to try
* @return string|bool The message, or false if it does not exist or on error
*/
public function getMsgFromNamespace( $title, $code ) {
return false;
}
- // Try the individual message cache
- $titleKey = $this->wanCache->makeKey( 'messages-big', $this->mCache[$code]['HASH'], $title );
+ // Individual message cache key
+ $titleKey = $this->bigMessageCacheKey( $this->mCache[$code]['HASH'], $title );
if ( $this->mCacheVolatile[$code] ) {
$entry = false;
__METHOD__ . ': loading volatile key \'{titleKey}\'',
[ 'titleKey' => $titleKey, 'code' => $code ] );
} else {
+ // Try the individual message cache
$entry = $this->wanCache->get( $titleKey );
}
$message = false; // negative caching
}
- if ( $message === false ) { // negative caching
+ if ( $message === false ) {
+ // Negative caching in case a "too big" message is no longer available (deleted)
$this->mCache[$code][$title] = '!NONEXISTENT';
$this->wanCache->set( $titleKey, '!NONEXISTENT', $this->mExpiry, $cacheOpts );
}
$langs = Language::fetchLanguageNames( null, 'mw' );
foreach ( array_keys( $langs ) as $code ) {
# Global and local caches
- $this->wanCache->touchCheckKey( wfMemcKey( 'messages', $code ) );
+ $this->wanCache->touchCheckKey( $this->wanCache->makeKey( 'messages', $code ) );
}
$this->mLoadedLanguages = [];
return $msgText;
}
+
+ /**
+ * @param string $hash Hash for this version of the entire key/value overrides map
+ * @param string $title Message cache key with initial uppercase letter
+ * @return string
+ */
+ private function bigMessageCacheKey( $hash, $title ) {
+ return $this->wanCache->makeKey( 'messages-big', $hash, $title );
+ }
}