Revision: Simplify loadText() with nested getWithSetCallback
authorTimo Tijhof <krinklemail@gmail.com>
Wed, 7 Sep 2016 01:34:43 +0000 (18:34 -0700)
committerTimo Tijhof <krinklemail@gmail.com>
Wed, 7 Sep 2016 21:03:01 +0000 (14:03 -0700)
Check the MapCacheLRU::get() return value properly for misses
this time.

This reverts commit 1f022240c73f0909d8d3eb457ede12119190621a.

Change-Id: Ic95b79ad8d7d7c788f406d45c0e9bedeeb8d220b

includes/Revision.php
includes/libs/MapCacheLRU.php

index 03c3e6d..bc760a3 100644 (file)
@@ -1079,13 +1079,14 @@ class Revision implements IDBAccessObject {
        }
 
        /**
-        * Fetch original serialized data without regard for view restrictions
+        * Get original serialized data (without checking view restrictions)
         *
         * @since 1.21
         * @return string
         */
        public function getSerializedData() {
                if ( $this->mText === null ) {
+                       // Revision is immutable. Load on demand.
                        $this->mText = $this->loadText();
                }
 
@@ -1103,17 +1104,14 @@ class Revision implements IDBAccessObject {
         */
        protected function getContentInternal() {
                if ( $this->mContent === null ) {
-                       // Revision is immutable. Load on demand:
-                       if ( $this->mText === null ) {
-                               $this->mText = $this->loadText();
-                       }
+                       $text = $this->getSerializedData();
 
-                       if ( $this->mText !== null && $this->mText !== false ) {
+                       if ( $text !== null && $text !== false ) {
                                // Unserialize content
                                $handler = $this->getContentHandler();
                                $format = $this->getContentFormat();
 
-                               $this->mContent = $handler->unserializeContent( $this->mText, $format );
+                               $this->mContent = $handler->unserializeContent( $text, $format );
                        }
                }
 
@@ -1576,29 +1574,38 @@ class Revision implements IDBAccessObject {
         *
         * @return string|bool The revision's text, or false on failure
         */
-       protected function loadText() {
+       private function loadText() {
                // Caching may be beneficial for massive use of external storage
                global $wgRevisionCacheExpiry;
                static $processCache = null;
 
+               if ( !$wgRevisionCacheExpiry ) {
+                       return $this->fetchText();
+               }
+
                if ( !$processCache ) {
                        $processCache = new MapCacheLRU( 10 );
                }
 
                $cache = ObjectCache::getMainWANInstance();
-               $textId = $this->getTextId();
-               $key = wfMemcKey( 'revisiontext', 'textid', $textId );
+               $key = $cache->makeKey( 'revisiontext', 'textid', $this->getTextId() );
 
-               if ( $wgRevisionCacheExpiry ) {
-                       if ( $processCache->has( $key ) ) {
-                               return $processCache->get( $key );
-                       }
-                       $text = $cache->get( $key );
-                       if ( is_string( $text ) ) {
-                               $processCache->set( $key, $text );
-                               return $text;
-                       }
-               }
+               // No negative caching; negative hits on text rows may be due to corrupted replica DBs
+               return $processCache->getWithSetCallback( $key, function () use ( $cache, $key ) {
+                       global $wgRevisionCacheExpiry;
+
+                       return $cache->getWithSetCallback(
+                               $key,
+                               $wgRevisionCacheExpiry,
+                               function () {
+                                       return $this->fetchText();
+                               }
+                       );
+               } );
+       }
+
+       private function fetchText() {
+               $textId = $this->getTextId();
 
                // If we kept data for lazy extraction, use it now...
                if ( $this->mTextRow !== null ) {
@@ -1638,13 +1645,7 @@ class Revision implements IDBAccessObject {
                        wfDebugLog( 'Revision', "No blob for text row '$textId' (revision {$this->getId()})." );
                }
 
-               # No negative caching -- negative hits on text rows may be due to corrupted replica DB servers
-               if ( $wgRevisionCacheExpiry && $text !== false ) {
-                       $processCache->set( $key, $text );
-                       $cache->set( $key, $text, $wgRevisionCacheExpiry );
-               }
-
-               return $text;
+               return is_string( $text ) ? $text : false;
        }
 
        /**
index 2370ed3..90c9a75 100644 (file)
@@ -84,13 +84,15 @@ class MapCacheLRU {
         * If the item is already set, it will be pushed to the top of the cache.
         *
         * @param string $key
-        * @return mixed
+        * @return mixed Returns null if the key was not found
         */
        public function get( $key ) {
                if ( !array_key_exists( $key, $this->cache ) ) {
                        return null;
                }
+
                $this->ping( $key );
+
                return $this->cache[$key];
        }
 
@@ -102,6 +104,28 @@ class MapCacheLRU {
                return array_keys( $this->cache );
        }
 
+       /**
+        * Get an item with the given key, producing and setting it if not found.
+        *
+        * If the callback returns false, then nothing is stored.
+        *
+        * @since 1.28
+        * @param string $key
+        * @param callable $callback Callback that will produce the value
+        * @return mixed The cached value if found or the result of $callback otherwise
+        */
+       public function getWithSetCallback( $key, callable $callback ) {
+               $value = $this->get( $key );
+               if ( $value === null ) {
+                       $value = call_user_func( $callback );
+                       if ( $value !== false ) {
+                               $this->set( $key, $value );
+                       }
+               }
+
+               return $value;
+       }
+
        /**
         * Clear one or several cache entries, or all cache entries
         *