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

Change subject: Reuse page preview parses by using the edit stash system
......................................................................


Reuse page preview parses by using the edit stash system

* This also changes previews to render section edit tokens but
  remove them on output, avoiding cache fragmentation.
* Also shortened the resulting getStashKey() value.

Change-Id: Ic8fa87669106b960c76912b864788b781f6ee2e6
---
M includes/EditPage.php
M includes/api/ApiStashEdit.php
M includes/parser/ParserOptions.php
3 files changed, 153 insertions(+), 32 deletions(-)

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



diff --git a/includes/EditPage.php b/includes/EditPage.php
index db2e442..993c20a 100644
--- a/includes/EditPage.php
+++ b/includes/EditPage.php
@@ -3496,7 +3496,6 @@
                        }
 
                        $parserOptions = $this->mArticle->makeParserOptions( 
$this->mArticle->getContext() );
-                       $parserOptions->setEditSection( false );
                        $parserOptions->setIsPreview( true );
                        $parserOptions->setIsSectionPreview( !is_null( 
$this->section ) && $this->section !== '' );
 
@@ -3548,13 +3547,17 @@
                        # For CSS/JS pages, we should have called the 
ShowRawCssJs hook here.
                        # But it's now deprecated, so never mind
 
-                       $content = $content->preSaveTransform( $this->mTitle, 
$wgUser, $parserOptions );
-                       $parserOutput = $content->getParserOutput(
-                               $this->getArticle()->getTitle(),
-                               null,
-                               $parserOptions
+                       $pstContent = $content->preSaveTransform( 
$this->mTitle, $wgUser, $parserOptions );
+                       $parserOutput = $pstContent->getParserOutput( 
$this->mTitle, null, $parserOptions );
+
+                       # Try to stash the edit for the final submission step
+                       # @todo: different date format preferences cause cache 
misses
+                       ApiStashEdit::stashEditFromPreview(
+                               $this->getArticle(), $content, $pstContent,
+                               $parserOutput, $parserOptions, $parserOptions, 
wfTimestampNow()
                        );
 
+                       $parserOutput->setEditSectionTokens( false ); // no 
section edit links
                        $previewHTML = $parserOutput->getText();
                        $this->mParserOutput = $parserOutput;
                        $wgOut->addParserOutputMetadata( $parserOutput );
diff --git a/includes/api/ApiStashEdit.php b/includes/api/ApiStashEdit.php
index 2bb0b5c..b32d332 100644
--- a/includes/api/ApiStashEdit.php
+++ b/includes/api/ApiStashEdit.php
@@ -121,17 +121,10 @@
                }
 
                if ( $editInfo && $editInfo->output ) {
-                       $parserOutput = $editInfo->output;
-                       // If an item is renewed, mind the cache TTL determined 
by config and parser functions
-                       $since = time() - wfTimestamp( TS_UNIX, 
$parserOutput->getTimestamp() );
-                       $ttl = min( $parserOutput->getCacheExpiry() - $since, 5 
* 60 );
-                       if ( $ttl > 0 && !$parserOutput->getFlag( 
'vary-revision' ) ) {
-                               // Only store what is actually needed
-                               $stashInfo = (object)array(
-                                       'pstContent' => $editInfo->pstContent,
-                                       'output' => $editInfo->output,
-                                       'timestamp' => $editInfo->timestamp
-                               );
+                       list( $stashInfo, $ttl ) = self::buildStashValue(
+                               $editInfo->pstContent, $editInfo->output, 
$editInfo->timestamp
+                       );
+                       if ( $stashInfo ) {
                                $ok = $wgMemc->set( $key, $stashInfo, $ttl );
                                if ( $ok ) {
                                        $status = 'stashed';
@@ -150,24 +143,69 @@
        }
 
        /**
-        * Get the temporary prepared edit stash key for a user
+        * Attempt to cache PST content and corresponding parser output in 
passing
         *
-        * @param Title $title
-        * @param Content $content
-        * @param User $user User to get parser options from
-        * @return string
+        * This method can be called when the output was already generated for 
other
+        * reasons. Parsing should not be done just to call this method, 
however.
+        * $pstOpts must be that of the user doing the edit preview. If $pOpts 
does
+        * not match the options of WikiPage::makeParserOptions( 'canonical' ), 
this
+        * will do nothing. Provided the values are cacheable, they will be 
stored
+        * in memcached so that final edit submission might make use of them.
+        *
+        * @param Article|WikiPage $page Page title
+        * @param Content $content Proposed page content
+        * @param Content $pstContent The result of preSaveTransform() on 
$content
+        * @param ParserOutput $pOut The result of getParserOutput() on 
$pstContent
+        * @param ParserOptions $pstOpts Options for $pstContent (MUST be for 
prospective author)
+        * @param ParserOptions $pOpts Options for $pOut
+        * @param string $timestamp TS_MW timestamp of parser output generation
+        * @return boolean Success
         */
-       protected static function getStashKey(
-               Title $title, Content $content, User $user
+       public static function stashEditFromPreview(
+               Page $page, Content $content, Content $pstContent, ParserOutput 
$pOut,
+               ParserOptions $pstOpts, ParserOptions $pOpts, $timestamp
        ) {
-               return wfMemcKey( 'prepared-edit',
-                       md5( $title->getPrefixedDBkey() ), // handle rename 
races
-                       $content->getModel(),
-                       $content->getDefaultFormat(),
-                       sha1( $content->serialize( $content->getDefaultFormat() 
) ),
-                       $user->getId() ?: md5( $user->getName() ), // account 
for user parser options
-                       $user->getId() ? $user->getTouched() : '-' // handle 
preference change races
-               );
+               global $wgMemc;
+
+               // getIsPreview() controls parser function behavior that 
references things
+               // like user/revision that don't exists yet. The user/text 
should already
+               // be set correctly by callers, just double check the preview 
flag.
+               if ( !$pOpts->getIsPreview() ) {
+                       return false; // sanity
+               } elseif ( $pOpts->getIsSectionPreview() ) {
+                       return false; // short-circuit (need the full content)
+               }
+
+               // PST parser options are for the user (handles signatures, 
etc...)
+               $user = $pstOpts->getUser();
+               // Get a key based on the source text, format, and user 
preferences
+               $key = self::getStashKey( $page->getTitle(), $content, $user );
+
+               // Parser output options must match cannonical options.
+               // Treat some options as matching that are different but don't 
matter.
+               $canonicalPOpts = $page->makeParserOptions( 'canonical' );
+               $canonicalPOpts->setIsPreview( true ); // force match
+               $canonicalPOpts->setTimestamp( $pOpts->getTimestamp() ); // 
force match
+               if ( !$pOpts->matches( $canonicalPOpts ) ) {
+                       wfDebugLog( 'StashEdit', "Uncacheable preview output 
for key '$key' (options)." );
+                       return false;
+               }
+
+               // Build a value to cache with a proper TTL
+               list( $stashInfo, $ttl ) = self::buildStashValue( $pstContent, 
$pOut, $timestamp );
+               if ( !$stashInfo ) {
+                       wfDebugLog( 'StashEdit', "Uncacheable parser output for 
key '$key' (rev/TTL)." );
+                       return false;
+               }
+
+               $ok = $wgMemc->set( $key, $stashInfo, $ttl );
+               if ( !$ok ) {
+                       wfDebugLog( 'StashEdit', "Failed to cache preview 
parser output for key '$key'." );
+               } else {
+                       wfDebugLog( 'StashEdit', "Cached preview output for key 
'$key'." );
+               }
+
+               return $ok;
        }
 
        /**
@@ -253,6 +291,59 @@
                return $editInfo;
        }
 
+       /**
+        * Get the temporary prepared edit stash key for a user
+        *
+        * This key can be used for caching prepared edits provided:
+        *   - a) The $user was used for PST options
+        *   - b) The parser output was made from the PST using cannonical 
matching options
+        *
+        * @param Title $title
+        * @param Content $content
+        * @param User $user User to get parser options from
+        * @return string
+        */
+       protected static function getStashKey( Title $title, Content $content, 
User $user ) {
+               $hash = sha1( implode( ':', array(
+                       $content->getModel(),
+                       $content->getDefaultFormat(),
+                       sha1( $content->serialize( $content->getDefaultFormat() 
) ),
+                       $user->getId() ?: md5( $user->getName() ), // account 
for user parser options
+                       $user->getId() ? $user->getTouched() : '-' // handle 
preference change races
+               ) ) );
+
+               return wfMemcKey( 'prepared-edit', md5( 
$title->getPrefixedDBkey() ), $hash );
+       }
+
+       /**
+        * Build a value to store in memcached based on the PST content and 
parser output
+        *
+        * This makes a simple version of WikiPage::prepareContentForEdit() as 
stash info
+        *
+        * @param Content $pstContent
+        * @param ParserOutput $parserOutput
+        * @param string $timestamp TS_MW
+        * @return array (stash info array, TTL in seconds) or (null, 0)
+        */
+       protected static function buildStashValue(
+               Content $pstContent, ParserOutput $parserOutput, $timestamp
+       ) {
+               // If an item is renewed, mind the cache TTL determined by 
config and parser functions
+               $since = time() - wfTimestamp( TS_UNIX, 
$parserOutput->getTimestamp() );
+               $ttl = min( $parserOutput->getCacheExpiry() - $since, 5 * 60 );
+               if ( $ttl > 0 && !$parserOutput->getFlag( 'vary-revision' ) ) {
+                       // Only store what is actually needed
+                       $stashInfo = (object)array(
+                               'pstContent' => $pstContent,
+                               'output'     => $parserOutput,
+                               'timestamp'  => $timestamp
+                       );
+                       return array( $stashInfo, $ttl );
+               }
+
+               return array( null, 0 );
+       }
+
        public function getAllowedParams() {
                return array(
                        'title' => array(
diff --git a/includes/parser/ParserOptions.php 
b/includes/parser/ParserOptions.php
index b570fa5..a61dbf0 100644
--- a/includes/parser/ParserOptions.php
+++ b/includes/parser/ParserOptions.php
@@ -641,6 +641,7 @@
 
                wfProfileIn( __METHOD__ );
 
+               // *UPDATE* ParserOptions::matches() if any of this changes as 
needed
                $this->mInterwikiMagic = $wgInterwikiMagic;
                $this->mAllowExternalImages = $wgAllowExternalImages;
                $this->mAllowExternalImagesFrom = $wgAllowExternalImagesFrom;
@@ -667,6 +668,32 @@
        }
 
        /**
+        * Check if these options match that of another options set
+        *
+        * This ignores report limit settings that only affect HTML comments
+        *
+        * @return bool
+        * @since 1.25
+        */
+       public function matches( ParserOptions $other ) {
+               $fields = array_keys( get_class_vars( __CLASS__ ) );
+               $fields = array_diff( $fields, array(
+                       'mEnableLimitReport', // only effects HTML comments
+                       'onAccessCallback', // only used for ParserOutput 
option tracking
+               ) );
+               foreach ( $fields as $field ) {
+                       if ( !is_object( $this->$field ) && $this->$field !== 
$other->$field ) {
+                               return false;
+                       }
+               }
+               // Check the object and lazy-loaded options
+               return (
+                       $this->mUserLang->getCode() === 
$other->mUserLang->getCode() &&
+                       $this->getDateFormat() === $other->getDateFormat()
+               );
+       }
+
+       /**
         * Registers a callback for tracking which ParserOptions which are used.
         * This is a private API with the parser.
         * @param callable $callback

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

Gerrit-MessageType: merged
Gerrit-Change-Id: Ic8fa87669106b960c76912b864788b781f6ee2e6
Gerrit-PatchSet: 7
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Aaron Schulz <[email protected]>
Gerrit-Reviewer: Anomie <[email protected]>
Gerrit-Reviewer: Cscott <[email protected]>
Gerrit-Reviewer: Jackmcbarn <[email protected]>
Gerrit-Reviewer: Ori.livneh <[email protected]>
Gerrit-Reviewer: Tpt <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

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

Reply via email to