Merge "SECURITY: Make blocks log users out if $wgBlockDisablesLogin"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Tue, 23 Aug 2016 01:34:04 +0000 (01:34 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Tue, 23 Aug 2016 01:34:04 +0000 (01:34 +0000)
autoload.php
includes/FileDeleteForm.php
includes/deferred/LinksUpdate.php
includes/deferred/SqlDataUpdate.php
includes/jobqueue/JobQueueDB.php
includes/jobqueue/utils/PurgeJobUtils.php [new file with mode: 0644]
includes/objectcache/SqlBagOStuff.php
includes/page/WikiPage.php

index 330ebc4..5457d2a 100644 (file)
@@ -1096,6 +1096,7 @@ $wgAutoloadLocalClasses = [
        'PurgeAction' => __DIR__ . '/includes/actions/PurgeAction.php',
        'PurgeChangedFiles' => __DIR__ . '/maintenance/purgeChangedFiles.php',
        'PurgeChangedPages' => __DIR__ . '/maintenance/purgeChangedPages.php',
+       'PurgeJobUtils' => __DIR__ . '/includes/jobqueue/utils/PurgeJobUtils.php',
        'PurgeList' => __DIR__ . '/maintenance/purgeList.php',
        'PurgeOldText' => __DIR__ . '/maintenance/purgeOldText.php',
        'PurgeParserCache' => __DIR__ . '/maintenance/purgeParserCache.php',
index 361058b..65638f2 100644 (file)
@@ -189,31 +189,24 @@ class FileDeleteForm {
                        );
                        $page = WikiPage::factory( $title );
                        $dbw = wfGetDB( DB_MASTER );
-                       try {
-                               $dbw->startAtomic( __METHOD__ );
-                               // delete the associated article first
-                               $error = '';
-                               $deleteStatus = $page->doDeleteArticleReal( $reason, $suppress, 0, false, $error, $user );
-                               // doDeleteArticleReal() returns a non-fatal error status if the page
-                               // or revision is missing, so check for isOK() rather than isGood()
-                               if ( $deleteStatus->isOK() ) {
-                                       $status = $file->delete( $reason, $suppress, $user );
-                                       if ( $status->isOK() ) {
-                                               $status->value = $deleteStatus->value; // log id
-                                               $dbw->endAtomic( __METHOD__ );
-                                       } else {
-                                               // Page deleted but file still there? rollback page delete
-                                               wfGetLBFactory()->rollbackMasterChanges( __METHOD__ );
-                                       }
-                               } else {
-                                       // Done; nothing changed
+                       $dbw->startAtomic( __METHOD__ );
+                       // delete the associated article first
+                       $error = '';
+                       $deleteStatus = $page->doDeleteArticleReal( $reason, $suppress, 0, false, $error, $user );
+                       // doDeleteArticleReal() returns a non-fatal error status if the page
+                       // or revision is missing, so check for isOK() rather than isGood()
+                       if ( $deleteStatus->isOK() ) {
+                               $status = $file->delete( $reason, $suppress, $user );
+                               if ( $status->isOK() ) {
+                                       $status->value = $deleteStatus->value; // log id
                                        $dbw->endAtomic( __METHOD__ );
+                               } else {
+                                       // Page deleted but file still there? rollback page delete
+                                       wfGetLBFactory()->rollbackMasterChanges( __METHOD__ );
                                }
-                       } catch ( Exception $e ) {
-                               // Rollback before returning to prevent UI from displaying
-                               // incorrect "View or restore N deleted edits?"
-                               $dbw->rollback( __METHOD__ );
-                               throw $e;
+                       } else {
+                               // Done; nothing changed
+                               $dbw->endAtomic( __METHOD__ );
                        }
                }
 
index 4f40c38..5e02c5c 100644 (file)
@@ -304,7 +304,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @param array $cats
         */
        function invalidateCategories( $cats ) {
-               $this->invalidatePages( NS_CATEGORY, array_keys( $cats ) );
+               PurgeJobUtils::invalidatePages( $this->mDb, NS_CATEGORY, array_keys( $cats ) );
        }
 
        /**
@@ -323,7 +323,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @param array $images
         */
        function invalidateImageDescriptions( $images ) {
-               $this->invalidatePages( NS_FILE, array_keys( $images ) );
+               PurgeJobUtils::invalidatePages( $this->mDb, NS_FILE, array_keys( $images ) );
        }
 
        /**
index 9740cbe..ff06915 100644 (file)
@@ -98,53 +98,4 @@ abstract class SqlDataUpdate extends DataUpdate {
                        $this->mHasTransaction = false;
                }
        }
-
-       /**
-        * Invalidate the cache of a list of pages from a single namespace.
-        * This is intended for use by subclasses.
-        *
-        * @param int $namespace Namespace number
-        * @param array $dbkeys
-        */
-       protected function invalidatePages( $namespace, array $dbkeys ) {
-               if ( $dbkeys === [] ) {
-                       return;
-               }
-
-               $dbw = $this->mDb;
-               $dbw->onTransactionPreCommitOrIdle( function() use ( $dbw, $namespace, $dbkeys ) {
-                       /**
-                        * Determine which pages need to be updated
-                        * This is necessary to prevent the job queue from smashing the DB with
-                        * large numbers of concurrent invalidations of the same page
-                        */
-                       $now = $dbw->timestamp();
-                       $ids = $dbw->selectFieldValues( 'page',
-                               'page_id',
-                               [
-                                       'page_namespace' => $namespace,
-                                       'page_title' => $dbkeys,
-                                       'page_touched < ' . $dbw->addQuotes( $now )
-                               ],
-                               __METHOD__
-                       );
-
-                       if ( $ids === [] ) {
-                               return;
-                       }
-
-                       /**
-                        * Do the update
-                        * We still need the page_touched condition, in case the row has changed since
-                        * the non-locking select above.
-                        */
-                       $dbw->update( 'page',
-                               [ 'page_touched' => $now ],
-                               [
-                                       'page_id' => $ids,
-                                       'page_touched < ' . $dbw->addQuotes( $now )
-                               ], __METHOD__
-                       );
-               } );
-       }
 }
index 479ec32..b9f4592 100644 (file)
@@ -245,10 +245,7 @@ class JobQueueDB extends JobQueue {
                                count( $rowSet ) + count( $rowList ) - count( $rows )
                        );
                } catch ( DBError $e ) {
-                       if ( $flags & self::QOS_ATOMIC ) {
-                               $dbw->rollback( $method );
-                       }
-                       throw $e;
+                       $this->throwDBException( $e );
                }
                if ( $flags & self::QOS_ATOMIC ) {
                        $dbw->endAtomic( $method );
diff --git a/includes/jobqueue/utils/PurgeJobUtils.php b/includes/jobqueue/utils/PurgeJobUtils.php
new file mode 100644 (file)
index 0000000..329bc23
--- /dev/null
@@ -0,0 +1,71 @@
+<?php
+/**
+ * Base code for update jobs that put some secondary data extracted
+ * from article content into the database.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+class PurgeJobUtils {
+       /**
+        * Invalidate the cache of a list of pages from a single namespace.
+        * This is intended for use by subclasses.
+        *
+        * @param IDatabase $dbw
+        * @param int $namespace Namespace number
+        * @param array $dbkeys
+        */
+       public static function invalidatePages( IDatabase $dbw, $namespace, array $dbkeys ) {
+               if ( $dbkeys === [] ) {
+                       return;
+               }
+
+               $dbw->onTransactionPreCommitOrIdle( function() use ( $dbw, $namespace, $dbkeys ) {
+                       // Determine which pages need to be updated.
+                       // This is necessary to prevent the job queue from smashing the DB with
+                       // large numbers of concurrent invalidations of the same page.
+                       $now = $dbw->timestamp();
+                       $ids = $dbw->selectFieldValues(
+                               'page',
+                               'page_id',
+                               [
+                                       'page_namespace' => $namespace,
+                                       'page_title' => $dbkeys,
+                                       'page_touched < ' . $dbw->addQuotes( $now )
+                               ],
+                               __METHOD__
+                       );
+
+                       if ( $ids === [] ) {
+                               return;
+                       }
+
+                       // Do the update.
+                       // We still need the page_touched condition, in case the row has changed since
+                       // the non-locking select above.
+                       $dbw->update(
+                               'page',
+                               [ 'page_touched' => $now ],
+                               [
+                                       'page_id' => $ids,
+                                       'page_touched < ' . $dbw->addQuotes( $now )
+                               ],
+                               __METHOD__
+                       );
+               } );
+       }
+}
index 5556dd8..84936a4 100644 (file)
@@ -695,6 +695,7 @@ class SqlBagOStuff extends BagOStuff {
         *
         * @param DBError $exception
         * @param int $serverIndex
+        * @throws Exception
         */
        protected function handleWriteError( DBError $exception, $serverIndex ) {
                if ( $exception instanceof DBConnectionError ) {
@@ -702,6 +703,13 @@ class SqlBagOStuff extends BagOStuff {
                }
                if ( $exception->db && $exception->db->wasReadOnlyError() ) {
                        if ( $exception->db->trxLevel() ) {
+                               if ( !$this->serverInfos ) {
+                                       // Errors like deadlocks and connection drops already cause rollback.
+                                       // For consistency, we have no choice but to throw an error and trigger
+                                       // complete rollback if the main DB is also being used as the cache DB.
+                                       throw $exception;
+                               }
+
                                try {
                                        $exception->db->rollback( __METHOD__ );
                                } catch ( DBError $e ) {
index 0344756..033eb67 100644 (file)
@@ -1770,7 +1770,6 @@ class WikiPage implements Page, IDBAccessObject {
                        $revisionId = $revision->insertOn( $dbw );
                        // Update page_latest and friends to reflect the new revision
                        if ( !$this->updateRevisionOn( $dbw, $revision, null, $meta['oldIsRedirect'] ) ) {
-                               $dbw->rollback( __METHOD__ ); // sanity; this should never happen
                                throw new MWException( "Failed to update page row to use new revision." );
                        }
 
@@ -1920,7 +1919,6 @@ class WikiPage implements Page, IDBAccessObject {
                $revisionId = $revision->insertOn( $dbw );
                // Update the page record with revision data
                if ( !$this->updateRevisionOn( $dbw, $revision, 0 ) ) {
-                       $dbw->rollback( __METHOD__ ); // sanity; this should never happen
                        throw new MWException( "Failed to update page row to use new revision." );
                }