X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2Fdb%2FDatabase.php;h=e07836b225b4d36db16de98f3ff2a0ff69c8af48;hb=d1cb2084b71fa31012697575d8528a02e10a92bc;hp=186a87bb53deeab6df60d30e8f22a2dde51ea5b0;hpb=45ae11e7488c58e31cc4fb11c17654b7f97a7314;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/db/Database.php b/includes/db/Database.php index 186a87bb53..e07836b225 100644 --- a/includes/db/Database.php +++ b/includes/db/Database.php @@ -32,24 +32,35 @@ abstract class DatabaseBase implements IDatabase { /** Number of times to re-try an operation in case of deadlock */ const DEADLOCK_TRIES = 4; - /** Minimum time to wait before retry, in microseconds */ const DEADLOCK_DELAY_MIN = 500000; - /** Maximum time to wait before retry */ const DEADLOCK_DELAY_MAX = 1500000; + /** How long before it is worth doing a dummy query to test the connection */ + const PING_TTL = 1.0; + + /** @var string SQL query */ protected $mLastQuery = ''; + /** @var bool */ protected $mDoneWrites = false; + /** @var string|bool */ protected $mPHPError = false; - - protected $mServer, $mUser, $mPassword, $mDBname; + /** @var string */ + protected $mServer; + /** @var string */ + protected $mUser; + /** @var string */ + protected $mPassword; + /** @var string */ + protected $mDBname; /** @var BagOStuff APC cache */ protected $srvCache; /** @var resource Database connection */ protected $mConn = null; + /** @var bool */ protected $mOpened = false; /** @var array[] List of (callable, method name) */ @@ -61,20 +72,27 @@ abstract class DatabaseBase implements IDatabase { /** @var bool Whether to suppress triggering of post-commit callbacks */ protected $suppressPostCommitCallbacks = false; + /** @var string */ protected $mTablePrefix; + /** @var string */ protected $mSchema; + /** @var integer */ protected $mFlags; + /** @var bool */ protected $mForeign; + /** @var array */ protected $mLBInfo = []; + /** @var bool|null */ protected $mDefaultBigSelects = null; + /** @var array|bool */ protected $mSchemaVars = false; /** @var array */ protected $mSessionVars = []; - + /** @var array|null */ protected $preparedArgs; - + /** @var string|bool|null Stashed value of html_errors INI setting */ protected $htmlErrors; - + /** @var string */ protected $delimiter = ';'; /** @@ -177,6 +195,12 @@ abstract class DatabaseBase implements IDatabase { */ protected $allViews = null; + /** @var float UNIX timestamp */ + protected $lastPing = 0.0; + + /** @var int[] Prior mFlags values */ + private $priorFlags = []; + /** @var TransactionProfiler */ protected $trxProfiler; @@ -409,14 +433,33 @@ abstract class DatabaseBase implements IDatabase { return $this->mOpened; } - public function setFlag( $flag ) { + public function setFlag( $flag, $remember = self::REMEMBER_NOTHING ) { + if ( $remember === self::REMEMBER_PRIOR ) { + array_push( $this->priorFlags, $this->mFlags ); + } $this->mFlags |= $flag; } - public function clearFlag( $flag ) { + public function clearFlag( $flag, $remember = self::REMEMBER_NOTHING ) { + if ( $remember === self::REMEMBER_PRIOR ) { + array_push( $this->priorFlags, $this->mFlags ); + } $this->mFlags &= ~$flag; } + public function restoreFlags( $state = self::RESTORE_PRIOR ) { + if ( !$this->priorFlags ) { + return; + } + + if ( $state === self::RESTORE_INITIAL ) { + $this->mFlags = reset( $this->priorFlags ); + $this->priorFlags = []; + } else { + $this->mFlags = array_pop( $this->priorFlags ); + } + } + public function getFlag( $flag ) { return !!( $this->mFlags & $flag ); } @@ -786,8 +829,8 @@ abstract class DatabaseBase implements IDatabase { $priorWritesPending = $this->writesOrCallbacksPending(); $this->mLastQuery = $sql; - $isWriteQuery = $this->isWriteQuery( $sql ); - if ( $isWriteQuery ) { + $isWrite = $this->isWriteQuery( $sql ); + if ( $isWrite ) { $reason = $this->getReadOnlyReason(); if ( $reason !== false ) { throw new DBReadOnlyError( $this, "Database is read-only: $reason" ); @@ -820,31 +863,12 @@ abstract class DatabaseBase implements IDatabase { } # Keep track of whether the transaction has write queries pending - if ( $this->mTrxLevel && !$this->mTrxDoneWrites && $isWriteQuery ) { + if ( $this->mTrxLevel && !$this->mTrxDoneWrites && $isWrite ) { $this->mTrxDoneWrites = true; $this->getTransactionProfiler()->transactionWritingIn( $this->mServer, $this->mDBname, $this->mTrxShortId ); } - $isMaster = !is_null( $this->getLBInfo( 'master' ) ); - # generalizeSQL will probably cut down the query to reasonable - # logging size most of the time. The substr is really just a sanity check. - if ( $isMaster ) { - $queryProf = 'query-m: ' . substr( DatabaseBase::generalizeSQL( $sql ), 0, 255 ); - $totalProf = 'DatabaseBase::query-master'; - } else { - $queryProf = 'query: ' . substr( DatabaseBase::generalizeSQL( $sql ), 0, 255 ); - $totalProf = 'DatabaseBase::query'; - } - # Include query transaction state - $queryProf .= $this->mTrxShortId ? " [TRX#{$this->mTrxShortId}]" : ""; - - $profiler = Profiler::instance(); - if ( !$profiler instanceof ProfilerStub ) { - $totalProfSection = $profiler->scopedProfileIn( $totalProf ); - $queryProfSection = $profiler->scopedProfileIn( $queryProf ); - } - if ( $this->debug() ) { wfDebugLog( 'queries', sprintf( "%s: %s", $this->mDBname, $commentedSql ) ); } @@ -852,15 +876,8 @@ abstract class DatabaseBase implements IDatabase { # Avoid fatals if close() was called $this->assertOpen(); - # Do the query and handle errors - $startTime = microtime( true ); - $ret = $this->doQuery( $commentedSql ); - $queryRuntime = microtime( true ) - $startTime; - # Log the query time and feed it into the DB trx profiler - $this->getTransactionProfiler()->recordQueryCompletion( - $queryProf, $startTime, $isWriteQuery, $this->affectedRows() ); - - MWDebug::query( $sql, $fname, $isMaster, $queryRuntime ); + # Send the query to the server + $ret = $this->doProfiledQuery( $sql, $commentedSql, $isWrite, $fname ); # Try reconnecting if the connection was lost if ( false === $ret && $this->wasErrorReissuable() ) { @@ -881,12 +898,7 @@ abstract class DatabaseBase implements IDatabase { $this->reportQueryError( $lastError, $lastErrno, $sql, $fname ); } else { # Should be safe to silently retry the query - $startTime = microtime( true ); - $ret = $this->doQuery( $commentedSql ); - $queryRuntime = microtime( true ) - $startTime; - # Log the query time and feed it into the DB trx profiler - $this->getTransactionProfiler()->recordQueryCompletion( - $queryProf, $startTime, $isWriteQuery, $this->affectedRows() ); + $ret = $this->doProfiledQuery( $sql, $commentedSql, $isWrite, $fname ); } } else { wfDebug( "Failed\n" ); @@ -911,16 +923,47 @@ abstract class DatabaseBase implements IDatabase { $res = $this->resultObject( $ret ); - // Destroy profile sections in the opposite order to their creation - ScopedCallback::consume( $queryProfSection ); - ScopedCallback::consume( $totalProfSection ); + return $res; + } - if ( $isWriteQuery && $this->mTrxLevel ) { - $this->mTrxWriteDuration += $queryRuntime; - $this->mTrxWriteCallers[] = $fname; + private function doProfiledQuery( $sql, $commentedSql, $isWrite, $fname ) { + $isMaster = !is_null( $this->getLBInfo( 'master' ) ); + # generalizeSQL() will probably cut down the query to reasonable + # logging size most of the time. The substr is really just a sanity check. + if ( $isMaster ) { + $queryProf = 'query-m: ' . substr( DatabaseBase::generalizeSQL( $sql ), 0, 255 ); + } else { + $queryProf = 'query: ' . substr( DatabaseBase::generalizeSQL( $sql ), 0, 255 ); } - return $res; + # Include query transaction state + $queryProf .= $this->mTrxShortId ? " [TRX#{$this->mTrxShortId}]" : ""; + + $profiler = Profiler::instance(); + if ( !( $profiler instanceof ProfilerStub ) ) { + $queryProfSection = $profiler->scopedProfileIn( $queryProf ); + } + + $startTime = microtime( true ); + $ret = $this->doQuery( $commentedSql ); + $queryRuntime = microtime( true ) - $startTime; + + unset( $queryProfSection ); // profile out (if set) + + if ( $ret !== false ) { + $this->lastPing = $startTime; + if ( $isWrite && $this->mTrxLevel ) { + $this->mTrxWriteDuration += $queryRuntime; + $this->mTrxWriteCallers[] = $fname; + } + } + + $this->getTransactionProfiler()->recordQueryCompletion( + $queryProf, $startTime, $isWrite, $this->affectedRows() + ); + MWDebug::query( $sql, $fname, $isMaster, $queryRuntime ); + + return $ret; } private function canRecoverFromDisconnect( $sql, $priorWritesPending ) { @@ -2926,21 +2969,35 @@ abstract class DatabaseBase implements IDatabase { } public function ping() { - try { - // This will reconnect if possible, or error out if not - $this->query( "SELECT 1 AS ping", __METHOD__ ); + if ( $this->isOpen() && ( microtime( true ) - $this->lastPing ) < self::PING_TTL ) { return true; - } catch ( DBError $e ) { - return false; } + + $ignoreErrors = true; + $this->clearFlag( DBO_TRX, self::REMEMBER_PRIOR ); + // This will reconnect if possible or return false if not + $ok = (bool)$this->query( "SELECT 1 AS ping", __METHOD__, $ignoreErrors ); + $this->restoreFlags( self::RESTORE_PRIOR ); + + return $ok; } /** * @return bool */ protected function reconnect() { - # Stub. Not essential to override. - return true; + $this->closeConnection(); + $this->mOpened = false; + $this->mConn = false; + try { + $this->open( $this->mServer, $this->mUser, $this->mPassword, $this->mDBname ); + $this->lastPing = microtime( true ); + $ok = true; + } catch ( DBConnectionError $e ) { + $ok = false; + } + + return $ok; } public function getSessionLagStatus() {