Merge "add IGNORE INDEX option to mysql database handler"
[lhc/web/wiklou.git] / includes / cache / MessageCache.php
index b26dc8d..d254d3d 100644 (file)
@@ -51,7 +51,7 @@ class MessageCache {
        protected $mCache;
 
        /**
-        * Should  mean that database cannot be used, but check
+        * Should mean that database cannot be used, but check
         * @var bool $mDisable
         */
        protected $mDisable;
@@ -302,7 +302,7 @@ class MessageCache {
                                                $where[] = 'global cache is expired';
                                                $staleCache = $cache;
                                        } elseif ( $hashVolatile ) {
-                                               # DB results are slave lag prone until the holdoff TTL passes.
+                                               # DB results are replica DB lag prone until the holdoff TTL passes.
                                                # By then, updates should be reflected in loadFromDBWithLock().
                                                # One thread renerates the cache while others use old values.
                                                $where[] = 'global cache is expired/volatile';
@@ -355,6 +355,7 @@ class MessageCache {
                        $where[] = 'loading FAILED - cache is disabled';
                        $this->mDisable = true;
                        $this->mCache = 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 {
@@ -441,7 +442,7 @@ class MessageCache {
        function loadFromDB( $code, $mode = null ) {
                global $wgMaxMsgCacheEntrySize, $wgLanguageCode, $wgAdaptiveMessageCache;
 
-               $dbr = wfGetDB( ( $mode == self::FOR_UPDATE ) ? DB_MASTER : DB_SLAVE );
+               $dbr = wfGetDB( ( $mode == self::FOR_UPDATE ) ? DB_MASTER : DB_REPLICA );
 
                $cache = [];
 
@@ -563,7 +564,7 @@ class MessageCache {
                }
 
                // Mark this cache as definitely "latest" (non-volatile) so
-               // load() calls do try to refresh the cache with slave data
+               // load() calls do try to refresh the cache with replica DB data
                $this->mCache[$code]['LATEST'] = time();
 
                // Update caches if the lock was acquired
@@ -818,84 +819,96 @@ class MessageCache {
         * @return string|bool The message, or false if not found
         */
        protected function getMessageFromFallbackChain( $lang, $lckey, $useDB ) {
-               global $wgLanguageCode, $wgContLang;
-
-               $uckey = $wgContLang->ucfirst( $lckey );
-               $langcode = $lang->getCode();
-               $message = false;
+               global $wgContLang;
 
-               // First try the requested language.
-               if ( $useDB ) {
-                       if ( $langcode === $wgLanguageCode ) {
-                               // Messages created in the content language will not have the /lang extension
-                               $message = $this->getMsgFromNamespace( $uckey, $langcode );
-                       } else {
-                               $message = $this->getMsgFromNamespace( "$uckey/$langcode", $langcode );
-                       }
-               }
+               $alreadyTried = [];
 
+                // First try the requested language.
+               $message = $this->getMessageForLang( $lang, $lckey, $useDB, $alreadyTried );
                if ( $message !== false ) {
                        return $message;
                }
 
-               // Check the CDB cache
-               $message = $lang->getMessage( $lckey );
-               if ( $message !== null ) {
-                       return $message;
-               }
+               // Now try checking the site language.
+               $message = $this->getMessageForLang( $wgContLang, $lckey, $useDB, $alreadyTried );
+               return $message;
+       }
 
-               list( $fallbackChain, $siteFallbackChain ) =
-                       Language::getFallbacksIncludingSiteLanguage( $langcode );
+       /**
+        * Given a language, try and fetch messages from that language and its fallbacks.
+        *
+        * @see MessageCache::get
+        * @param Language|StubObject $lang Preferred language
+        * @param string $lckey Lowercase key for the message (as for localisation cache)
+        * @param bool $useDB Whether to include messages from the wiki database
+        * @param bool[] $alreadyTried Contains true for each language that has been tried already
+        * @return string|bool The message, or false if not found
+        */
+       private function getMessageForLang( $lang, $lckey, $useDB, &$alreadyTried ) {
+               global $wgContLang;
+               $langcode = $lang->getCode();
 
-               // Next try checking the database for all of the fallback languages of the requested language.
+               // Try checking the database for the requested language
                if ( $useDB ) {
-                       foreach ( $fallbackChain as $code ) {
-                               if ( $code === $wgLanguageCode ) {
-                                       // Messages created in the content language will not have the /lang extension
-                                       $message = $this->getMsgFromNamespace( $uckey, $code );
-                               } else {
-                                       $message = $this->getMsgFromNamespace( "$uckey/$code", $code );
-                               }
+                       $uckey = $wgContLang->ucfirst( $lckey );
+
+                       if ( !isset( $alreadyTried[ $langcode ] ) ) {
+                               $message = $this->getMsgFromNamespace(
+                                       $this->getMessagePageName( $langcode, $uckey ),
+                                       $langcode
+                               );
 
                                if ( $message !== false ) {
-                                       // Found the message.
                                        return $message;
                                }
+                               $alreadyTried[ $langcode ] = true;
                        }
                }
 
-               // Now try checking the site language.
-               if ( $useDB ) {
-                       $message = $this->getMsgFromNamespace( $uckey, $wgLanguageCode );
-                       if ( $message !== false ) {
-                               return $message;
-                       }
-               }
-
-               $message = $wgContLang->getMessage( $lckey );
+               // Check the CDB cache
+               $message = $lang->getMessage( $lckey );
                if ( $message !== null ) {
                        return $message;
                }
 
-               // Finally try the DB for the site language's fallbacks.
+               // Try checking the database for all of the fallback languages
                if ( $useDB ) {
-                       foreach ( $siteFallbackChain as $code ) {
-                               $message = $this->getMsgFromNamespace( "$uckey/$code", $code );
-                               if ( $message === false && $code === $wgLanguageCode ) {
-                                       // Messages created in the content language will not have the /lang extension
-                                       $message = $this->getMsgFromNamespace( $uckey, $code );
+                       $fallbackChain = Language::getFallbacksFor( $langcode );
+
+                       foreach ( $fallbackChain as $code ) {
+                               if ( isset( $alreadyTried[ $code ] ) ) {
+                                       continue;
                                }
 
+                               $message = $this->getMsgFromNamespace( $this->getMessagePageName( $code, $uckey ), $code );
+
                                if ( $message !== false ) {
-                                       // Found the message.
                                        return $message;
                                }
+                               $alreadyTried[ $code ] = true;
                        }
                }
 
                return false;
        }
 
+       /**
+        * Get the message page name for a given language
+        *
+        * @param string $langcode
+        * @param string $uckey Uppercase key for the message
+        * @return string The page name
+        */
+       private function getMessagePageName( $langcode, $uckey ) {
+               global $wgLanguageCode;
+               if ( $langcode === $wgLanguageCode ) {
+                       // Messages created in the content language will not have the /lang extension
+                       return $uckey;
+               } else {
+                       return "$uckey/$langcode";
+               }
+       }
+
        /**
         * Get a message from the MediaWiki namespace, with caching. The key must
         * first be converted to two-part lang/msg form if necessary.
@@ -1101,6 +1114,22 @@ class MessageCache {
                $this->mDisable = false;
        }
 
+       /**
+        * Whether DB/cache usage is disabled for determining messages
+        *
+        * If so, this typically indicates either:
+        *   - a) load() failed to find a cached copy nor query the DB
+        *   - b) we are in a special context or error mode that cannot use the DB
+        * If the DB is ignored, any derived HTML output or cached objects may be wrong.
+        * To avoid long-term cache pollution, TTLs can be adjusted accordingly.
+        *
+        * @return bool
+        * @since 1.27
+        */
+       public function isDisabled() {
+               return $this->mDisable;
+       }
+
        /**
         * Clear all stored messages. Mainly used after a mass rebuild.
         */