MediaWiki REL1_32
LBFactory.php
Go to the documentation of this file.
1<?php
24namespace Wikimedia\Rdbms;
25
26use Psr\Log\LoggerInterface;
27use Wikimedia\ScopedCallback;
31use Exception;
32use RuntimeException;
33use LogicException;
34
39abstract class LBFactory implements ILBFactory {
41 private $chronProt;
43 private $profiler;
45 private $trxProfiler;
47 private $replLogger;
49 private $connLogger;
51 private $queryLogger;
53 private $perfLogger;
55 private $errorLogger;
58
60 protected $srvCache;
62 protected $memStash;
64 protected $wanCache;
65
67 protected $localDomain;
68
70 private $hostname;
72 private $requestInfo;
74 private $cliMode;
76 private $agent;
77
79 private $tableAliases = [];
81 private $indexAliases = [];
84
86 private $ticket;
88 private $trxRoundId = false;
91
93 protected $readOnlyReason = false;
94
96 private $defaultGroup = null;
97
98 const ROUND_CURSORY = 'cursory';
99 const ROUND_BEGINNING = 'within-begin';
100 const ROUND_COMMITTING = 'within-commit';
101 const ROUND_ROLLING_BACK = 'within-rollback';
102 const ROUND_COMMIT_CALLBACKS = 'within-commit-callbacks';
103 const ROUND_ROLLBACK_CALLBACKS = 'within-rollback-callbacks';
104
105 private static $loggerFields =
106 [ 'replLogger', 'connLogger', 'queryLogger', 'perfLogger' ];
107
108 public function __construct( array $conf ) {
109 $this->localDomain = isset( $conf['localDomain'] )
110 ? DatabaseDomain::newFromId( $conf['localDomain'] )
112
113 if ( isset( $conf['readOnlyReason'] ) && is_string( $conf['readOnlyReason'] ) ) {
114 $this->readOnlyReason = $conf['readOnlyReason'];
115 }
116
117 $this->srvCache = $conf['srvCache'] ?? new EmptyBagOStuff();
118 $this->memStash = $conf['memStash'] ?? new EmptyBagOStuff();
119 $this->wanCache = $conf['wanCache'] ?? WANObjectCache::newEmpty();
120
121 foreach ( self::$loggerFields as $key ) {
122 $this->$key = $conf[$key] ?? new \Psr\Log\NullLogger();
123 }
124 $this->errorLogger = $conf['errorLogger'] ?? function ( Exception $e ) {
125 trigger_error( get_class( $e ) . ': ' . $e->getMessage(), E_USER_WARNING );
126 };
127 $this->deprecationLogger = $conf['deprecationLogger'] ?? function ( $msg ) {
128 trigger_error( $msg, E_USER_DEPRECATED );
129 };
130
131 $this->profiler = $conf['profiler'] ?? null;
132 $this->trxProfiler = $conf['trxProfiler'] ?? new TransactionProfiler();
133
134 $this->requestInfo = [
135 'IPAddress' => $_SERVER[ 'REMOTE_ADDR' ] ?? '',
136 'UserAgent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
137 // Headers application can inject via LBFactory::setRequestInfo()
138 'ChronologyProtection' => null,
139 'ChronologyClientId' => null, // prior $cpClientId value from LBFactory::shutdown()
140 'ChronologyPositionIndex' => null // prior $cpIndex value from LBFactory::shutdown()
141 ];
142
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;
147
148 $this->ticket = mt_rand();
149 }
150
151 public function destroy() {
152 $this->shutdown( self::SHUTDOWN_NO_CHRONPROT );
153 $this->forEachLBCallMethod( 'disable' );
154 }
155
156 public function getLocalDomainID() {
157 return $this->localDomain->getId();
158 }
159
160 public function resolveDomainID( $domain ) {
161 return ( $domain !== false ) ? (string)$domain : $this->getLocalDomainID();
162 }
163
164 public function shutdown(
165 $mode = self::SHUTDOWN_CHRONPROT_SYNC,
166 callable $workCallback = null,
167 &$cpIndex = null,
168 &$cpClientId = null
169 ) {
171 if ( $mode === self::SHUTDOWN_CHRONPROT_SYNC ) {
172 $this->shutdownChronologyProtector( $chronProt, $workCallback, 'sync', $cpIndex );
173 } elseif ( $mode === self::SHUTDOWN_CHRONPROT_ASYNC ) {
174 $this->shutdownChronologyProtector( $chronProt, null, 'async', $cpIndex );
175 }
176
177 $cpClientId = $chronProt->getClientId();
178
179 $this->commitMasterChanges( __METHOD__ ); // sanity
180 }
181
187 abstract public function newMainLB( $domain = false );
188
194 abstract public function getMainLB( $domain = false );
195
201 abstract public function newExternalLB( $cluster );
202
208 abstract public function getExternalLB( $cluster );
209
216 protected function forEachLBCallMethod( $methodName, array $args = [] ) {
217 $this->forEachLB(
218 function ( ILoadBalancer $loadBalancer, $methodName, array $args ) {
219 $loadBalancer->$methodName( ...$args );
220 },
221 [ $methodName, $args ]
222 );
223 }
224
225 public function flushReplicaSnapshots( $fname = __METHOD__ ) {
226 $this->forEachLBCallMethod( 'flushReplicaSnapshots', [ $fname ] );
227 }
228
229 final public function commitAll( $fname = __METHOD__, array $options = [] ) {
231 $this->forEachLBCallMethod( 'flushMasterSnapshots', [ $fname ] );
232 $this->forEachLBCallMethod( 'flushReplicaSnapshots', [ $fname ] );
233 }
234
235 final public function beginMasterChanges( $fname = __METHOD__ ) {
236 $this->assertTransactionRoundStage( self::ROUND_CURSORY );
237 $this->trxRoundStage = self::ROUND_BEGINNING;
238 if ( $this->trxRoundId !== false ) {
239 throw new DBTransactionError(
240 null,
241 "$fname: transaction round '{$this->trxRoundId}' already started."
242 );
243 }
244 $this->trxRoundId = $fname;
245 // Set DBO_TRX flags on all appropriate DBs
246 $this->forEachLBCallMethod( 'beginMasterChanges', [ $fname ] );
247 $this->trxRoundStage = self::ROUND_CURSORY;
248 }
249
250 final public function commitMasterChanges( $fname = __METHOD__, array $options = [] ) {
251 $this->assertTransactionRoundStage( self::ROUND_CURSORY );
252 $this->trxRoundStage = self::ROUND_COMMITTING;
253 if ( $this->trxRoundId !== false && $this->trxRoundId !== $fname ) {
254 throw new DBTransactionError(
255 null,
256 "$fname: transaction round '{$this->trxRoundId}' still running."
257 );
258 }
260 $scope = $this->getScopedPHPBehaviorForCommit(); // try to ignore client aborts
261 // Run pre-commit callbacks and suppress post-commit callbacks, aborting on failure
262 do {
263 $count = 0; // number of callbacks executed this iteration
264 $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$count ) {
265 $count += $lb->finalizeMasterChanges();
266 } );
267 } while ( $count > 0 );
268 $this->trxRoundId = false;
269 // Perform pre-commit checks, aborting on failure
270 $this->forEachLBCallMethod( 'approveMasterChanges', [ $options ] );
271 // Log the DBs and methods involved in multi-DB transactions
273 // Actually perform the commit on all master DB connections and revert DBO_TRX
274 $this->forEachLBCallMethod( 'commitMasterChanges', [ $fname ] );
275 // Run all post-commit callbacks in a separate step
276 $this->trxRoundStage = self::ROUND_COMMIT_CALLBACKS;
278 $this->trxRoundStage = self::ROUND_CURSORY;
279 // Throw any last post-commit callback error
280 if ( $e instanceof Exception ) {
281 throw $e;
282 }
283 }
284
285 final public function rollbackMasterChanges( $fname = __METHOD__ ) {
286 $this->trxRoundStage = self::ROUND_ROLLING_BACK;
287 $this->trxRoundId = false;
288 // Actually perform the rollback on all master DB connections and revert DBO_TRX
289 $this->forEachLBCallMethod( 'rollbackMasterChanges', [ $fname ] );
290 // Run all post-commit callbacks in a separate step
291 $this->trxRoundStage = self::ROUND_ROLLBACK_CALLBACKS;
293 $this->trxRoundStage = self::ROUND_CURSORY;
294 }
295
300 // Run all post-commit callbacks until new ones stop getting added
301 $e = null; // first callback exception
302 do {
303 $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$e ) {
305 $e = $e ?: $ex;
306 } );
307 } while ( $this->hasMasterChanges() );
308 // Run all listener callbacks once
309 $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$e ) {
311 $e = $e ?: $ex;
312 } );
313
314 return $e;
315 }
316
317 public function hasTransactionRound() {
318 return ( $this->trxRoundId !== false );
319 }
320
321 public function isReadyForRoundOperations() {
322 return ( $this->trxRoundStage === self::ROUND_CURSORY );
323 }
324
328 private function logIfMultiDbTransaction() {
329 $callersByDB = [];
330 $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$callersByDB ) {
331 $masterName = $lb->getServerName( $lb->getWriterIndex() );
332 $callers = $lb->pendingMasterChangeCallers();
333 if ( $callers ) {
334 $callersByDB[$masterName] = $callers;
335 }
336 } );
337
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";
343 }
344 $this->queryLogger->info( $msg );
345 }
346 }
347
348 public function hasMasterChanges() {
349 $ret = false;
350 $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$ret ) {
351 $ret = $ret || $lb->hasMasterChanges();
352 } );
353
354 return $ret;
355 }
356
357 public function laggedReplicaUsed() {
358 $ret = false;
359 $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$ret ) {
360 $ret = $ret || $lb->laggedReplicaUsed();
361 } );
362
363 return $ret;
364 }
365
366 public function hasOrMadeRecentMasterChanges( $age = null ) {
367 $ret = false;
368 $this->forEachLB( function ( ILoadBalancer $lb ) use ( $age, &$ret ) {
369 $ret = $ret || $lb->hasOrMadeRecentMasterChanges( $age );
370 } );
371 return $ret;
372 }
373
374 public function waitForReplication( array $opts = [] ) {
375 $opts += [
376 'domain' => false,
377 'cluster' => false,
378 'timeout' => $this->cliMode ? 60 : 1,
379 'ifWritesSince' => null
380 ];
381
382 if ( $opts['domain'] === false && isset( $opts['wiki'] ) ) {
383 $opts['domain'] = $opts['wiki']; // b/c
384 }
385
386 // Figure out which clusters need to be checked
388 $lbs = [];
389 if ( $opts['cluster'] !== false ) {
390 $lbs[] = $this->getExternalLB( $opts['cluster'] );
391 } elseif ( $opts['domain'] !== false ) {
392 $lbs[] = $this->getMainLB( $opts['domain'] );
393 } else {
394 $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$lbs ) {
395 $lbs[] = $lb;
396 } );
397 if ( !$lbs ) {
398 return; // nothing actually used
399 }
400 }
401
402 // Get all the master positions of applicable DBs right now.
403 // This can be faster since waiting on one cluster reduces the
404 // time needed to wait on the next clusters.
405 $masterPositions = array_fill( 0, count( $lbs ), false );
406 foreach ( $lbs as $i => $lb ) {
407 if ( $lb->getServerCount() <= 1 ) {
408 // T29975 - Don't try to wait for replica DBs if there are none
409 // Prevents permission error when getting master position
410 continue;
411 } elseif ( $opts['ifWritesSince']
412 && $lb->lastMasterChangeTimestamp() < $opts['ifWritesSince']
413 ) {
414 continue; // no writes since the last wait
415 }
416 $masterPositions[$i] = $lb->getMasterPos();
417 }
418
419 // Run any listener callbacks *after* getting the DB positions. The more
420 // time spent in the callbacks, the less time is spent in waitForAll().
421 foreach ( $this->replicationWaitCallbacks as $callback ) {
422 $callback();
423 }
424
425 $failed = [];
426 foreach ( $lbs as $i => $lb ) {
427 if ( $masterPositions[$i] ) {
428 // The RDBMS may not support getMasterPos()
429 if ( !$lb->waitForAll( $masterPositions[$i], $opts['timeout'] ) ) {
430 $failed[] = $lb->getServerName( $lb->getWriterIndex() );
431 }
432 }
433 }
434
435 return !$failed;
436 }
437
438 public function setWaitForReplicationListener( $name, callable $callback = null ) {
439 if ( $callback ) {
440 $this->replicationWaitCallbacks[$name] = $callback;
441 } else {
442 unset( $this->replicationWaitCallbacks[$name] );
443 }
444 }
445
447 if ( $this->hasMasterChanges() ) {
448 $this->queryLogger->error( __METHOD__ . ": $fname does not have outer scope.\n" .
449 ( new RuntimeException() )->getTraceAsString() );
450
451 return null;
452 }
453
454 return $this->ticket;
455 }
456
457 final public function commitAndWaitForReplication( $fname, $ticket, array $opts = [] ) {
458 if ( $ticket !== $this->ticket ) {
459 $this->perfLogger->error( __METHOD__ . ": $fname does not have outer scope.\n" .
460 ( new RuntimeException() )->getTraceAsString() );
461
462 return;
463 }
464
465 // The transaction owner and any caller with the empty transaction ticket can commit
466 // so that getEmptyTransactionTicket() callers don't risk seeing DBTransactionError.
467 if ( $this->trxRoundId !== false && $fname !== $this->trxRoundId ) {
468 $this->queryLogger->info( "$fname: committing on behalf of {$this->trxRoundId}." );
469 $fnameEffective = $this->trxRoundId;
470 } else {
471 $fnameEffective = $fname;
472 }
473
474 $this->commitMasterChanges( $fnameEffective );
475 $waitSucceeded = $this->waitForReplication( $opts );
476 // If a nested caller committed on behalf of $fname, start another empty $fname
477 // transaction, leaving the caller with the same empty transaction state as before.
478 if ( $fnameEffective !== $fname ) {
479 $this->beginMasterChanges( $fnameEffective );
480 }
481 return $waitSucceeded;
482 }
483
484 public function getChronologyProtectorTouched( $dbName ) {
485 return $this->getChronologyProtector()->getTouched( $dbName );
486 }
487
488 public function disableChronologyProtection() {
489 $this->getChronologyProtector()->setEnabled( false );
490 }
491
495 protected function getChronologyProtector() {
496 if ( $this->chronProt ) {
497 return $this->chronProt;
498 }
499
500 $this->chronProt = new ChronologyProtector(
501 $this->memStash,
502 [
503 'ip' => $this->requestInfo['IPAddress'],
504 'agent' => $this->requestInfo['UserAgent'],
505 'clientId' => $this->requestInfo['ChronologyClientId']
506 ],
507 $this->requestInfo['ChronologyPositionIndex']
508 );
509 $this->chronProt->setLogger( $this->replLogger );
510
511 if ( $this->cliMode ) {
512 $this->chronProt->setEnabled( false );
513 } elseif ( $this->requestInfo['ChronologyProtection'] === 'false' ) {
514 // Request opted out of using position wait logic. This is useful for requests
515 // done by the job queue or background ETL that do not have a meaningful session.
516 $this->chronProt->setWaitEnabled( false );
517 } elseif ( $this->memStash instanceof EmptyBagOStuff ) {
518 // No where to store any DB positions and wait for them to appear
519 $this->chronProt->setEnabled( false );
520 $this->replLogger->info( 'Cannot use ChronologyProtector with EmptyBagOStuff.' );
521 }
522
523 $this->replLogger->debug( __METHOD__ . ': using request info ' .
524 json_encode( $this->requestInfo, JSON_PRETTY_PRINT ) );
525
526 return $this->chronProt;
527 }
528
537 protected function shutdownChronologyProtector(
538 ChronologyProtector $cp, $workCallback, $mode, &$cpIndex = null
539 ) {
540 // Record all the master positions needed
541 $this->forEachLB( function ( ILoadBalancer $lb ) use ( $cp ) {
542 $cp->shutdownLB( $lb );
543 } );
544 // Write them to the persistent stash. Try to do something useful by running $work
545 // while ChronologyProtector waits for the stash write to replicate to all DCs.
546 $unsavedPositions = $cp->shutdown( $workCallback, $mode, $cpIndex );
547 if ( $unsavedPositions && $workCallback ) {
548 // Invoke callback in case it did not cache the result yet
549 $workCallback(); // work now to block for less time in waitForAll()
550 }
551 // If the positions failed to write to the stash, at least wait on local datacenter
552 // replica DBs to catch up before responding. Even if there are several DCs, this increases
553 // the chance that the user will see their own changes immediately afterwards. As long
554 // as the sticky DC cookie applies (same domain), this is not even an issue.
555 $this->forEachLB( function ( ILoadBalancer $lb ) use ( $unsavedPositions ) {
556 $masterName = $lb->getServerName( $lb->getWriterIndex() );
557 if ( isset( $unsavedPositions[$masterName] ) ) {
558 $lb->waitForAll( $unsavedPositions[$masterName] );
559 }
560 } );
561 }
562
567 final protected function baseLoadBalancerParams() {
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;
572 } else {
573 $initStage = null;
574 }
575
576 return [
577 'localDomain' => $this->localDomain,
578 'readOnlyReason' => $this->readOnlyReason,
579 'srvCache' => $this->srvCache,
580 'wanCache' => $this->wanCache,
581 'profiler' => $this->profiler,
582 'trxProfiler' => $this->trxProfiler,
583 'queryLogger' => $this->queryLogger,
584 'connLogger' => $this->connLogger,
585 'replLogger' => $this->replLogger,
586 'errorLogger' => $this->errorLogger,
587 'deprecationLogger' => $this->deprecationLogger,
588 'hostname' => $this->hostname,
589 'cliMode' => $this->cliMode,
590 'agent' => $this->agent,
591 'defaultGroup' => $this->defaultGroup,
592 'chronologyCallback' => function ( ILoadBalancer $lb ) {
593 // Defer ChronologyProtector construction in case setRequestInfo() ends up
594 // being called later (but before the first connection attempt) (T192611)
595 $this->getChronologyProtector()->initLB( $lb );
596 },
597 'roundStage' => $initStage
598 ];
599 }
600
604 protected function initLoadBalancer( ILoadBalancer $lb ) {
605 if ( $this->trxRoundId !== false ) {
606 $lb->beginMasterChanges( $this->trxRoundId ); // set DBO_TRX
607 }
608
609 $lb->setTableAliases( $this->tableAliases );
610 $lb->setIndexAliases( $this->indexAliases );
611 }
612
613 public function setTableAliases( array $aliases ) {
614 $this->tableAliases = $aliases;
615 }
616
617 public function setIndexAliases( array $aliases ) {
618 $this->indexAliases = $aliases;
619 }
620
621 public function setDomainPrefix( $prefix ) {
622 $this->localDomain = new DatabaseDomain(
623 $this->localDomain->getDatabase(),
624 null,
625 $prefix
626 );
627
628 $this->forEachLB( function ( ILoadBalancer $lb ) use ( $prefix ) {
629 $lb->setDomainPrefix( $prefix );
630 } );
631 }
632
633 public function redefineLocalDomain( $domain ) {
634 $this->closeAll();
635
636 $this->localDomain = DatabaseDomain::newFromId( $domain );
637
638 $this->forEachLB( function ( ILoadBalancer $lb ) {
639 $lb->redefineLocalDomain( $this->localDomain );
640 } );
641 }
642
643 public function closeAll() {
644 $this->forEachLBCallMethod( 'closeAll', [] );
645 }
646
647 public function setAgentName( $agent ) {
648 $this->agent = $agent;
649 }
650
651 public function appendShutdownCPIndexAsQuery( $url, $index ) {
652 $usedCluster = 0;
653 $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$usedCluster ) {
654 $usedCluster |= ( $lb->getServerCount() > 1 );
655 } );
656
657 if ( !$usedCluster ) {
658 return $url; // no master/replica clusters touched
659 }
660
661 return strpos( $url, '?' ) === false ? "$url?cpPosIndex=$index" : "$url&cpPosIndex=$index";
662 }
663
671 public static function makeCookieValueFromCPIndex( $index, $time, $clientId ) {
672 return "$index@$time#$clientId";
673 }
674
681 public static function getCPInfoFromCookieValue( $value, $minTimestamp ) {
682 static $placeholder = [ 'index' => null, 'clientId' => null ];
683
684 if ( !preg_match( '/^(\d+)@(\d+)#([0-9a-f]{32})$/', $value, $m ) ) {
685 return $placeholder; // invalid
686 }
687
688 $index = (int)$m[1];
689 if ( $index <= 0 ) {
690 return $placeholder; // invalid
691 } elseif ( isset( $m[2] ) && $m[2] !== '' && (int)$m[2] < $minTimestamp ) {
692 return $placeholder; // expired
693 }
694
695 $clientId = ( isset( $m[3] ) && $m[3] !== '' ) ? $m[3] : null;
696
697 return [ 'index' => $index, 'clientId' => $clientId ];
698 }
699
700 public function setRequestInfo( array $info ) {
701 if ( $this->chronProt ) {
702 throw new LogicException( 'ChronologyProtector already initialized.' );
703 }
704
705 $this->requestInfo = $info + $this->requestInfo;
706 }
707
711 private function assertTransactionRoundStage( $stage ) {
712 if ( $this->trxRoundStage !== $stage ) {
713 throw new DBTransactionError(
714 null,
715 "Transaction round stage must be '$stage' (not '{$this->trxRoundStage}')"
716 );
717 }
718 }
719
726 final protected function getScopedPHPBehaviorForCommit() {
727 if ( PHP_SAPI != 'cli' ) { // https://bugs.php.net/bug.php?id=47540
728 $old = ignore_user_abort( true ); // avoid half-finished operations
729 return new ScopedCallback( function () use ( $old ) {
730 ignore_user_abort( $old );
731 } );
732 }
733
734 return null;
735 }
736
737 function __destruct() {
738 $this->destroy();
739 }
740}
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
if(defined( 'MW_SETUP_CALLBACK')) $fname
Customization point after all loading (constants, functions, classes, DefaultSettings,...
Definition Setup.php:121
if( $line===false) $args
Definition cdb.php:64
Class representing a cache/ephemeral data store.
Definition BagOStuff.php:58
A BagOStuff object with no objects in it.
Multi-datacenter aware caching interface.
Class for ensuring a consistent ordering of events as seen by the user, despite replication.
shutdownLB(ILoadBalancer $lb)
Notify the ChronologyProtector that the ILoadBalancer is about to shut down.
shutdown(callable $workCallback=null, $mode='sync', &$cpIndex=null)
Notify the ChronologyProtector that the LBFactory is done calling shutdownLB() for now.
Class to handle database/prefix specification for IDatabase domains.
An interface for generating database load balancers.
Definition LBFactory.php:39
LoggerInterface $replLogger
Definition LBFactory.php:47
callable[] $replicationWaitCallbacks
Definition LBFactory.php:83
isReadyForRoundOperations()
Check if transaction rounds can be started, committed, or rolled back right now.
setTableAliases(array $aliases)
Make certain table names use their own database, schema, and table prefix when passed into SQL querie...
hasMasterChanges()
Determine if any master connection has pending changes.
WANObjectCache $wanCache
Definition LBFactory.php:64
assertTransactionRoundStage( $stage)
shutdownChronologyProtector(ChronologyProtector $cp, $workCallback, $mode, &$cpIndex=null)
Get and record all of the staged DB positions into persistent memory storage.
string[] $indexAliases
Map of (index alias => index)
Definition LBFactory.php:81
newMainLB( $domain=false)
getEmptyTransactionTicket( $fname)
Get a token asserting that no transaction writes are active.
hasTransactionRound()
Check if an explicit transaction round is active.
redefineLocalDomain( $domain)
Close all connection and redefine the local domain for testing or schema creation.
LoggerInterface $connLogger
Definition LBFactory.php:49
setRequestInfo(array $info)
LoggerInterface $queryLogger
Definition LBFactory.php:51
commitAndWaitForReplication( $fname, $ticket, array $opts=[])
Convenience method for safely running commitMasterChanges()/waitForReplication()
string $trxRoundStage
One of the ROUND_* class constants.
Definition LBFactory.php:90
callable $errorLogger
Error logger.
Definition LBFactory.php:55
shutdown( $mode=self::SHUTDOWN_CHRONPROT_SYNC, callable $workCallback=null, &$cpIndex=null, &$cpClientId=null)
Prepare all tracked load balancers for shutdown.
beginMasterChanges( $fname=__METHOD__)
Flush any master transaction snapshots and set DBO_TRX (if DBO_DEFAULT is set)
array $requestInfo
Web request information about the client.
Definition LBFactory.php:72
string $hostname
Local hostname of the app server.
Definition LBFactory.php:70
getMainLB( $domain=false)
getChronologyProtectorTouched( $dbName)
getLocalDomainID()
Get the local (and default) database domain ID of connection handles.
getScopedPHPBehaviorForCommit()
Make PHP ignore user aborts/disconnects until the returned value leaves scope.
setDomainPrefix( $prefix)
Set a new table prefix for the existing local domain ID for testing.
waitForReplication(array $opts=[])
Waits for the replica DBs to catch up to the current master position.
LoggerInterface $perfLogger
Definition LBFactory.php:53
rollbackMasterChanges( $fname=__METHOD__)
Rollback changes on all master connections.
initLoadBalancer(ILoadBalancer $lb)
hasOrMadeRecentMasterChanges( $age=null)
Determine if any master connection has pending/written changes from this request.
DatabaseDomain $localDomain
Local domain.
Definition LBFactory.php:67
TransactionProfiler $trxProfiler
Definition LBFactory.php:45
ChronologyProtector $chronProt
Definition LBFactory.php:41
string bool $trxRoundId
String if a requested DBO_TRX transaction round is active.
Definition LBFactory.php:88
destroy()
Disables all load balancers.
__construct(array $conf)
Construct a manager of ILoadBalancer objects.
forEachLBCallMethod( $methodName, array $args=[])
Call a method of each tracked load balancer.
object string $profiler
Class name or object With profileIn/profileOut methods.
Definition LBFactory.php:43
setWaitForReplicationListener( $name, callable $callback=null)
Add a callback to be run in every call to waitForReplication() before waiting.
laggedReplicaUsed()
Detemine if any lagged replica DB connection was used.
appendShutdownCPIndexAsQuery( $url, $index)
Append ?cpPosIndex parameter to a URL for ChronologyProtector purposes if needed.
disableChronologyProtection()
Disable the ChronologyProtector for all load balancers.
setIndexAliases(array $aliases)
Convert certain index names to alternative names before querying the DB.
commitMasterChanges( $fname=__METHOD__, array $options=[])
Commit changes and clear view snapshots on all master connections.
commitAll( $fname=__METHOD__, array $options=[])
Commit open transactions on all connections.
bool $cliMode
Whether this PHP instance is for a CLI script.
Definition LBFactory.php:74
logIfMultiDbTransaction()
Log query info if multi DB transactions are going to be committed now.
static getCPInfoFromCookieValue( $value, $minTimestamp)
string bool $readOnlyReason
Reason all LBs are read-only or false if not.
Definition LBFactory.php:93
baseLoadBalancerParams()
Base parameters to ILoadBalancer::__construct()
static makeCookieValueFromCPIndex( $index, $time, $clientId)
string $agent
Agent name for query profiling.
Definition LBFactory.php:76
callable $deprecationLogger
Deprecation logger.
Definition LBFactory.php:57
closeAll()
Close all open database connections on all open load balancers.
flushReplicaSnapshots( $fname=__METHOD__)
Commit all replica DB transactions so as to flush any REPEATABLE-READ or SSI snapshot.
Helper class that detects high-contention DB queries via profiling calls.
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
see documentation in includes Linker php for Linker::makeImageLink & $time
Definition hooks.txt:1841
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
Definition hooks.txt:181
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
Definition hooks.txt:2050
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
Definition hooks.txt:2054
Allows to change the fields on the form that will be generated $name
Definition hooks.txt:302
returning false will NOT prevent logging $e
Definition hooks.txt:2226
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition injection.txt:37
An interface for generating database load balancers.
forEachLB( $callback, array $params=[])
Execute a function for each tracked load balancer The callback is called with the load balancer as th...
Database cluster connection, tracking, load balancing, and transaction manager interface.
finalizeMasterChanges()
Run pre-commit callbacks and defer execution of post-commit callbacks.
setDomainPrefix( $prefix)
Set a new table prefix for the existing local domain ID for testing.
runMasterTransactionIdleCallbacks()
Consume and run all pending post-COMMIT/ROLLBACK callbacks and commit dangling transactions.
hasOrMadeRecentMasterChanges( $age=null)
Check if this load balancer object had any recent or still pending writes issued against it by this P...
beginMasterChanges( $fname=__METHOD__)
Flush any master transaction snapshots and set DBO_TRX (if DBO_DEFAULT is set)
pendingMasterChangeCallers()
Get the list of callers that have pending master changes.
hasMasterChanges()
Whether there are pending changes or callbacks in a transaction by this thread.
setTableAliases(array $aliases)
Make certain table names use their own database, schema, and table prefix when passed into SQL querie...
redefineLocalDomain( $domain)
Close all connection and redefine the local domain for testing or schema creation.
laggedReplicaUsed()
Checks whether the database for generic connections this request was both:
getServerCount()
Get the number of defined servers (not the number of open connections)
setIndexAliases(array $aliases)
Convert certain index names to alternative names before querying the DB.
runMasterTransactionListenerCallbacks()
Run all recurring post-COMMIT/ROLLBACK listener callbacks.
getServerName( $i)
Get the host name or IP address of the server with the specified index.
waitForAll( $pos, $timeout=null)
Set the master wait position and wait for ALL replica DBs to catch up to it.
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))