Move up devunt's name to Developers
[lhc/web/wiklou.git] / includes / db / LoadBalancer.php
index 48c636e..2ea2487 100644 (file)
@@ -214,7 +214,7 @@ class LoadBalancer {
         * @return bool|int|string
         */
        public function getReaderIndex( $group = false, $wiki = false ) {
-               global $wgReadOnly, $wgDBtype;
+               global $wgDBtype;
 
                # @todo FIXME: For now, only go through all this for mysql databases
                if ( $wgDBtype != 'mysql' ) {
@@ -258,7 +258,7 @@ class LoadBalancer {
                # meets our criteria
                $currentLoads = $nonErrorLoads;
                while ( count( $currentLoads ) ) {
-                       if ( $wgReadOnly || $this->mAllowLagged || $laggedSlaveMode ) {
+                       if ( $this->mAllowLagged || $laggedSlaveMode ) {
                                $i = ArrayUtils::pickRandom( $currentLoads );
                        } else {
                                $i = false;
@@ -277,8 +277,6 @@ class LoadBalancer {
                                if ( $i === false && count( $currentLoads ) != 0 ) {
                                        # All slaves lagged. Switch to read-only mode
                                        wfDebugLog( 'replication', "All slaves lagged. Switch to read-only mode" );
-                                       $wgReadOnly = 'The database has been automatically locked ' .
-                                               'while the slave database servers catch up to the master';
                                        $i = ArrayUtils::pickRandom( $currentLoads );
                                        $laggedSlaveMode = true;
                                }
@@ -330,6 +328,10 @@ class LoadBalancer {
                        }
                        if ( $this->mReadIndex <= 0 && $this->mLoads[$i] > 0 && $group === false ) {
                                $this->mReadIndex = $i;
+                               # Record if the generic reader index is in "lagged slave" mode
+                               if ( $laggedSlaveMode ) {
+                                       $this->mLaggedSlaveMode = true;
+                               }
                        }
                        $serverName = $this->getServerName( $i );
                        wfDebug( __METHOD__ . ": using server $serverName for group '$group'\n" );
@@ -356,6 +358,37 @@ class LoadBalancer {
                }
        }
 
+       /**
+        * Set the master wait position and wait for a "generic" slave to catch up to it
+        *
+        * This can be used a faster proxy for waitForAll()
+        *
+        * @param DBMasterPos $pos
+        * @param int $timeout Max seconds to wait; default is mWaitTimeout
+        * @return bool Success (able to connect and no timeouts reached)
+        * @since 1.26
+        */
+       public function waitForOne( $pos, $timeout = null ) {
+               $this->mWaitForPos = $pos;
+
+               $i = $this->mReadIndex;
+               if ( $i <= 0 ) {
+                       // Pick a generic slave if there isn't one yet
+                       $readLoads = $this->mLoads;
+                       unset( $readLoads[$this->getWriterIndex()] ); // slaves only
+                       $readLoads = array_filter( $readLoads ); // with non-zero load
+                       $i = ArrayUtils::pickRandom( $readLoads );
+               }
+
+               if ( $i > 0 ) {
+                       $ok = $this->doWait( $i, true, $timeout );
+               } else {
+                       $ok = true; // no applicable loads
+               }
+
+               return $ok;
+       }
+
        /**
         * Set the master wait position and wait for ALL slaves to catch up to it
         * @param DBMasterPos $pos
@@ -812,8 +845,9 @@ class LoadBalancer {
 
        /**
         * @return int
+        * @since 1.26
         */
-       private function getWriterIndex() {
+       public function getWriterIndex() {
                return 0;
        }
 
@@ -978,7 +1012,6 @@ class LoadBalancer {
         *  Issue COMMIT only on master, only if queries were done on connection
         */
        public function commitMasterChanges() {
-               // Always 0, but who knows.. :)
                $masterIndex = $this->getWriterIndex();
                foreach ( $this->mConns as $conns2 ) {
                        if ( empty( $conns2[$masterIndex] ) ) {
@@ -998,7 +1031,8 @@ class LoadBalancer {
         * @since 1.23
         */
        public function rollbackMasterChanges() {
-               // Always 0, but who knows.. :)
+               $failedServers = array();
+
                $masterIndex = $this->getWriterIndex();
                foreach ( $this->mConns as $conns2 ) {
                        if ( empty( $conns2[$masterIndex] ) ) {
@@ -1007,10 +1041,20 @@ class LoadBalancer {
                        /** @var DatabaseBase $conn */
                        foreach ( $conns2[$masterIndex] as $conn ) {
                                if ( $conn->trxLevel() && $conn->writesOrCallbacksPending() ) {
-                                       $conn->rollback( __METHOD__, 'flush' );
+                                       try {
+                                               $conn->rollback( __METHOD__, 'flush' );
+                                       } catch ( DBError $e ) {
+                                               MWExceptionHandler::logException( $e );
+                                               $failedServers[] = $conn->getServer();
+                                       }
                                }
                        }
                }
+
+               if ( $failedServers ) {
+                       throw new DBExpectedError( null, "Rollback failed on server(s) " .
+                               implode( ', ', array_unique( $failedServers ) ) );
+               }
        }
 
        /**
@@ -1027,7 +1071,6 @@ class LoadBalancer {
         * @return bool
         */
        public function hasMasterChanges() {
-               // Always 0, but who knows.. :)
                $masterIndex = $this->getWriterIndex();
                foreach ( $this->mConns as $conns2 ) {
                        if ( empty( $conns2[$masterIndex] ) ) {
@@ -1050,7 +1093,6 @@ class LoadBalancer {
         */
        public function lastMasterChangeTimestamp() {
                $lastTime = false;
-               // Always 0, but who knows.. :)
                $masterIndex = $this->getWriterIndex();
                foreach ( $this->mConns as $conns2 ) {
                        if ( empty( $conns2[$masterIndex] ) ) {
@@ -1088,9 +1130,12 @@ class LoadBalancer {
        }
 
        /**
-        * @return bool
+        * @return bool Whether the generic connection for reads is highly "lagged"
         */
        public function getLaggedSlaveMode() {
+               # Get a generic reader connection
+               $this->getConnection( DB_SLAVE );
+
                return $this->mLaggedSlaveMode;
        }
 
@@ -1228,49 +1273,3 @@ class LoadBalancer {
                $this->mProcCache->clear( 'slave_lag' );
        }
 }
-
-/**
- * Helper class to handle automatically marking connections as reusable (via RAII pattern)
- * as well handling deferring the actual network connection until the handle is used
- *
- * @ingroup Database
- * @since 1.22
- */
-class DBConnRef implements IDatabase {
-       /** @var LoadBalancer */
-       private $lb;
-
-       /** @var DatabaseBase|null */
-       private $conn;
-
-       /** @var array|null */
-       private $params;
-
-       /**
-        * @param LoadBalancer $lb
-        * @param DatabaseBase|array $conn Connection or (server index, group, wiki ID) array
-        */
-       public function __construct( LoadBalancer $lb, $conn ) {
-               $this->lb = $lb;
-               if ( $conn instanceof DatabaseBase ) {
-                       $this->conn = $conn;
-               } else {
-                       $this->params = $conn;
-               }
-       }
-
-       public function __call( $name, $arguments ) {
-               if ( $this->conn === null ) {
-                       list( $db, $groups, $wiki ) = $this->params;
-                       $this->conn = $this->lb->getConnection( $db, $groups, $wiki );
-               }
-
-               return call_user_func_array( array( $this->conn, $name ), $arguments );
-       }
-
-       public function __destruct() {
-               if ( $this->conn !== null ) {
-                       $this->lb->reuseConnection( $this->conn );
-               }
-       }
-}