X-Git-Url: https://git.heureux-cyclage.org/?p=lhc%2Fweb%2Fwiklou.git;a=blobdiff_plain;f=includes%2Flibs%2Frdbms%2Floadbalancer%2FLoadBalancer.php;h=d5e65cdddf58894343904256f6054d35a485418a;hp=7c1b9d98dda257b1ab1328e64a628883c03713cc;hb=a3dcadab7b1e584254d72db95cc337ac1185cbbc;hpb=238494825619ad1a5f81f48e76c8e0f5a674b22e diff --git a/includes/libs/rdbms/loadbalancer/LoadBalancer.php b/includes/libs/rdbms/loadbalancer/LoadBalancer.php index 7c1b9d98dd..d5e65cdddf 100644 --- a/includes/libs/rdbms/loadbalancer/LoadBalancer.php +++ b/includes/libs/rdbms/loadbalancer/LoadBalancer.php @@ -59,8 +59,8 @@ class LoadBalancer implements ILoadBalancer { /** @var ILoadMonitor */ private $loadMonitor; - /** @var ChronologyProtector|null */ - private $chronProt; + /** @var callable|null Callback to run before the first connection attempt */ + private $chronologyCallback; /** @var BagOStuff */ private $srvCache; /** @var WANObjectCache */ @@ -111,11 +111,13 @@ class LoadBalancer implements ILoadBalancer { /** @var callable Exception logger */ private $errorLogger; + /** @var callable Deprecation logger */ + private $deprecationLogger; /** @var bool */ private $disabled = false; - /** @var bool */ - private $chronProtInitialized = false; + /** @var bool Whether any connection has been attempted yet */ + private $connectionAttempted = false; /** @var int */ private $maxLag = self::MAX_LAG_DEFAULT; @@ -223,6 +225,11 @@ class LoadBalancer implements ILoadBalancer { : function ( Exception $e ) { trigger_error( get_class( $e ) . ': ' . $e->getMessage(), E_USER_WARNING ); }; + $this->deprecationLogger = isset( $params['deprecationLogger'] ) + ? $params['deprecationLogger'] + : function ( $msg ) { + trigger_error( $msg, E_USER_DEPRECATED ); + }; foreach ( [ 'replLogger', 'connLogger', 'queryLogger', 'perfLogger' ] as $key ) { $this->$key = isset( $params[$key] ) ? $params[$key] : new NullLogger(); @@ -236,8 +243,8 @@ class LoadBalancer implements ILoadBalancer { : ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' ); $this->agent = isset( $params['agent'] ) ? $params['agent'] : ''; - if ( isset( $params['chronologyProtector'] ) ) { - $this->chronProt = $params['chronologyProtector']; + if ( isset( $params['chronologyCallback'] ) ) { + $this->chronologyCallback = $params['chronologyCallback']; } } @@ -304,7 +311,7 @@ class LoadBalancer implements ILoadBalancer { ": server {host} is not replicating?", [ 'host' => $host ] ); unset( $loads[$i] ); } elseif ( $lag > $maxServerLag ) { - $this->replLogger->info( + $this->replLogger->debug( __METHOD__ . ": server {host} has {lag} seconds of lag (>= {maxlag})", [ 'host' => $host, 'lag' => $lag, 'maxlag' => $maxServerLag ] @@ -362,7 +369,7 @@ class LoadBalancer implements ILoadBalancer { // Scale the configured load ratios according to each server's load and state $this->getLoadMonitor()->scaleLoads( $loads, $domain ); - // Pick a server to use, accounting for weights, load, lag, and mWaitForPos + // Pick a server to use, accounting for weights, load, lag, and "waitForPos" list( $i, $laggedReplicaMode ) = $this->pickReaderIndex( $loads, $domain ); if ( $i === false ) { // Replica DB connection unsuccessful @@ -372,7 +379,7 @@ class LoadBalancer implements ILoadBalancer { if ( $this->waitForPos && $i != $this->getWriterIndex() ) { // Before any data queries are run, wait for the server to catch up to the // specified position. This is used to improve session consistency. Note that - // when LoadBalancer::waitFor() sets mWaitForPos, the waiting triggers here, + // when LoadBalancer::waitFor() sets "waitForPos", the waiting triggers here, // so update laggedReplicaMode as needed for consistency. if ( !$this->doWait( $i ) ) { $laggedReplicaMode = true; @@ -417,7 +424,7 @@ class LoadBalancer implements ILoadBalancer { } else { $i = false; if ( $this->waitForPos && $this->waitForPos->asOfTime() ) { - // ChronologyProtecter sets mWaitForPos for session consistency. + // "chronologyCallback" sets "waitForPos" for session consistency. // This triggers doWait() after connect, so it's especially good to // avoid lagged servers so as to avoid excessive delay in that method. $ago = microtime( true ) - $this->waitForPos->asOfTime(); @@ -532,7 +539,7 @@ class LoadBalancer implements ILoadBalancer { if ( $this->loads[$i] > 0 ) { $start = microtime( true ); $ok = $this->doWait( $i, true, $timeout ) && $ok; - $timeout -= ( microtime( true ) - $start ); + $timeout -= intval( microtime( true ) - $start ); if ( $timeout <= 0 ) { break; // timeout reached } @@ -559,17 +566,17 @@ class LoadBalancer implements ILoadBalancer { } } - /** - * @param int $i - * @return IDatabase|bool - */ - public function getAnyOpenConnection( $i ) { + public function getAnyOpenConnection( $i, $flags = 0 ) { + $autocommit = ( ( $flags & self::CONN_TRX_AUTOCOMMIT ) == self::CONN_TRX_AUTOCOMMIT ); foreach ( $this->conns as $connsByServer ) { - if ( !empty( $connsByServer[$i] ) ) { - /** @var IDatabase[] $serverConns */ - $serverConns = $connsByServer[$i]; + if ( !isset( $connsByServer[$i] ) ) { + continue; + } - return reset( $serverConns ); + foreach ( $connsByServer[$i] as $conn ) { + if ( !$autocommit || $conn->getLBInfo( 'autoCommitOnly' ) ) { + return $conn; + } } } @@ -584,7 +591,7 @@ class LoadBalancer implements ILoadBalancer { * @return bool */ protected function doWait( $index, $open = false, $timeout = null ) { - $timeout = max( 1, $timeout ?: $this->waitTimeout ); + $timeout = max( 1, intval( $timeout ?: $this->waitTimeout ) ); // Check if we already know that the DB has reached this point $server = $this->getServerName( $index ); @@ -682,7 +689,7 @@ class LoadBalancer implements ILoadBalancer { $domain = false; // local connection requested } - if ( ( $flags & self::CONN_TRX_AUTO ) === self::CONN_TRX_AUTO ) { + if ( ( $flags & self::CONN_TRX_AUTOCOMMIT ) === self::CONN_TRX_AUTOCOMMIT ) { // Assuming all servers are of the same type (or similar), which is overwhelmingly // the case, use the master server information to get the attributes. The information // for $i cannot be used since it might be DB_REPLICA, which might require connection @@ -693,8 +700,9 @@ class LoadBalancer implements ILoadBalancer { // rows (e.g. FOR UPDATE) or (b) make small commits during a larger transactions // to reduce lock contention. None of these apply for sqlite and using separate // connections just causes self-deadlocks. - $flags &= ~self::CONN_TRX_AUTO; - $this->connLogger->info( __METHOD__ . ': ignoring CONN_TRX_AUTO to avoid deadlocks.' ); + $flags &= ~self::CONN_TRX_AUTOCOMMIT; + $this->connLogger->info( __METHOD__ . + ': ignoring CONN_TRX_AUTOCOMMIT to avoid deadlocks.' ); } } @@ -707,7 +715,7 @@ class LoadBalancer implements ILoadBalancer { if ( $i == self::DB_MASTER ) { $i = $this->getWriterIndex(); - } else { + } elseif ( $i == self::DB_REPLICA ) { # Try to find an available server in any the query groups (in order) foreach ( $groups as $group ) { $groupIndex = $this->getReaderIndex( $group, $domain ); @@ -841,18 +849,18 @@ class LoadBalancer implements ILoadBalancer { $domain = false; // local connection requested } - if ( !$this->chronProtInitialized && $this->chronProt ) { + if ( !$this->connectionAttempted && $this->chronologyCallback ) { $this->connLogger->debug( __METHOD__ . ': calling initLB() before first connection.' ); - // Load CP positions before connecting so that doWait() triggers later if needed - $this->chronProtInitialized = true; - $this->chronProt->initLB( $this ); + // Load any "waitFor" positions before connecting so that doWait() is triggered + $this->connectionAttempted = true; + call_user_func( $this->chronologyCallback, $this ); } // Check if an auto-commit connection is being requested. If so, it will not reuse the // main set of DB connections but rather its own pool since: // a) those are usually set to implicitly use transaction rounds via DBO_TRX // b) those must support the use of explicit transaction rounds via beginMasterChanges() - $autoCommit = ( ( $flags & self::CONN_TRX_AUTO ) == self::CONN_TRX_AUTO ); + $autoCommit = ( ( $flags & self::CONN_TRX_AUTOCOMMIT ) == self::CONN_TRX_AUTOCOMMIT ); if ( $domain !== false ) { // Connection is to a foreign domain @@ -930,7 +938,7 @@ class LoadBalancer implements ILoadBalancer { $domainInstance = DatabaseDomain::newFromId( $domain ); $dbName = $domainInstance->getDatabase(); $prefix = $domainInstance->getTablePrefix(); - $autoCommit = ( ( $flags & self::CONN_TRX_AUTO ) == self::CONN_TRX_AUTO ); + $autoCommit = ( ( $flags & self::CONN_TRX_AUTOCOMMIT ) == self::CONN_TRX_AUTOCOMMIT ); if ( $autoCommit ) { $connFreeKey = self::KEY_FOREIGN_FREE_NOROUND; @@ -1067,6 +1075,7 @@ class LoadBalancer implements ILoadBalancer { $server['connLogger'] = $this->connLogger; $server['queryLogger'] = $this->queryLogger; $server['errorLogger'] = $this->errorLogger; + $server['deprecationLogger'] = $this->deprecationLogger; $server['profiler'] = $this->profiler; $server['trxProfiler'] = $this->trxProfiler; // Use the same agent and PHP mode for all DB handles @@ -1131,7 +1140,7 @@ class LoadBalancer implements ILoadBalancer { $context ); - // If all servers were busy, mLastError will contain something sensible + // If all servers were busy, "lastError" will contain something sensible throw new DBConnectionError( null, $this->lastError ); } } @@ -1164,6 +1173,14 @@ class LoadBalancer implements ILoadBalancer { return ( $name != '' ) ? $name : 'localhost'; } + public function getServerInfo( $i ) { + if ( isset( $this->servers[$i] ) ) { + return $this->servers[$i]; + } else { + return false; + } + } + public function getServerType( $i ) { return isset( $this->servers[$i]['type'] ) ? $this->servers[$i]['type'] : 'unknown'; } @@ -1212,7 +1229,7 @@ class LoadBalancer implements ILoadBalancer { } public function closeConnection( IDatabase $conn ) { - $serverIndex = $conn->getLBInfo( 'serverIndex' ); // second index level of mConns + $serverIndex = $conn->getLBInfo( 'serverIndex' ); foreach ( $this->conns as $type => $connsByServer ) { if ( !isset( $connsByServer[$serverIndex] ) ) { continue;