Merge "Reduce outages due to master DB problems in doViewUpdates"
[lhc/web/wiklou.git] / includes / page / WikiPage.php
index b435c5c..059a3f8 100644 (file)
@@ -367,14 +367,12 @@ class WikiPage implements Page, IDBAccessObject {
                        $data = $this->pageDataFromTitle( wfGetDB( DB_MASTER ), $this->mTitle );
                } elseif ( $from === self::READ_NORMAL ) {
                        $data = $this->pageDataFromTitle( wfGetDB( DB_SLAVE ), $this->mTitle );
-                       // Use a "last rev inserted" timestamp key to diminish the issue of slave lag.
-                       // Note that DB also stores the master position in the session and checks it.
-                       $touched = $this->getCachedLastEditTime();
-                       if ( $touched ) { // key set
-                               if ( !$data || $touched > wfTimestamp( TS_MW, $data->page_touched ) ) {
-                                       $from = self::READ_LATEST;
-                                       $data = $this->pageDataFromTitle( wfGetDB( DB_MASTER ), $this->mTitle );
-                               }
+                       if ( !$data
+                               && wfGetLB()->getServerCount() > 1
+                               && wfGetLB()->hasOrMadeRecentMasterChanges()
+                       ) {
+                               $from = self::READ_LATEST;
+                               $data = $this->pageDataFromTitle( wfGetDB( DB_MASTER ), $this->mTitle );
                        }
                } else {
                        // No idea from where the caller got this data, assume slave database.
@@ -810,29 +808,6 @@ class WikiPage implements Page, IDBAccessObject {
                }
        }
 
-       /**
-        * Get the cached timestamp for the last time the page changed.
-        * This is only used to help handle slave lag by comparing to page_touched.
-        * @return string MW timestamp
-        */
-       protected function getCachedLastEditTime() {
-               global $wgMemc;
-               $key = wfMemcKey( 'page-lastedit', md5( $this->mTitle->getPrefixedDBkey() ) );
-               return $wgMemc->get( $key );
-       }
-
-       /**
-        * Set the cached timestamp for the last time the page changed.
-        * This is only used to help handle slave lag by comparing to page_touched.
-        * @param string $timestamp
-        * @return void
-        */
-       public function setCachedLastEditTime( $timestamp ) {
-               global $wgMemc;
-               $key = wfMemcKey( 'page-lastedit', md5( $this->mTitle->getPrefixedDBkey() ) );
-               $wgMemc->set( $key, wfTimestamp( TS_MW, $timestamp ), 60 * 15 );
-       }
-
        /**
         * Determine whether a page would be suitable for being counted as an
         * article in the site_stats table based on the title & its content
@@ -1167,7 +1142,12 @@ class WikiPage implements Page, IDBAccessObject {
                }
 
                // Update newtalk / watchlist notification status
-               $user->clearNotification( $this->mTitle, $oldid );
+               try {
+                       $user->clearNotification( $this->mTitle, $oldid );
+               } catch ( DBError $e ) {
+                       // Avoid outage if the master is not reachable
+                       MWExceptionHandler::logException( $e );
+               }
        }
 
        /**
@@ -1175,28 +1155,24 @@ class WikiPage implements Page, IDBAccessObject {
         * @return bool
         */
        public function doPurge() {
-               global $wgUseSquid;
-
                if ( !Hooks::run( 'ArticlePurge', array( &$this ) ) ) {
                        return false;
                }
 
-               // Invalidate the cache
-               $this->mTitle->invalidateCache();
-
-               if ( $wgUseSquid ) {
-                       // Commit the transaction before the purge is sent
-                       $dbw = wfGetDB( DB_MASTER );
-                       $dbw->commit( __METHOD__ );
-
-                       // Send purge
-                       $update = SquidUpdate::newSimplePurge( $this->mTitle );
-                       $update->doUpdate();
-               }
+               $title = $this->mTitle;
+               wfGetDB( DB_MASTER )->onTransactionIdle( function() use ( $title ) {
+                       global $wgUseSquid;
+                       // Invalidate the cache in auto-commit mode
+                       $title->invalidateCache();
+                       if ( $wgUseSquid ) {
+                               // Send purge now that page_touched update was committed above
+                               $update = SquidUpdate::newSimplePurge( $title );
+                               $update->doUpdate();
+                       }
+               } );
 
                if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
                        // @todo move this logic to MessageCache
-
                        if ( $this->exists() ) {
                                // NOTE: use transclusion text for messages.
                                //       This is consistent with  MessageCache::getMsgFromNamespace()
@@ -1213,6 +1189,7 @@ class WikiPage implements Page, IDBAccessObject {
 
                        MessageCache::singleton()->replace( $this->mTitle->getDBkey(), $text );
                }
+
                return true;
        }
 
@@ -1311,7 +1288,6 @@ class WikiPage implements Page, IDBAccessObject {
                if ( $result ) {
                        $this->updateRedirectOn( $dbw, $rt, $lastRevIsRedirect );
                        $this->setLastEdit( $revision );
-                       $this->setCachedLastEditTime( $now );
                        $this->mLatest = $revision->getId();
                        $this->mIsRedirect = (bool)$rt;
                        // Update the LinkCache.
@@ -1513,8 +1489,18 @@ class WikiPage implements Page, IDBAccessObject {
 
                $baseRevId = null;
                if ( $edittime && $sectionId !== 'new' ) {
-                       $dbw = wfGetDB( DB_MASTER );
-                       $rev = Revision::loadFromTimestamp( $dbw, $this->mTitle, $edittime );
+                       $dbr = wfGetDB( DB_SLAVE );
+                       $rev = Revision::loadFromTimestamp( $dbr, $this->mTitle, $edittime );
+                       // Try the master if this thread may have just added it.
+                       // This could be abstracted into a Revision method, but we don't want
+                       // to encourage loading of revisions by timestamp.
+                       if ( !$rev
+                               && wfGetLB()->getServerCount() > 1
+                               && wfGetLB()->hasOrMadeRecentMasterChanges()
+                       ) {
+                               $dbw = wfGetDB( DB_MASTER );
+                               $rev = Revision::loadFromTimestamp( $dbw, $this->mTitle, $edittime );
+                       }
                        if ( $rev ) {
                                $baseRevId = $rev->getId();
                        }
@@ -1553,10 +1539,7 @@ class WikiPage implements Page, IDBAccessObject {
                        if ( is_null( $baseRevId ) || $sectionId === 'new' ) {
                                $oldContent = $this->getContent();
                        } else {
-                               // TODO: try DB_SLAVE first
-                               $dbw = wfGetDB( DB_MASTER );
-                               $rev = Revision::loadFromId( $dbw, $baseRevId );
-
+                               $rev = Revision::newFromId( $baseRevId );
                                if ( !$rev ) {
                                        wfDebug( __METHOD__ . " asked for bogus section (page: " .
                                                $this->getId() . "; section: $sectionId)\n" );
@@ -2097,7 +2080,7 @@ class WikiPage implements Page, IDBAccessObject {
                }
 
                // The edit may have already been prepared via api.php?action=stashedit
-               $cachedEdit = $useCache && $wgAjaxEditStash
+               $cachedEdit = $useCache && $wgAjaxEditStash && !$user->isAllowed( 'bot' )
                        ? ApiStashEdit::checkCache( $this->getTitle(), $content, $user )
                        : false;
 
@@ -2389,8 +2372,8 @@ class WikiPage implements Page, IDBAccessObject {
                $dbw = wfGetDB( DB_MASTER );
 
                foreach ( $restrictionTypes as $action ) {
-                       if ( !isset( $expiry[$action] ) ) {
-                               $expiry[$action] = $dbw->getInfinity();
+                       if ( !isset( $expiry[$action] ) || $expiry[$action] === $dbw->getInfinity() ) {
+                               $expiry[$action] = 'infinity';
                        }
                        if ( !isset( $limit[$action] ) ) {
                                $limit[$action] = '';
@@ -2630,10 +2613,8 @@ class WikiPage implements Page, IDBAccessObject {
         */
        protected function formatExpiry( $expiry ) {
                global $wgContLang;
-               $dbr = wfGetDB( DB_SLAVE );
 
-               $encodedExpiry = $dbr->encodeExpiry( $expiry );
-               if ( $encodedExpiry != 'infinity' ) {
+               if ( $expiry != 'infinity' ) {
                        return wfMessage(
                                'protect-expiring',
                                $wgContLang->timeanddate( $expiry, false, false ),