jenkins-bot has submitted this change and it was merged.

Change subject: Avoid MessageCache rebuilds if replace() was called recently
......................................................................


Avoid MessageCache rebuilds if replace() was called recently

* If replace() was called recently, then we know that this is the
  master data center and that the messages are up-to-date. With this
  change, replace() calls have nothing to contend with aside from other
  replace() calls. Even if there were timeouts due to such contention,
  caused by high MediaWiki: page edit rates, the replace() updates would
  pick up the prior changes in passing since they do load().
* This also avoids the following scenario:
  a) Someone edits a message page, triggering replace()
  b) Some page view causes load() to trigger loadFromDB()
     due to the hash being seen as volatile due to replace()
  c) The loadFromDB() loads stale slave data and undoes
     the message key update the replace() did

Change-Id: I9cdf7ad3d67f168fcba7f633af9e32a8d1fa928e
---
M includes/cache/MessageCache.php
1 file changed, 40 insertions(+), 21 deletions(-)

Approvals:
  Ori.livneh: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/includes/cache/MessageCache.php b/includes/cache/MessageCache.php
index a894038..69f4c63 100644
--- a/includes/cache/MessageCache.php
+++ b/includes/cache/MessageCache.php
@@ -526,26 +526,26 @@
 
                list( $msg, $code ) = $this->figureMessage( $title );
                if ( strpos( $title, '/' ) !== false && $code === 
$wgLanguageCode ) {
-                       # Content language overrides do not use the /<code> 
suffix
+                       // Content language overrides do not use the /<code> 
suffix
                        return;
                }
 
-               # Note that if the cache is volatile, load() may trigger a DB 
fetch.
-               # In that case we reenter/reuse the existing cache key lock to 
avoid
-               # a self-deadlock. This is safe as no reads happen *directly* 
in this
-               # method between getReentrantScopedLock() and load() below. 
There is
-               # no risk of data "changing under our feet" for replace().
+               // Note that if the cache is volatile, load() may trigger a DB 
fetch.
+               // In that case we reenter/reuse the existing cache key lock to 
avoid
+               // a self-deadlock. This is safe as no reads happen *directly* 
in this
+               // method between getReentrantScopedLock() and load() below. 
There is
+               // no risk of data "changing under our feet" for replace().
                $cacheKey = wfMemcKey( 'messages', $code );
                $scopedLock = $this->getReentrantScopedLock( $cacheKey );
                $this->load( $code, self::FOR_UPDATE );
 
                $titleKey = wfMemcKey( 'messages', 'individual', $title );
                if ( $text === false ) {
-                       # Article was deleted
+                       // Article was deleted
                        $this->mCache[$code][$title] = '!NONEXISTENT';
                        $this->wanCache->delete( $titleKey );
                } elseif ( strlen( $text ) > $wgMaxMsgCacheEntrySize ) {
-                       # Check for size
+                       // Check for size
                        $this->mCache[$code][$title] = '!TOO BIG';
                        $this->wanCache->set( $titleKey, ' ' . $text, 
$this->mExpiry );
                } else {
@@ -553,12 +553,17 @@
                        $this->wanCache->delete( $titleKey );
                }
 
+               // Mark this cache as definitely "latest" (non-volatile) so
+               // load() calls do try to refresh the cache with slave data
+               $this->mCache[$code]['LATEST'] = time();
+
+               // Update caches if the lock was acquired
                if ( $scopedLock ) {
-                       # Update caches
                        $this->saveToCaches( $this->mCache[$code], 'all', $code 
);
                }
 
                ScopedCallback::consume( $scopedLock );
+               // Relay the purge to APC and other DCs
                $this->wanCache->touchCheckKey( wfMemcKey( 'messages', $code ) 
);
 
                // Also delete cached sidebar... just in case it is affected
@@ -610,7 +615,7 @@
         * @param string|bool $code Language code (default: false)
         * @return bool
         */
-       protected function saveToCaches( $cache, $dest, $code = false ) {
+       protected function saveToCaches( array $cache, $dest, $code = false ) {
                if ( $dest === 'all' ) {
                        $cacheKey = wfMemcKey( 'messages', $code );
                        $success = $this->mMemc->set( $cacheKey, $cache );
@@ -618,16 +623,14 @@
                        $success = true;
                }
 
-               $this->setValidationHash( $code, $cache['HASH'] );
-
-               # Save to local cache
+               $this->setValidationHash( $code, $cache );
                $this->saveToLocalCache( $code, $cache );
 
                return $success;
        }
 
        /**
-        * Get the md5 used to validate the local disk cache
+        * Get the md5 used to validate the local APC cache
         *
         * @param string $code
         * @return array (hash or false, bool expiry/volatility status)
@@ -635,25 +638,41 @@
        protected function getValidationHash( $code ) {
                $curTTL = null;
                $value = $this->wanCache->get(
-                       wfMemcKey( 'messages', $code, 'hash' ),
+                       wfMemcKey( 'messages', $code, 'hash', 'v1' ),
                        $curTTL,
                        array( wfMemcKey( 'messages', $code ) )
                );
-               $expired = ( $curTTL === null || $curTTL < 0 );
 
-               return array( $value, $expired );
+               if ( !$value ) {
+                       // No hash found at all; cache must regenerate to be 
safe
+                       $expired = true;
+               } elseif ( ( time() - $value['latest'] ) < 
WANObjectCache::HOLDOFF_TTL ) {
+                       // Cache was recently updated via replace() and should 
be up-to-date
+                       $expired = false;
+               } else {
+                       // See if the "check" key was bumped after the hash was 
generated
+                       $expired = ( $curTTL < 0 );
+               }
+
+               return array( $value['hash'], $expired );
        }
 
        /**
         * Set the md5 used to validate the local disk cache
         *
+        * If $cache has a 'LATEST' UNIX timestamp key, then the hash will not
+        * be treated as "volatile" by getValidationHash() for the next few 
seconds
+        *
         * @param string $code
-        * @param string $hash
+        * @param array $cache Cached messages with a version
         */
-       protected function setValidationHash( $code, $hash ) {
+       protected function setValidationHash( $code, array $cache ) {
                $this->wanCache->set(
-                       wfMemcKey( 'messages', $code, 'hash' ),
-                       $hash,
+                       wfMemcKey( 'messages', $code, 'hash', 'v1' ),
+                       array(
+                               'hash' => $cache['HASH'],
+                               'latest' => isset( $cache['LATEST'] ) ? 
$cache['LATEST'] : 0
+                       ),
                        WANObjectCache::TTL_NONE
                );
        }

-- 
To view, visit https://gerrit.wikimedia.org/r/234836
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I9cdf7ad3d67f168fcba7f633af9e32a8d1fa928e
Gerrit-PatchSet: 6
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Aaron Schulz <[email protected]>
Gerrit-Reviewer: Aaron Schulz <[email protected]>
Gerrit-Reviewer: Ori.livneh <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to