* @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' ) {
# meets our criteria
$currentLoads = $nonErrorLoads;
while ( count( $currentLoads ) ) {
- if ( $wgReadOnly || $this->mAllowLagged || $laggedSlaveMode ) {
+ if ( $this->mAllowLagged || $laggedSlaveMode ) {
$i = ArrayUtils::pickRandom( $currentLoads );
} else {
$i = false;
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;
}
}
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" );
}
}
+ /**
+ * 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
/**
* @return int
+ * @since 1.26
*/
- private function getWriterIndex() {
+ public function getWriterIndex() {
return 0;
}
* 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] ) ) {
* @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] ) ) {
/** @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 ) ) );
+ }
}
/**
* @return bool
*/
public function hasMasterChanges() {
- // Always 0, but who knows.. :)
$masterIndex = $this->getWriterIndex();
foreach ( $this->mConns as $conns2 ) {
if ( empty( $conns2[$masterIndex] ) ) {
*/
public function lastMasterChangeTimestamp() {
$lastTime = false;
- // Always 0, but who knows.. :)
$masterIndex = $this->getWriterIndex();
foreach ( $this->mConns as $conns2 ) {
if ( empty( $conns2[$masterIndex] ) ) {
}
/**
- * @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;
}
$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 );
- }
- }
-}