26use Psr\Log\LoggerInterface;
27use Wikimedia\ScopedCallback;
106 [
'replLogger',
'connLogger',
'queryLogger',
'perfLogger' ];
109 $this->localDomain = isset( $conf[
'localDomain'] )
113 if ( isset( $conf[
'readOnlyReason'] ) && is_string( $conf[
'readOnlyReason'] ) ) {
114 $this->readOnlyReason = $conf[
'readOnlyReason'];
119 $this->wanCache = $conf[
'wanCache'] ?? WANObjectCache::newEmpty();
121 foreach ( self::$loggerFields as $key ) {
122 $this->$key = $conf[$key] ?? new \Psr\Log\NullLogger();
124 $this->errorLogger = $conf[
'errorLogger'] ??
function ( Exception
$e ) {
125 trigger_error( get_class(
$e ) .
': ' .
$e->getMessage(), E_USER_WARNING );
127 $this->deprecationLogger = $conf[
'deprecationLogger'] ??
function ( $msg ) {
128 trigger_error( $msg, E_USER_DEPRECATED );
131 $this->profiler = $conf[
'profiler'] ??
null;
134 $this->requestInfo = [
135 'IPAddress' => $_SERVER[
'REMOTE_ADDR' ] ??
'',
136 'UserAgent' => $_SERVER[
'HTTP_USER_AGENT'] ??
'',
138 'ChronologyProtection' =>
null,
139 'ChronologyClientId' =>
null,
140 'ChronologyPositionIndex' =>
null
143 $this->cliMode = $conf[
'cliMode'] ?? ( PHP_SAPI ===
'cli' || PHP_SAPI ===
'phpdbg' );
144 $this->hostname = $conf[
'hostname'] ?? gethostname();
145 $this->agent = $conf[
'agent'] ??
'';
146 $this->defaultGroup = $conf[
'defaultGroup'] ??
null;
148 $this->ticket = mt_rand();
152 $this->
shutdown( self::SHUTDOWN_NO_CHRONPROT );
157 return $this->localDomain->getId();
165 $mode = self::SHUTDOWN_CHRONPROT_SYNC,
166 callable $workCallback =
null,
171 if ( $mode === self::SHUTDOWN_CHRONPROT_SYNC ) {
173 } elseif ( $mode === self::SHUTDOWN_CHRONPROT_ASYNC ) {
219 $loadBalancer->$methodName( ...
$args );
221 [ $methodName,
$args ]
238 if ( $this->trxRoundId !==
false ) {
241 "$fname: transaction round '{$this->trxRoundId}' already started."
244 $this->trxRoundId =
$fname;
253 if ( $this->trxRoundId !==
false && $this->trxRoundId !==
$fname ) {
256 "$fname: transaction round '{$this->trxRoundId}' still running."
267 }
while ( $count > 0 );
268 $this->trxRoundId =
false;
280 if (
$e instanceof Exception ) {
287 $this->trxRoundId =
false;
318 return ( $this->trxRoundId !==
false );
322 return ( $this->trxRoundStage === self::ROUND_CURSORY );
334 $callersByDB[$masterName] = $callers;
338 if ( count( $callersByDB ) >= 2 ) {
339 $dbs = implode(
', ', array_keys( $callersByDB ) );
340 $msg =
"Multi-DB transaction [{$dbs}]:\n";
341 foreach ( $callersByDB as $db => $callers ) {
342 $msg .=
"$db: " . implode(
'; ', $callers ) .
"\n";
344 $this->queryLogger->info( $msg );
378 'timeout' => $this->cliMode ? 60 : 1,
379 'ifWritesSince' => null
382 if ( $opts[
'domain'] ===
false && isset( $opts[
'wiki'] ) ) {
383 $opts[
'domain'] = $opts[
'wiki'];
389 if ( $opts[
'cluster'] !==
false ) {
391 } elseif ( $opts[
'domain'] !==
false ) {
392 $lbs[] = $this->
getMainLB( $opts[
'domain'] );
405 $masterPositions = array_fill( 0, count( $lbs ),
false );
406 foreach ( $lbs as $i => $lb ) {
407 if ( $lb->getServerCount() <= 1 ) {
411 } elseif ( $opts[
'ifWritesSince']
412 && $lb->lastMasterChangeTimestamp() < $opts[
'ifWritesSince']
416 $masterPositions[$i] = $lb->getMasterPos();
421 foreach ( $this->replicationWaitCallbacks as $callback ) {
426 foreach ( $lbs as $i => $lb ) {
427 if ( $masterPositions[$i] ) {
429 if ( !$lb->waitForAll( $masterPositions[$i], $opts[
'timeout'] ) ) {
430 $failed[] = $lb->getServerName( $lb->getWriterIndex() );
440 $this->replicationWaitCallbacks[
$name] = $callback;
442 unset( $this->replicationWaitCallbacks[$name] );
448 $this->queryLogger->error( __METHOD__ .
": $fname does not have outer scope.\n" .
449 (
new RuntimeException() )->getTraceAsString() );
458 if (
$ticket !== $this->ticket ) {
459 $this->perfLogger->error( __METHOD__ .
": $fname does not have outer scope.\n" .
460 (
new RuntimeException() )->getTraceAsString() );
467 if ( $this->trxRoundId !==
false &&
$fname !== $this->trxRoundId ) {
468 $this->queryLogger->info(
"$fname: committing on behalf of {$this->trxRoundId}." );
478 if ( $fnameEffective !==
$fname ) {
481 return $waitSucceeded;
496 if ( $this->chronProt ) {
503 'ip' => $this->requestInfo[
'IPAddress'],
504 'agent' => $this->requestInfo[
'UserAgent'],
505 'clientId' => $this->requestInfo[
'ChronologyClientId']
507 $this->requestInfo[
'ChronologyPositionIndex']
509 $this->chronProt->setLogger( $this->replLogger );
511 if ( $this->cliMode ) {
512 $this->chronProt->setEnabled(
false );
513 } elseif ( $this->requestInfo[
'ChronologyProtection'] ===
'false' ) {
516 $this->chronProt->setWaitEnabled(
false );
519 $this->chronProt->setEnabled(
false );
520 $this->replLogger->info(
'Cannot use ChronologyProtector with EmptyBagOStuff.' );
523 $this->replLogger->debug( __METHOD__ .
': using request info ' .
524 json_encode( $this->requestInfo, JSON_PRETTY_PRINT ) );
542 $cp->shutdownLB( $lb );
546 $unsavedPositions = $cp->shutdown( $workCallback, $mode, $cpIndex );
547 if ( $unsavedPositions && $workCallback ) {
557 if ( isset( $unsavedPositions[$masterName] ) ) {
558 $lb->
waitForAll( $unsavedPositions[$masterName] );
568 if ( $this->trxRoundStage === self::ROUND_COMMIT_CALLBACKS ) {
569 $initStage = ILoadBalancer::STAGE_POSTCOMMIT_CALLBACKS;
570 } elseif ( $this->trxRoundStage === self::ROUND_ROLLBACK_CALLBACKS ) {
571 $initStage = ILoadBalancer::STAGE_POSTROLLBACK_CALLBACKS;
597 'roundStage' => $initStage
605 if ( $this->trxRoundId !==
false ) {
614 $this->tableAliases = $aliases;
618 $this->indexAliases = $aliases;
623 $this->localDomain->getDatabase(),
657 if ( !$usedCluster ) {
661 return strpos( $url,
'?' ) ===
false ?
"$url?cpPosIndex=$index" :
"$url&cpPosIndex=$index";
672 return "$index@$time#$clientId";
682 static $placeholder = [
'index' =>
null,
'clientId' => null ];
684 if ( !preg_match(
'/^(\d+)@(\d+)#([0-9a-f]{32})$/',
$value, $m ) ) {
691 } elseif ( isset( $m[2] ) && $m[2] !==
'' && (
int)$m[2] < $minTimestamp ) {
695 $clientId = ( isset( $m[3] ) && $m[3] !==
'' ) ? $m[3] :
null;
697 return [
'index' => $index,
'clientId' => $clientId ];
701 if ( $this->chronProt ) {
702 throw new LogicException(
'ChronologyProtector already initialized.' );
712 if ( $this->trxRoundStage !== $stage ) {
715 "Transaction round stage must be '$stage' (not '{$this->trxRoundStage}')"
727 if ( PHP_SAPI !=
'cli' ) {
728 $old = ignore_user_abort(
true );
729 return new ScopedCallback(
function () use ( $old ) {
730 ignore_user_abort( $old );
if(defined( 'MW_SETUP_CALLBACK')) $fname
Customization point after all loading (constants, functions, classes, DefaultSettings,...
Class representing a cache/ephemeral data store.
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
This code would result in ircNotify being run twice when an article is and once for brion Hooks can return three possible true was required This is the default since MediaWiki *some string
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
The wiki should then use memcached to cache various data To use multiple just add more items to the array To increase the weight of a make its entry a array("192.168.0.1:11211", 2))