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