Merge "Reduce WikiPage::getContentModel() queries via caching"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Wed, 14 Sep 2016 19:18:32 +0000 (19:18 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 14 Sep 2016 19:18:32 +0000 (19:18 +0000)
1  2 
includes/page/WikiPage.php

@@@ -83,11 -83,6 +83,11 @@@ class WikiPage implements Page, IDBAcce
         */
        protected $mLinksUpdated = '19700101000000';
  
 +      const PURGE_CDN_CACHE = 1; // purge CDN cache for page variant URLs
 +      const PURGE_CLUSTER_PCACHE = 2; // purge parser cache in the local datacenter
 +      const PURGE_GLOBAL_PCACHE = 4; // set page_touched to clear parser cache in all datacenters
 +      const PURGE_ALL = 7;
 +
        /**
         * Constructor and clear the article
         * @param Title $title Reference to a Title object.
         * @return bool
         */
        public function hasViewableContent() {
 -              return $this->exists() || $this->mTitle->isAlwaysKnown();
 +              return $this->mTitle->isKnown();
        }
  
        /**
         */
        public function getContentModel() {
                if ( $this->exists() ) {
-                       // look at the revision's actual content model
-                       $rev = $this->getRevision();
-                       if ( $rev !== null ) {
-                               return $rev->getContentModel();
-                       } else {
-                               $title = $this->mTitle->getPrefixedDBkey();
-                               wfWarn( "Page $title exists but has no (visible) revisions!" );
-                       }
+                       $cache = ObjectCache::getMainWANInstance();
+                       return $cache->getWithSetCallback(
+                               $cache->makeKey( 'page', 'content-model', $this->getLatest() ),
+                               $cache::TTL_MONTH,
+                               function () {
+                                       $rev = $this->getRevision();
+                                       if ( $rev ) {
+                                               // Look at the revision's actual content model
+                                               return $rev->getContentModel();
+                                       } else {
+                                               $title = $this->mTitle->getPrefixedDBkey();
+                                               wfWarn( "Page $title exists but has no (visible) revisions!" );
+                                               return $this->mTitle->getContentModel();
+                                       }
+                               }
+                       );
                }
  
                // use the default model for this page
  
        /**
         * Perform the actions of a page purging
 +       * @param integer $flags Bitfield of WikiPage::PURGE_* constants
         * @return bool
         */
 -      public function doPurge() {
 +      public function doPurge( $flags = self::PURGE_ALL ) {
                if ( !Hooks::run( 'ArticlePurge', [ &$this ] ) ) {
                        return false;
                }
  
 -              $this->mTitle->invalidateCache();
 -              // Send purge after above page_touched update was committed
 -              DeferredUpdates::addUpdate(
 -                      new CdnCacheUpdate( $this->mTitle->getCdnUrls() ),
 -                      DeferredUpdates::PRESEND
 -              );
 +              if ( ( $flags & self::PURGE_GLOBAL_PCACHE ) == self::PURGE_GLOBAL_PCACHE ) {
 +                      // Set page_touched in the database to invalidate all DC caches
 +                      $this->mTitle->invalidateCache();
 +              } elseif ( ( $flags & self::PURGE_CLUSTER_PCACHE ) == self::PURGE_CLUSTER_PCACHE ) {
 +                      // Delete the parser options key in the local cluster to invalidate the DC cache
 +                      ParserCache::singleton()->deleteOptionsKey( $this );
 +                      // Avoid sending HTTP 304s in ViewAction to the client who just issued the purge
 +                      $cache = ObjectCache::getLocalClusterInstance();
 +                      $cache->set(
 +                              $cache->makeKey( 'page', 'last-dc-purge', $this->getId() ),
 +                              wfTimestamp( TS_MW ),
 +                              $cache::TTL_HOUR
 +                      );
 +              }
 +
 +              if ( ( $flags & self::PURGE_CDN_CACHE ) == self::PURGE_CDN_CACHE ) {
 +                      // Clear any HTML file cache
 +                      HTMLFileCache::clearFileCache( $this->getTitle() );
 +                      // Send purge after any page_touched above update was committed
 +                      DeferredUpdates::addUpdate(
 +                              new CdnCacheUpdate( $this->mTitle->getCdnUrls() ),
 +                              DeferredUpdates::PRESEND
 +                      );
 +              }
  
                if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
                        // @todo move this logic to MessageCache
                return true;
        }
  
 +      /**
 +       * Get the last time a user explicitly purged the page via action=purge
 +       *
 +       * @return string|bool TS_MW timestamp or false
 +       * @since 1.28
 +       */
 +      public function getLastPurgeTimestamp() {
 +              $cache = ObjectCache::getLocalClusterInstance();
 +
 +              return $cache->get( $cache->makeKey( 'page', 'last-dc-purge', $this->getId() ) );
 +      }
 +
        /**
         * Insert a new empty page record for this article.
         * This *must* be followed up by creating a revision
         */
        public function doEditContent(
                Content $content, $summary, $flags = 0, $baseRevId = false,
 -              User $user = null, $serialFormat = null, $tags = null
 +              User $user = null, $serialFormat = null, $tags = []
        ) {
                global $wgUser, $wgUseAutomaticEditSummaries;
  
 +              // Old default parameter for $tags was null
 +              if ( $tags === null ) {
 +                      $tags = [];
 +              }
 +
                // Low-level sanity check
                if ( $this->mTitle->getText() === '' ) {
                        throw new MWException( 'Something is trying to edit an article with an empty title' );
                $old_revision = $this->getRevision(); // current revision
                $old_content = $this->getContent( Revision::RAW ); // current revision's content
  
 +              if ( $old_content && $old_content->getModel() !== $content->getModel() ) {
 +                      $tags[] = 'mw-contentmodelchange';
 +              }
 +
                // Provide autosummaries if one is not provided and autosummaries are enabled
                if ( $wgUseAutomaticEditSummaries && ( $flags & EDIT_AUTOSUMMARY ) && $summary == '' ) {
                        $handler = $content->getContentHandler();
                Hooks::run( 'WikiPageDeletionUpdates', [ $this, $content, &$updates ] );
                return $updates;
        }
 +
 +      /**
 +       * Whether this content displayed on this page
 +       * comes from the local database
 +       *
 +       * @since 1.28
 +       * @return bool
 +       */
 +      public function isLocal() {
 +              return true;
 +      }
  }