Make Revision::getRevisionText() cache the converted text
[lhc/web/wiklou.git] / includes / Revision.php
index d9e42ff..a3dbc14 100644 (file)
@@ -21,6 +21,8 @@
  */
 use MediaWiki\Linker\LinkTarget;
 use MediaWiki\MediaWikiServices;
+use Wikimedia\Rdbms\ResultWrapper;
+use Wikimedia\Rdbms\FakeResultWrapper;
 
 /**
  * @todo document
@@ -216,7 +218,7 @@ class Revision implements IDBAccessObject {
                        // Pre-1.5 ar_text row
                        $attribs['text'] = self::getRevisionText( $row, 'ar_' );
                        if ( $attribs['text'] === false ) {
-                               throw new MWException( 'Unable to load text from archive row (possibly bug 22624)' );
+                               throw new MWException( 'Unable to load text from archive row (possibly T24624)' );
                        }
                }
                return new self( $attribs );
@@ -1238,8 +1240,9 @@ class Revision implements IDBAccessObject {
 
        /**
         * Get revision text associated with an old or archive row
-        * $row is usually an object from wfFetchRow(), both the flags and the text
-        * field must be included.
+        *
+        * Both the flags and the text field must be included. Including the old_id
+        * field will activate cache usage as long as the $wiki parameter is not set.
         *
         * @param stdClass $row The text data
         * @param string $prefix Table prefix (default 'old_')
@@ -1250,8 +1253,6 @@ class Revision implements IDBAccessObject {
         * @return string|false Text the text requested or false on failure
         */
        public static function getRevisionText( $row, $prefix = 'old_', $wiki = false ) {
-
-               # Get data
                $textField = $prefix . 'text';
                $flagsField = $prefix . 'flags';
 
@@ -1267,21 +1268,35 @@ class Revision implements IDBAccessObject {
                        return false;
                }
 
-               # Use external methods for external objects, text in table is URL-only then
+               // Use external methods for external objects, text in table is URL-only then
                if ( in_array( 'external', $flags ) ) {
                        $url = $text;
                        $parts = explode( '://', $url, 2 );
                        if ( count( $parts ) == 1 || $parts[1] == '' ) {
                                return false;
                        }
-                       $text = ExternalStore::fetchFromURL( $url, [ 'wiki' => $wiki ] );
-               }
 
-               // If the text was fetched without an error, convert it
-               if ( $text !== false ) {
-                       $text = self::decompressRevisionText( $text, $flags );
+                       if ( isset( $row->old_id ) && $wiki === false ) {
+                               // Make use of the wiki-local revision text cache
+                               $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
+                               // The cached value should be decompressed, so handle that and return here
+                               return $cache->getWithSetCallback(
+                                       $cache->makeKey( 'revisiontext', 'textid', $row->old_id ),
+                                       self::getCacheTTL( $cache ),
+                                       function () use ( $url, $wiki, $flags ) {
+                                               // No negative caching per Revision::loadText()
+                                               $text = ExternalStore::fetchFromURL( $url, [ 'wiki' => $wiki ] );
+
+                                               return ExternalStore::decompressRevisionText( $text, $flags );
+                                       },
+                                       [ 'pcGroup' => self::TEXT_CACHE_GROUP, 'pcTTL' => $cache::TTL_PROC_LONG ]
+                               );
+                       } else {
+                               $text = ExternalStore::fetchFromURL( $url, [ 'wiki' => $wiki ] );
+                       }
                }
-               return $text;
+
+               return self::decompressRevisionText( $text, $flags );
        }
 
        /**
@@ -1327,6 +1342,13 @@ class Revision implements IDBAccessObject {
         * @return string|bool Decompressed text, or false on failure
         */
        public static function decompressRevisionText( $text, $flags ) {
+               global $wgLegacyEncoding, $wgContLang;
+
+               if ( $text === false ) {
+                       // Text failed to be fetched; nothing to do
+                       return false;
+               }
+
                if ( in_array( 'gzip', $flags ) ) {
                        # Deal with optional compression of archived pages.
                        # This can be done periodically via maintenance/compressOld.php, and
@@ -1349,7 +1371,6 @@ class Revision implements IDBAccessObject {
                        $text = $obj->getText();
                }
 
-               global $wgLegacyEncoding;
                if ( $text !== false && $wgLegacyEncoding
                        && !in_array( 'utf-8', $flags ) && !in_array( 'utf8', $flags )
                ) {
@@ -1357,7 +1378,6 @@ class Revision implements IDBAccessObject {
                        # Upconvert on demand.
                        # ("utf8" checked for compatibility with some broken
                        #  conversion scripts 2008-12-30)
-                       global $wgContLang;
                        $text = $wgContLang->iconv( $wgLegacyEncoding, 'UTF-8', $text );
                }
 
@@ -1557,15 +1577,14 @@ class Revision implements IDBAccessObject {
        }
 
        /**
-        * Lazy-load the revision's text.
-        * Currently hardcoded to the 'text' table storage engine.
+        * Get the text cache TTL
         *
-        * @return string|bool The revision's text, or false on failure
+        * @param WANObjectCache $cache
+        * @return integer
         */
-       private function loadText() {
+       private static function getCacheTTL( WANObjectCache $cache ) {
                global $wgRevisionCacheExpiry;
 
-               $cache = ObjectCache::getMainWANInstance();
                if ( $cache->getQoS( $cache::ATTR_EMULATION ) <= $cache::QOS_EMULATION_SQL ) {
                        // Do not cache RDBMs blobs in...the RDBMs store
                        $ttl = $cache::TTL_UNCACHEABLE;
@@ -1573,10 +1592,22 @@ class Revision implements IDBAccessObject {
                        $ttl = $wgRevisionCacheExpiry ?: $cache::TTL_UNCACHEABLE;
                }
 
+               return $ttl;
+       }
+
+       /**
+        * Lazy-load the revision's text.
+        * Currently hardcoded to the 'text' table storage engine.
+        *
+        * @return string|bool The revision's text, or false on failure
+        */
+       private function loadText() {
+               $cache = ObjectCache::getMainWANInstance();
+
                // No negative caching; negative hits on text rows may be due to corrupted replica DBs
                return $cache->getWithSetCallback(
                        $cache->makeKey( 'revisiontext', 'textid', $this->getTextId() ),
-                       $ttl,
+                       self::getCacheTTL( $cache ),
                        function () {
                                return $this->fetchText();
                        },
@@ -1781,6 +1812,7 @@ class Revision implements IDBAccessObject {
         *
         * @param Title $title
         * @param int $id
+        * @param int $flags
         * @return string|bool False if not found
         */
        static function getTimestampFromId( $title, $id, $flags = 0 ) {