Merge "Use Elvis operator where possible in API"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Tue, 8 Mar 2016 16:24:10 +0000 (16:24 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Tue, 8 Mar 2016 16:24:10 +0000 (16:24 +0000)
13 files changed:
includes/DefaultSettings.php
includes/context/RequestContext.php
includes/db/DatabaseMysqlBase.php
includes/db/DatabaseUtility.php
includes/db/IDatabase.php
includes/filebackend/FileBackendMultiWrite.php
includes/filerepo/FileRepo.php
includes/jobqueue/JobQueueDB.php
includes/jobqueue/JobQueueFederated.php
includes/upload/UploadFromChunks.php
load.php
resources/src/mediawiki.special/mediawiki.special.preferences.js
tests/phpunit/includes/db/DatabaseMysqlBaseTest.php

index 2a9986e..c04602c 100644 (file)
@@ -2096,7 +2096,7 @@ $wgTransactionalTimeLimit = 120;
 
 /**
  * Directory for caching data in the local filesystem. Should not be accessible
- * from the web. Set this to false to not use any local caches.
+ * from the web.
  *
  * Note: if multiple wikis share the same localisation cache directory, they
  * must all have the same set of extensions. You can set a directory just for
index 35ee1b7..c8b8108 100644 (file)
@@ -167,7 +167,7 @@ class RequestContext implements IContextSource, MutableContext {
         *
         * @param Title $title
         */
-       public function setTitle( Title $title ) {
+       public function setTitle( Title $title = null ) {
                $this->title = $title;
                // Erase the WikiPage so a new one with the new title gets created.
                $this->wikipage = null;
index 7058061..1e27205 100644 (file)
@@ -757,19 +757,13 @@ abstract class DatabaseMysqlBase extends Database {
                return $approxLag;
        }
 
-       /**
-        * Wait for the slave to catch up to a given master position.
-        * @todo Return values for this and base class are rubbish
-        *
-        * @param DBMasterPos|MySQLMasterPos $pos
-        * @param int $timeout The maximum number of seconds to wait for synchronisation
-        * @return int Zero if the slave was past that position already,
-        *   greater than zero if we waited for some period of time, less than
-        *   zero if we timed out.
-        */
        function masterPosWait( DBMasterPos $pos, $timeout ) {
+               if ( !( $pos instanceof MySQLMasterPos ) ) {
+                       throw new InvalidArgumentException( "Position not an instance of MySQLMasterPos" );
+               }
+
                if ( $this->lastKnownSlavePos && $this->lastKnownSlavePos->hasReached( $pos ) ) {
-                       return '0'; // http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html
+                       return 0;
                }
 
                # Commit any open transactions
@@ -778,18 +772,28 @@ abstract class DatabaseMysqlBase extends Database {
                # Call doQuery() directly, to avoid opening a transaction if DBO_TRX is set
                $encFile = $this->addQuotes( $pos->file );
                $encPos = intval( $pos->pos );
-               $sql = "SELECT MASTER_POS_WAIT($encFile, $encPos, $timeout)";
-               $res = $this->doQuery( $sql );
-
-               $status = false;
-               if ( $res ) {
-                       $row = $this->fetchRow( $res );
-                       if ( $row ) {
-                               $status = $row[0]; // can be NULL, -1, or 0+ per the MySQL manual
-                               if ( ctype_digit( $status ) ) { // success
-                                       $this->lastKnownSlavePos = $pos;
-                               }
+               $res = $this->doQuery( "SELECT MASTER_POS_WAIT($encFile, $encPos, $timeout)" );
+
+               $row = $res ? $this->fetchRow( $res ) : false;
+               if ( !$row ) {
+                       throw new DBExpectedError( $this, "Failed to query MASTER_POS_WAIT()" );
+               }
+
+               // Result can be NULL (error), -1 (timeout), or 0+ per the MySQL manual
+               $status = ( $row[0] !== null ) ? intval( $row[0] ) : null;
+               if ( $status === null ) {
+                       // T126436: jobs programmed to wait on master positions might be referencing binlogs
+                       // with an old master hostname. Such calls make MASTER_POS_WAIT() return null. Try
+                       // to detect this and treat the slave as having reached the position; a proper master
+                       // switchover already requires that the new master be caught up before the switch.
+                       $slavePos = $this->getSlavePos();
+                       if ( $slavePos && !$slavePos->channelsMatch( $pos ) ) {
+                               $this->lastKnownSlavePos = $slavePos;
+                               $status = 0;
                        }
+               } elseif ( $status >= 0 ) {
+                       // Remember that this position was reached to save queries next time
+                       $this->lastKnownSlavePos = $pos;
                }
 
                return $status;
@@ -1446,11 +1450,34 @@ class MySQLMasterPos implements DBMasterPos {
                return ( $thisPos && $thatPos && $thisPos >= $thatPos );
        }
 
+       function channelsMatch( DBMasterPos $pos ) {
+               if ( !( $pos instanceof self ) ) {
+                       throw new InvalidArgumentException( "Position not an instance of " . __CLASS__ );
+               }
+
+               $thisBinlog = $this->getBinlogName();
+               $thatBinlog = $pos->getBinlogName();
+
+               return ( $thisBinlog !== false && $thisBinlog === $thatBinlog );
+       }
+
        function __toString() {
                // e.g db1034-bin.000976/843431247
                return "{$this->file}/{$this->pos}";
        }
 
+       /**
+        * @return string|bool
+        */
+       protected function getBinlogName() {
+               $m = [];
+               if ( preg_match( '!^(.+)\.(\d+)/(\d+)$!', (string)$this, $m ) ) {
+                       return $m[1];
+               }
+
+               return false;
+       }
+
        /**
         * @return array|bool (int, int)
         */
index 6fa8bf0..b6c37ee 100644 (file)
@@ -332,6 +332,13 @@ interface DBMasterPos {
         */
        public function hasReached( DBMasterPos $pos );
 
+       /**
+        * @param DBMasterPos $pos
+        * @return bool Whether this position appears to be for the same channel as another
+        * @since 1.27
+        */
+       public function channelsMatch( DBMasterPos $pos );
+
        /**
         * @return string
         * @since 1.27
index 7855861..8b1c3df 100644 (file)
@@ -1183,14 +1183,13 @@ interface IDatabase {
        public function wasReadOnlyError();
 
        /**
-        * Wait for the slave to catch up to a given master position.
+        * Wait for the slave to catch up to a given master position
         *
         * @param DBMasterPos $pos
-        * @param int $timeout The maximum number of seconds to wait for
-        *   synchronisation
-        * @return int Zero if the slave was past that position already,
+        * @param int $timeout The maximum number of seconds to wait for synchronisation
+        * @return int|null Zero if the slave was past that position already,
         *   greater than zero if we waited for some period of time, less than
-        *   zero if we timed out.
+        *   zero if it timed out, and null on error
         */
        public function masterPosWait( DBMasterPos $pos, $timeout );
 
index 5a103c6..6f40bda 100644 (file)
@@ -202,11 +202,17 @@ class FileBackendMultiWrite extends FileBackend {
                                if ( $this->asyncWrites && !$this->hasVolatileSources( $ops ) ) {
                                        // Bind $scopeLock to the callback to preserve locks
                                        DeferredUpdates::addCallableUpdate(
-                                               function() use ( $backend, $realOps, $opts, $scopeLock ) {
+                                               function() use ( $backend, $realOps, $opts, $scopeLock, $relevantPaths ) {
+                                                       wfDebugLog( 'FileOperationReplication',
+                                                               "'{$backend->getName()}' async replication; paths: " .
+                                                               FormatJson::encode( $relevantPaths ) );
                                                        $backend->doOperations( $realOps, $opts );
                                                }
                                        );
                                } else {
+                                       wfDebugLog( 'FileOperationReplication',
+                                               "'{$backend->getName()}' sync replication; paths: " .
+                                               FormatJson::encode( $relevantPaths ) );
                                        $status->merge( $backend->doOperations( $realOps, $opts ) );
                                }
                        }
index 789803f..f3c2abf 100644 (file)
@@ -1115,7 +1115,7 @@ class FileRepo {
         * @param array $srcPaths Ordered list of source virtual URLs/storage paths
         * @param string $dstPath Target file system path
         * @param int $flags Bitwise combination of the following flags:
-        *   self::DELETE_SOURCE     Delete the source files
+        *   self::DELETE_SOURCE     Delete the source files on success
         * @return FileRepoStatus
         */
        public function concatenate( array $srcPaths, $dstPath, $flags = 0 ) {
index aaf9fb0..479ec32 100644 (file)
@@ -452,7 +452,6 @@ class JobQueueDB extends JobQueue {
         * @see JobQueue::doAck()
         * @param Job $job
         * @throws MWException
-        * @return Job|bool
         */
        protected function doAck( Job $job ) {
                if ( !isset( $job->metadata['id'] ) ) {
@@ -476,8 +475,6 @@ class JobQueueDB extends JobQueue {
                } catch ( DBError $e ) {
                        $this->throwDBException( $e );
                }
-
-               return true;
        }
 
        /**
index c127239..bd832db 100644 (file)
@@ -49,7 +49,7 @@
 class JobQueueFederated extends JobQueue {
        /** @var HashRing */
        protected $partitionRing;
-       /** @var array (partition name => JobQueue) reverse sorted by weight */
+       /** @var JobQueue[] (partition name => JobQueue) reverse sorted by weight */
        protected $partitionQueues = [];
 
        /** @var int Maximum number of partitions to try */
@@ -311,7 +311,7 @@ class JobQueueFederated extends JobQueue {
                        throw new MWException( "The given job has no defined partition name." );
                }
 
-               return $this->partitionQueues[$job->metadata['QueuePartition']]->ack( $job );
+               $this->partitionQueues[$job->metadata['QueuePartition']]->ack( $job );
        }
 
        protected function doIsRootJobOldDuplicate( Job $job ) {
index d82a9e6..8ee0845 100644 (file)
@@ -145,6 +145,7 @@ class UploadFromChunks extends UploadFromFile {
                if ( !$status->isOk() ) {
                        return $status;
                }
+
                wfDebugLog( 'fileconcatenate', "Combined $i chunks in $tAmount seconds." );
 
                // File system path
index c6452fb..d30a348 100644 (file)
--- a/load.php
+++ b/load.php
 
 use MediaWiki\Logger\LoggerFactory;
 
+// This endpoint is supposed to be independent of request cookies and other
+// details of the session. Log warnings for violations of the no-session
+// constraint.
+define( 'MW_NO_SESSION', 'warn' );
+
 require __DIR__ . '/includes/WebStart.php';
 
 // URL safety checks
index 29322f4..a756f22 100644 (file)
 
                // Check if all of the form values are unchanged
                function isPrefsChanged() {
-                       var inputs = $( '#mw-prefs-form :input' ),
+                       var inputs = $( '#mw-prefs-form :input[name]' ),
                                input, $input, inputType,
                                index, optIndex,
                                opt;
                                $input = $( input );
 
                                // Different types of inputs have different methods for accessing defaults
-                               if ( $input.is( 'select' ) ) { // <select> has the property defaultSelected for each option
+                               if ( $input.is( 'select' ) ) {
+                                       // <select> has the property defaultSelected for each option
                                        for ( optIndex = 0; optIndex < input.options.length; optIndex++ ) {
                                                opt = input.options[ optIndex ];
                                                if ( opt.selected !== opt.defaultSelected ) {
index 8e8002c..168b2c6 100644 (file)
@@ -247,14 +247,64 @@ class DatabaseMysqlBaseTest extends MediaWikiTestCase {
                ];
        }
 
-       function testMasterPos() {
-               $pos1 = new MySQLMasterPos( 'db1034-bin.000976', '843431247' );
-               $pos2 = new MySQLMasterPos( 'db1034-bin.000976', '843431248' );
-
-               $this->assertTrue( $pos1->hasReached( $pos1 ) );
-               $this->assertTrue( $pos2->hasReached( $pos2 ) );
-               $this->assertTrue( $pos2->hasReached( $pos1 ) );
-               $this->assertFalse( $pos1->hasReached( $pos2 ) );
+       /**
+        * @dataProvider provideComparePositions
+        */
+       function testHasReached( MySQLMasterPos $lowerPos, MySQLMasterPos $higherPos ) {
+               $this->assertTrue( $higherPos->hasReached( $lowerPos ) );
+               $this->assertTrue( $higherPos->hasReached( $higherPos ) );
+               $this->assertTrue( $lowerPos->hasReached( $lowerPos ) );
+               $this->assertFalse( $lowerPos->hasReached( $higherPos ) );
+       }
+
+       function provideComparePositions() {
+               return [
+                       [
+                               new MySQLMasterPos( 'db1034-bin.000976', '843431247' ),
+                               new MySQLMasterPos( 'db1034-bin.000976', '843431248' )
+                       ],
+                       [
+                               new MySQLMasterPos( 'db1034-bin.000976', '999' ),
+                               new MySQLMasterPos( 'db1034-bin.000976', '1000' )
+                       ],
+                       [
+                               new MySQLMasterPos( 'db1034-bin.000976', '999' ),
+                               new MySQLMasterPos( 'db1035-bin.000976', '1000' )
+                       ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideChannelPositions
+        */
+       function testChannelsMatch( MySQLMasterPos $pos1, MySQLMasterPos $pos2, $matches ) {
+               $this->assertEquals( $matches, $pos1->channelsMatch( $pos2 ) );
+               $this->assertEquals( $matches, $pos2->channelsMatch( $pos1 ) );
+       }
+
+       function provideChannelPositions() {
+               return [
+                       [
+                               new MySQLMasterPos( 'db1034-bin.000876', '44' ),
+                               new MySQLMasterPos( 'db1034-bin.000976', '74' ),
+                               true
+                       ],
+                       [
+                               new MySQLMasterPos( 'db1052-bin.000976', '999' ),
+                               new MySQLMasterPos( 'db1052-bin.000976', '1000' ),
+                               true
+                       ],
+                       [
+                               new MySQLMasterPos( 'db1066-bin.000976', '9999' ),
+                               new MySQLMasterPos( 'db1035-bin.000976', '10000' ),
+                               false
+                       ],
+                       [
+                               new MySQLMasterPos( 'db1066-bin.000976', '9999' ),
+                               new MySQLMasterPos( 'trump2016.000976', '10000' ),
+                               false
+                       ],
+               ];
        }
 
        /**