Make MessageCache handle lock timeouts better
authorAaron Schulz <aschulz@wikimedia.org>
Wed, 20 Apr 2016 19:37:00 +0000 (12:37 -0700)
committerOri.livneh <ori@wikimedia.org>
Thu, 21 Apr 2016 13:18:29 +0000 (13:18 +0000)
* Timeouts happen when memcached is cleared or on DC-switchover likewise
* Disable the sidebar cache when the MessageCache is disabled
* Also lower any output expiry for CDN and set a custom header
  when the MessageCache is disabled
* Log when this happens to a dedicated "MessageCacheError" channel

Bug: T133069
Bug: T7092
Change-Id: I1b80e250532033c52680246279e1e8f56350ae83

includes/DefaultSettings.php
includes/MediaWiki.php
includes/cache/MessageCache.php
includes/skins/Skin.php

index 10e6adb..f2370ad 100644 (file)
@@ -2589,6 +2589,13 @@ $wgCdnMaxageLagged = 30;
  */
 $wgCdnReboundPurgeDelay = 0;
 
+/**
+ * Cache timeout for the CDN when a response is known to be wrong or incomplete (due to load)
+ * @see $wgSquidMaxage
+ * @since 1.27
+ */
+$wgCdnMaxageSubstitute = 60;
+
 /**
  * Default maximum age for raw CSS/JS accesses
  *
index ad02e68..3dd7420 100644 (file)
@@ -585,6 +585,13 @@ class MediaWiki {
                        $request->response()->header( "X-Database-Lagged: true" );
                        wfDebugLog( 'replication', "Lagged DB used; CDN cache TTL limited to $maxAge seconds" );
                }
+
+               // Avoid long-term cache pollution due to message cache rebuild timeouts (T133069)
+               if ( MessageCache::singleton()->isDisabled() ) {
+                       $maxAge = $config->get( 'CdnMaxageSubstitute' );
+                       $context->getOutput()->lowerCdnMaxage( $maxAge );
+                       $request->response()->header( "X-Response-Substitute: true" );
+               }
        }
 
        /**
index 0e566ea..62fab5f 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;
@@ -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 {
@@ -1113,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.
         */
index 4d7fc50..04cf351 100644 (file)
@@ -1200,7 +1200,9 @@ abstract class Skin extends ContextSource {
                        $cache = ObjectCache::getMainWANInstance();
                        $sidebar = $cache->getWithSetCallback(
                                $cache->makeKey( 'sidebar', $this->getLanguage()->getCode() ),
-                               $wgSidebarCacheExpiry,
+                               MessageCache::singleton()->isDisabled()
+                                       ? $cache::TTL_UNCACHEABLE // bug T133069
+                                       : $wgSidebarCacheExpiry,
                                $callback,
                                [ 'lockTSE' => 30 ]
                        );