Merge "parser: add vary-revision-sha1 and related ParserOutput methods"
[lhc/web/wiklou.git] / includes / Storage / PageEditStash.php
index cc3e4bc..4671d99 100644 (file)
@@ -109,7 +109,7 @@ class PageEditStash {
                // the stash request finishes parsing. For the lock acquisition below, there is not much
                // need to duplicate parsing of the same content/user/summary bundle, so try to avoid
                // blocking at all here.
-               $dbw = $this->lb->getConnection( DB_MASTER );
+               $dbw = $this->lb->getConnectionRef( DB_MASTER );
                if ( !$dbw->lock( $key, $fname, 0 ) ) {
                        // De-duplicate requests on the same key
                        return self::ERROR_BUSY;
@@ -221,7 +221,7 @@ class PageEditStash {
 
                $editInfo = $this->getAndWaitForStashValue( $key );
                if ( !is_object( $editInfo ) || !$editInfo->output ) {
-                       $this->stats->increment( 'editstash.cache_misses.no_stash' );
+                       $this->incrStatsByContent( 'cache_misses.no_stash', $content );
                        if ( $this->recentStashEntryCount( $user ) > 0 ) {
                                $logger->info( "Empty cache for key '{key}' but not for user.", $context );
                        } else {
@@ -237,28 +237,28 @@ class PageEditStash {
                $isCacheUsable = true;
                if ( $age <= self::PRESUME_FRESH_TTL_SEC ) {
                        // Assume nothing changed in this time
-                       $this->stats->increment( 'editstash.cache_hits.presumed_fresh' );
+                       $this->incrStatsByContent( 'cache_hits.presumed_fresh', $content );
                        $logger->debug( "Timestamp-based cache hit for key '{key}'.", $context );
                } elseif ( $user->isAnon() ) {
                        $lastEdit = $this->lastEditTime( $user );
                        $cacheTime = $editInfo->output->getCacheTime();
                        if ( $lastEdit < $cacheTime ) {
                                // Logged-out user made no local upload/template edits in the meantime
-                               $this->stats->increment( 'editstash.cache_hits.presumed_fresh' );
+                               $this->incrStatsByContent( 'cache_hits.presumed_fresh', $content );
                                $logger->debug( "Edit check based cache hit for key '{key}'.", $context );
                        } else {
                                $isCacheUsable = false;
-                               $this->stats->increment( 'editstash.cache_misses.proven_stale' );
+                               $this->incrStatsByContent( 'cache_misses.proven_stale', $content );
                                $logger->info( "Stale cache for key '{key}' due to outside edits.", $context );
                        }
                } else {
                        if ( $editInfo->edits === $user->getEditCount() ) {
                                // Logged-in user made no local upload/template edits in the meantime
-                               $this->stats->increment( 'editstash.cache_hits.presumed_fresh' );
+                               $this->incrStatsByContent( 'cache_hits.presumed_fresh', $content );
                                $logger->debug( "Edit count based cache hit for key '{key}'.", $context );
                        } else {
                                $isCacheUsable = false;
-                               $this->stats->increment( 'editstash.cache_misses.proven_stale' );
+                               $this->incrStatsByContent( 'cache_misses.proven_stale', $content );
                                $logger->info( "Stale cache for key '{key}'due to outside edits.", $context );
                        }
                }
@@ -269,22 +269,42 @@ class PageEditStash {
 
                if ( $editInfo->output->getFlag( 'vary-revision' ) ) {
                        // This can be used for the initial parse, e.g. for filters or doEditContent(),
-                       // but a second parse will be triggered in doEditUpdates(). This is not optimal.
+                       // but a second parse will be triggered in doEditUpdates() no matter what
                        $logger->info(
-                               "Cache for key '{key}' has vary_revision; post-insertion parse inevitable.",
-                               $context
-                       );
-               } elseif ( $editInfo->output->getFlag( 'vary-revision-id' ) ) {
-                       // Similar to the above if we didn't guess the ID correctly.
-                       $logger->debug(
-                               "Cache for key '{key}' has vary_revision_id; post-insertion parse possible.",
+                               "Cache for key '{key}' has 'vary-revision'; post-insertion parse inevitable.",
                                $context
                        );
+               } else {
+                       static $flagsMaybeReparse = [
+                               // Similar to the above if we didn't guess the ID correctly
+                               'vary-revision-id',
+                               // Similar to the above if we didn't guess the timestamp correctly
+                               'vary-revision-timestamp',
+                               // Similar to the above if we didn't guess the content correctly
+                               'vary-revision-sha1'
+                       ];
+                       foreach ( $flagsMaybeReparse as $flag ) {
+                               if ( $editInfo->output->getFlag( $flag ) ) {
+                                       $logger->debug(
+                                               "Cache for key '{key}' has $flag; post-insertion parse possible.",
+                                               $context
+                                       );
+                               }
+                       }
                }
 
                return $editInfo;
        }
 
+       /**
+        * @param string $subkey
+        * @param Content $content
+        */
+       private function incrStatsByContent( $subkey, Content $content ) {
+               $this->stats->increment( 'editstash.' . $subkey ); // overall for b/c
+               $this->stats->increment( 'editstash_by_model.' . $content->getModel() . '.' . $subkey );
+       }
+
        /**
         * @param string $key
         * @return bool|stdClass
@@ -329,7 +349,12 @@ class PageEditStash {
        public function stashInputText( $text, $textHash ) {
                $textKey = $this->cache->makeKey( 'stashedit', 'text', $textHash );
 
-               return $this->cache->set( $textKey, $text, self::MAX_CACHE_TTL );
+               return $this->cache->set(
+                       $textKey,
+                       $text,
+                       self::MAX_CACHE_TTL,
+                       BagOStuff::WRITE_ALLOW_SEGMENTS
+               );
        }
 
        /**
@@ -337,7 +362,8 @@ class PageEditStash {
         * @return string|null TS_MW timestamp or null
         */
        private function lastEditTime( User $user ) {
-               $db = $this->lb->getConnection( DB_REPLICA );
+               $db = $this->lb->getConnectionRef( DB_REPLICA );
+
                $actorQuery = ActorMigration::newMigration()->getWhere( $db, 'rc_user', $user, false );
                $time = $db->selectField(
                        [ 'recentchanges' ] + $actorQuery['tables'],
@@ -379,7 +405,7 @@ class PageEditStash {
         */
        private function getStashKey( Title $title, $contentHash, User $user ) {
                return $this->cache->makeKey(
-                       'stashed-edit-info',
+                       'stashedit-info-v1',
                        md5( $title->getPrefixedDBkey() ),
                        // Account for the edit model/text
                        $contentHash,
@@ -388,29 +414,13 @@ class PageEditStash {
                );
        }
 
-       /**
-        * @param string $hash
-        * @return string
-        */
-       private function getStashParserOutputKey( $hash ) {
-               return $this->cache->makeKey( 'stashed-edit-output', $hash );
-       }
-
        /**
         * @param string $key
         * @return stdClass|bool Object map (pstContent,output,outputID,timestamp,edits) or false
         */
        private function getStashValue( $key ) {
                $stashInfo = $this->cache->get( $key );
-               if ( !is_object( $stashInfo ) ) {
-                       return false;
-               }
-
-               $parserOutputKey = $this->getStashParserOutputKey( $stashInfo->outputID );
-               $parserOutput = $this->cache->get( $parserOutputKey );
-               if ( $parserOutput instanceof ParserOutput ) {
-                       $stashInfo->output = $parserOutput;
-
+               if ( is_object( $stashInfo ) && $stashInfo->output instanceof ParserOutput ) {
                        return $stashInfo;
                }
 
@@ -450,23 +460,14 @@ class PageEditStash {
                }
 
                // Store what is actually needed and split the output into another key (T204742)
-               $parserOutputID = md5( $key );
                $stashInfo = (object)[
                        'pstContent' => $pstContent,
-                       'outputID'   => $parserOutputID,
+                       'output'     => $parserOutput,
                        'timestamp'  => $timestamp,
                        'edits'      => $user->getEditCount()
                ];
 
-               $ok = $this->cache->set( $key, $stashInfo, $ttl );
-               if ( $ok ) {
-                       $ok = $this->cache->set(
-                               $this->getStashParserOutputKey( $parserOutputID ),
-                               $parserOutput,
-                               $ttl
-                       );
-               }
-
+               $ok = $this->cache->set( $key, $stashInfo, $ttl, BagOStuff::WRITE_ALLOW_SEGMENTS );
                if ( $ok ) {
                        // These blobs can waste slots in low cardinality memcached slabs
                        $this->pruneExcessStashedEntries( $user, $key );
@@ -485,7 +486,7 @@ class PageEditStash {
                $keyList = $this->cache->get( $key ) ?: [];
                if ( count( $keyList ) >= self::MAX_CACHE_RECENT ) {
                        $oldestKey = array_shift( $keyList );
-                       $this->cache->delete( $oldestKey );
+                       $this->cache->delete( $oldestKey, BagOStuff::WRITE_PRUNE_SEGMENTS );
                }
 
                $keyList[] = $newKey;