Merge "Allow partially blocked users to tag unrelated revisions"
[lhc/web/wiklou.git] / includes / cache / MessageCache.php
index 93fdb16..848d9c9 100644 (file)
@@ -45,6 +45,12 @@ class MessageCache {
        /** 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
         *
@@ -70,12 +76,6 @@ class MessageCache {
         */
        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
@@ -137,7 +137,6 @@ class MessageCache {
         * @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(
@@ -145,7 +144,6 @@ class MessageCache {
                BagOStuff $clusterCache,
                BagOStuff $serverCache,
                $useDB,
-               $expiry,
                Language $contLang = null
        ) {
                $this->wanCache = $wanCache;
@@ -155,7 +153,6 @@ class MessageCache {
                $this->cache = new MapCacheLRU( 5 ); // limit size for sanity
 
                $this->mDisable = !$useDB;
-               $this->mExpiry = $expiry;
                $this->contLang = $contLang ?? MediaWikiServices::getInstance()->getContentLanguage();
        }
 
@@ -168,8 +165,8 @@ class MessageCache {
                global $wgUser;
 
                if ( !$this->mParserOptions ) {
-                       if ( !$wgUser->isSafeToLoad() ) {
-                               // $wgUser isn't unstubbable yet, so don't try to get a
+                       if ( !$wgUser || !$wgUser->isSafeToLoad() ) {
+                               // $wgUser isn't available yet, so don't try to get a
                                // ParserOptions for it. And don't cache this ParserOptions
                                // either.
                                $po = ParserOptions::newFromAnon();
@@ -504,6 +501,21 @@ class MessageCache {
                // 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'],
@@ -512,7 +524,7 @@ class MessageCache {
                                'page_latest = rev_id' // get the latest revision only
                        ] ),
                        __METHOD__ . "($code)-small",
-                       [],
+                       [ 'STRAIGHT_JOIN' ],
                        $revQuery['joins']
                );
                foreach ( $res as $row ) {
@@ -551,7 +563,7 @@ class MessageCache {
                # 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;
@@ -666,7 +678,7 @@ class MessageCache {
                        $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
@@ -1090,11 +1102,11 @@ class MessageCache {
                $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 );
@@ -1179,6 +1191,7 @@ class MessageCache {
                        $class = $wgParserConf['class'];
                        if ( $class == ParserDiffTest::class ) {
                                # Uncloneable
+                               // @phan-suppress-next-line PhanTypeMismatchProperty
                                $this->mParser = new $class( $wgParserConf );
                        } else {
                                $this->mParser = clone $parser;