Merge "Reduce page deletion lock contention"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Tue, 2 Jun 2015 13:20:27 +0000 (13:20 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Tue, 2 Jun 2015 13:20:27 +0000 (13:20 +0000)
1  2 
includes/page/WikiPage.php

@@@ -1961,13 -1961,13 +1961,13 @@@ class WikiPage implements Page, IDBAcce
                $status->value['revision'] = $revision;
  
                $hook_args = array( &$this, &$user, $content, $summary,
 -                                                      $flags & EDIT_MINOR, null, null, &$flags, $revision, &$status, $baseRevId );
 +                      $flags & EDIT_MINOR, null, null, &$flags, $revision, &$status, $baseRevId );
  
                ContentHandler::runLegacyHooks( 'ArticleSaveComplete', $hook_args );
                Hooks::run( 'PageContentSaveComplete', $hook_args );
  
                // Promote user to any groups they meet the criteria for
 -              $dbw->onTransactionIdle( function () use ( $user ) {
 +              DeferredUpdates::addCallableUpdate( function () use ( $user ) {
                        $user->addAutopromoteOnceGroups( 'onEdit' );
                        $user->addAutopromoteOnceGroups( 'onView' ); // b/c
                } );
                $dbw->begin( __METHOD__ );
  
                if ( $id == 0 ) {
-                       $this->loadPageData( 'forupdate' );
+                       // T98706: lock the page from various other updates but avoid using
+                       // WikiPage::READ_LOCKING as that will carry over the FOR UPDATE to
+                       // the revisions queries (which also JOIN on user). Only lock the page
+                       // row and CAS check on page_latest to see if the trx snapshot matches.
+                       $latest = $this->lock();
+                       $this->loadPageData( WikiPage::READ_LATEST );
                        $id = $this->getID();
-                       if ( $id == 0 ) {
+                       if ( $id == 0 || $this->getLatest() != $latest ) {
+                               // Page not there or trx snapshot is stale
                                $dbw->rollback( __METHOD__ );
                                $status->error( 'cannotdelete', wfEscapeWikiText( $this->getTitle()->getPrefixedText() ) );
                                return $status;
                return $status;
        }
  
+       /**
+        * Lock the page row for this title and return page_latest (or 0)
+        *
+        * @return integer
+        */
+       protected function lock() {
+               return (int)wfGetDB( DB_MASTER )->selectField(
+                       'page',
+                       'page_latest',
+                       array(
+                               'page_namespace' => $this->getTitle()->getNamespace(),
+                               'page_title' => $this->getTitle()->getDBkey()
+                       ),
+                       __METHOD__,
+                       array( 'FOR UPDATE' )
+               );
+       }
        /**
         * Do some database updates after deletion
         *