26use Psr\Log\LoggerInterface;
27use Wikimedia\ScopedCallback;
84 [
'replLogger',
'connLogger',
'queryLogger',
'perfLogger' ];
87 $this->localDomain = isset( $conf[
'localDomain'] )
91 if ( isset( $conf[
'readOnlyReason'] ) && is_string( $conf[
'readOnlyReason'] ) ) {
92 $this->readOnlyReason = $conf[
'readOnlyReason'];
95 $this->srvCache = isset( $conf[
'srvCache'] ) ? $conf[
'srvCache'] :
new EmptyBagOStuff();
96 $this->memStash = isset( $conf[
'memStash'] ) ? $conf[
'memStash'] :
new EmptyBagOStuff();
97 $this->wanCache = isset( $conf[
'wanCache'] )
99 : WANObjectCache::newEmpty();
101 foreach ( self::$loggerFields as $key ) {
102 $this->$key = isset( $conf[$key] ) ? $conf[$key] : new \Psr\Log\NullLogger();
104 $this->errorLogger = isset( $conf[
'errorLogger'] )
105 ? $conf[
'errorLogger']
106 :
function ( Exception
$e ) {
107 trigger_error( E_USER_WARNING, get_class(
$e ) .
': ' .
$e->getMessage() );
110 $this->profiler = isset( $conf[
'profiler'] ) ? $conf[
'profiler'] :
null;
111 $this->trxProfiler = isset( $conf[
'trxProfiler'] )
112 ? $conf[
'trxProfiler']
115 $this->requestInfo = [
116 'IPAddress' => isset( $_SERVER[
'REMOTE_ADDR' ] ) ? $_SERVER[
'REMOTE_ADDR' ] :
'',
117 'UserAgent' => isset( $_SERVER[
'HTTP_USER_AGENT'] ) ? $_SERVER[
'HTTP_USER_AGENT'] :
'',
118 'ChronologyProtection' =>
'true'
121 $this->cliMode = isset( $conf[
'cliMode'] ) ? $conf[
'cliMode'] : PHP_SAPI ===
'cli';
122 $this->hostname = isset( $conf[
'hostname'] ) ? $conf[
'hostname'] : gethostname();
123 $this->agent = isset( $conf[
'agent'] ) ? $conf[
'agent'] :
'';
125 $this->ticket = mt_rand();
129 $this->
shutdown( self::SHUTDOWN_NO_CHRONPROT );
134 $mode = self::SHUTDOWN_CHRONPROT_SYNC, callable $workCallback =
null
137 if ( $mode === self::SHUTDOWN_CHRONPROT_SYNC ) {
139 } elseif ( $mode === self::SHUTDOWN_CHRONPROT_ASYNC ) {
183 call_user_func_array( [ $loadBalancer, $methodName ],
$args );
185 [ $methodName,
$args ]
199 if ( $this->trxRoundId !==
false ) {
202 "$fname: transaction round '{$this->trxRoundId}' already started."
205 $this->trxRoundId =
$fname;
211 if ( $this->trxRoundId !==
false && $this->trxRoundId !==
$fname ) {
214 "$fname: transaction round '{$this->trxRoundId}' still running."
221 $this->trxRoundId =
false;
238 if (
$e instanceof Exception ) {
244 $this->trxRoundId =
false;
254 return ( $this->trxRoundId !==
false );
266 $callersByDB[$masterName] = $callers;
270 if ( count( $callersByDB ) >= 2 ) {
271 $dbs = implode(
', ', array_keys( $callersByDB ) );
272 $msg =
"Multi-DB transaction [{$dbs}]:\n";
273 foreach ( $callersByDB as $db => $callers ) {
274 $msg .=
"$db: " . implode(
'; ', $callers ) .
"\n";
276 $this->queryLogger->info( $msg );
311 'ifWritesSince' => null
314 if ( $opts[
'domain'] ===
false && isset( $opts[
'wiki'] ) ) {
315 $opts[
'domain'] = $opts[
'wiki'];
321 if ( $opts[
'cluster'] !==
false ) {
323 } elseif ( $opts[
'domain'] !==
false ) {
324 $lbs[] = $this->
getMainLB( $opts[
'domain'] );
337 $masterPositions = array_fill( 0, count( $lbs ),
false );
338 foreach ( $lbs as $i => $lb ) {
339 if ( $lb->getServerCount() <= 1 ) {
343 } elseif ( $opts[
'ifWritesSince']
344 && $lb->lastMasterChangeTimestamp() < $opts[
'ifWritesSince']
348 $masterPositions[$i] = $lb->getMasterPos();
353 foreach ( $this->replicationWaitCallbacks as $callback ) {
358 foreach ( $lbs as $i => $lb ) {
359 if ( $masterPositions[$i] ) {
361 if ( !$lb->waitForAll( $masterPositions[$i], $opts[
'timeout'] ) ) {
362 $failed[] = $lb->getServerName( $lb->getWriterIndex() );
370 "Could not wait for replica DBs to catch up to " .
371 implode(
', ', $failed )
378 $this->replicationWaitCallbacks[
$name] = $callback;
380 unset( $this->replicationWaitCallbacks[$name] );
386 $this->queryLogger->error( __METHOD__ .
": $fname does not have outer scope.\n" .
387 (
new RuntimeException() )->getTraceAsString() );
396 if (
$ticket !== $this->ticket ) {
397 $this->perfLogger->error( __METHOD__ .
": $fname does not have outer scope.\n" .
398 (
new RuntimeException() )->getTraceAsString() );
405 if ( $this->trxRoundId !==
false &&
$fname !== $this->trxRoundId ) {
406 $this->queryLogger->info(
"$fname: committing on behalf of {$this->trxRoundId}." );
416 if ( $fnameEffective !==
$fname ) {
433 if ( $this->chronProt ) {
440 'ip' => $this->requestInfo[
'IPAddress'],
441 'agent' => $this->requestInfo[
'UserAgent'],
443 isset( $_GET[
'cpPosTime'] ) ? $_GET[
'cpPosTime'] : null
445 $this->chronProt->
setLogger( $this->replLogger );
447 if ( $this->cliMode ) {
448 $this->chronProt->setEnabled(
false );
449 } elseif ( $this->requestInfo[
'ChronologyProtection'] ===
'false' ) {
452 $this->chronProt->setWaitEnabled(
false );
455 $this->replLogger->debug( __METHOD__ .
': using request info ' .
456 json_encode( $this->requestInfo, JSON_PRETTY_PRINT ) );
473 $cp->shutdownLB( $lb );
477 $unsavedPositions = $cp->shutdown( $workCallback, $mode );
478 if ( $unsavedPositions && $workCallback ) {
488 if ( isset( $unsavedPositions[$masterName] ) ) {
489 $lb->
waitForAll( $unsavedPositions[$masterName] );
521 if ( $this->trxRoundId !==
false ) {
528 $this->localDomain->getDatabase(),
552 if ( !$usedCluster ) {
556 return strpos( $url,
'?' ) ===
false ?
"$url?cpPosTime=$time" :
"$url&cpPosTime=$time";
570 if ( PHP_SAPI !=
'cli' ) {
571 $old = ignore_user_abort(
true );
572 return new ScopedCallback(
function () use ( $old ) {
573 ignore_user_abort( $old );
585class_alias( LBFactory::class,
'LBFactory' );
if(!defined( 'MEDIAWIKI')) $fname
This file is not a valid entry point, perform no further processing unless MEDIAWIKI is defined.
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)
see documentation in includes Linker php for Linker::makeImageLink & $time
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