26use Psr\Log\LoggerInterface;
27use Wikimedia\ScopedCallback;
92 [
'replLogger',
'connLogger',
'queryLogger',
'perfLogger' ];
95 $this->localDomain = isset( $conf[
'localDomain'] )
99 if ( isset( $conf[
'readOnlyReason'] ) && is_string( $conf[
'readOnlyReason'] ) ) {
100 $this->readOnlyReason = $conf[
'readOnlyReason'];
103 $this->srvCache = isset( $conf[
'srvCache'] ) ? $conf[
'srvCache'] :
new EmptyBagOStuff();
104 $this->memStash = isset( $conf[
'memStash'] ) ? $conf[
'memStash'] :
new EmptyBagOStuff();
105 $this->wanCache = isset( $conf[
'wanCache'] )
107 : WANObjectCache::newEmpty();
109 foreach ( self::$loggerFields as $key ) {
110 $this->$key = isset( $conf[$key] ) ? $conf[$key] : new \Psr\Log\NullLogger();
112 $this->errorLogger = isset( $conf[
'errorLogger'] )
113 ? $conf[
'errorLogger']
114 :
function ( Exception
$e ) {
115 trigger_error( get_class(
$e ) .
': ' .
$e->getMessage(), E_USER_WARNING );
117 $this->deprecationLogger = isset( $conf[
'deprecationLogger'] )
118 ? $conf[
'deprecationLogger']
119 :
function ( $msg ) {
120 trigger_error( $msg, E_USER_DEPRECATED );
123 $this->profiler = isset( $conf[
'profiler'] ) ? $conf[
'profiler'] :
null;
124 $this->trxProfiler = isset( $conf[
'trxProfiler'] )
125 ? $conf[
'trxProfiler']
128 $this->requestInfo = [
129 'IPAddress' => isset( $_SERVER[
'REMOTE_ADDR' ] ) ? $_SERVER[
'REMOTE_ADDR' ] :
'',
130 'UserAgent' => isset( $_SERVER[
'HTTP_USER_AGENT'] ) ? $_SERVER[
'HTTP_USER_AGENT'] :
'',
131 'ChronologyProtection' =>
'true',
132 'ChronologyPositionIndex' => isset( $_GET[
'cpPosIndex'] ) ? $_GET[
'cpPosIndex'] : null
135 $this->cliMode = isset( $conf[
'cliMode'] )
137 : ( PHP_SAPI ===
'cli' || PHP_SAPI ===
'phpdbg' );
138 $this->hostname = isset( $conf[
'hostname'] ) ? $conf[
'hostname'] : gethostname();
139 $this->agent = isset( $conf[
'agent'] ) ? $conf[
'agent'] :
'';
141 $this->ticket = mt_rand();
145 $this->
shutdown( self::SHUTDOWN_NO_CHRONPROT );
150 $mode = self::SHUTDOWN_CHRONPROT_SYNC, callable $workCallback =
null, &$cpIndex =
null
153 if ( $mode === self::SHUTDOWN_CHRONPROT_SYNC ) {
155 } elseif ( $mode === self::SHUTDOWN_CHRONPROT_ASYNC ) {
199 call_user_func_array( [ $loadBalancer, $methodName ],
$args );
201 [ $methodName,
$args ]
215 if ( $this->trxRoundId !==
false ) {
218 "$fname: transaction round '{$this->trxRoundId}' already started."
221 $this->trxRoundId =
$fname;
227 if ( $this->trxRoundId !==
false && $this->trxRoundId !==
$fname ) {
230 "$fname: transaction round '{$this->trxRoundId}' still running."
237 $this->trxRoundId =
false;
254 if (
$e instanceof Exception ) {
260 $this->trxRoundId =
false;
270 return ( $this->trxRoundId !==
false );
282 $callersByDB[$masterName] = $callers;
286 if ( count( $callersByDB ) >= 2 ) {
287 $dbs = implode(
', ', array_keys( $callersByDB ) );
288 $msg =
"Multi-DB transaction [{$dbs}]:\n";
289 foreach ( $callersByDB as $db => $callers ) {
290 $msg .=
"$db: " . implode(
'; ', $callers ) .
"\n";
292 $this->queryLogger->info( $msg );
326 'timeout' => $this->cliMode ? 60 : 10,
327 'ifWritesSince' => null
330 if ( $opts[
'domain'] ===
false && isset( $opts[
'wiki'] ) ) {
331 $opts[
'domain'] = $opts[
'wiki'];
337 if ( $opts[
'cluster'] !==
false ) {
339 } elseif ( $opts[
'domain'] !==
false ) {
340 $lbs[] = $this->
getMainLB( $opts[
'domain'] );
353 $masterPositions = array_fill( 0, count( $lbs ),
false );
354 foreach ( $lbs as $i => $lb ) {
355 if ( $lb->getServerCount() <= 1 ) {
359 } elseif ( $opts[
'ifWritesSince']
360 && $lb->lastMasterChangeTimestamp() < $opts[
'ifWritesSince']
364 $masterPositions[$i] = $lb->getMasterPos();
369 foreach ( $this->replicationWaitCallbacks as $callback ) {
374 foreach ( $lbs as $i => $lb ) {
375 if ( $masterPositions[$i] ) {
377 if ( !$lb->waitForAll( $masterPositions[$i], $opts[
'timeout'] ) ) {
378 $failed[] = $lb->getServerName( $lb->getWriterIndex() );
386 "Could not wait for replica DBs to catch up to " .
387 implode(
', ', $failed )
394 $this->replicationWaitCallbacks[
$name] = $callback;
396 unset( $this->replicationWaitCallbacks[$name] );
402 $this->queryLogger->error( __METHOD__ .
": $fname does not have outer scope.\n" .
403 (
new RuntimeException() )->getTraceAsString() );
412 if (
$ticket !== $this->ticket ) {
413 $this->perfLogger->error( __METHOD__ .
": $fname does not have outer scope.\n" .
414 (
new RuntimeException() )->getTraceAsString() );
421 if ( $this->trxRoundId !==
false &&
$fname !== $this->trxRoundId ) {
422 $this->queryLogger->info(
"$fname: committing on behalf of {$this->trxRoundId}." );
432 if ( $fnameEffective !==
$fname ) {
449 if ( $this->chronProt ) {
456 'ip' => $this->requestInfo[
'IPAddress'],
457 'agent' => $this->requestInfo[
'UserAgent'],
459 $this->requestInfo[
'ChronologyPositionIndex']
461 $this->chronProt->setLogger( $this->replLogger );
463 if ( $this->cliMode ) {
464 $this->chronProt->setEnabled(
false );
465 } elseif ( $this->requestInfo[
'ChronologyProtection'] ===
'false' ) {
468 $this->chronProt->setWaitEnabled(
false );
471 $this->chronProt->setEnabled(
false );
472 $this->replLogger->info(
'Cannot use ChronologyProtector with EmptyBagOStuff.' );
475 $this->replLogger->debug( __METHOD__ .
': using request info ' .
476 json_encode( $this->requestInfo, JSON_PRETTY_PRINT ) );
494 $cp->shutdownLB( $lb );
498 $unsavedPositions = $cp->shutdown( $workCallback, $mode, $cpIndex );
499 if ( $unsavedPositions && $workCallback ) {
509 if ( isset( $unsavedPositions[$masterName] ) ) {
510 $lb->
waitForAll( $unsavedPositions[$masterName] );
547 if ( $this->trxRoundId !==
false ) {
556 $this->tableAliases = $aliases;
560 $this->indexAliases = $aliases;
565 $this->localDomain->getDatabase(),
589 if ( !$usedCluster ) {
593 return strpos( $url,
'?' ) ===
false ?
"$url?cpPosIndex=$index" :
"$url&cpPosIndex=$index";
597 if ( $this->chronProt ) {
598 throw new LogicException(
'ChronologyProtector already initialized.' );
611 if ( PHP_SAPI !=
'cli' ) {
612 $old = ignore_user_abort(
true );
613 return new ScopedCallback(
function () use ( $old ) {
614 ignore_user_abort( $old );
626class_alias( LBFactory::class,
'LBFactory' );
if(defined( 'MW_SETUP_CALLBACK')) $fname
Customization point after all loading (constants, functions, classes, DefaultSettings,...
interface is intended to be more or less compatible with the PHP memcached client.
A BagOStuff object with no objects in it.
Multi-datacenter aware caching interface.
Class to handle database/prefix specification for IDatabase domains.
static newFromId( $domain)
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses & $ret
Allows to change the fields on the form that will be generated $name
returning false will NOT prevent logging $e