MediaWiki  master
LBFactory.php
Go to the documentation of this file.
1 <?php
24 namespace Wikimedia\Rdbms;
25 
35 
40 abstract class LBFactory implements ILBFactory {
42  private $chronProt;
44  private $profiler;
46  private $trxProfiler;
48  private $replLogger;
50  private $connLogger;
52  private $queryLogger;
54  private $perfLogger;
56  private $errorLogger;
59 
61  protected $srvCache;
63  protected $memStash;
65  protected $wanCache;
66 
68  protected $localDomain;
69 
71  private $hostname;
73  private $requestInfo;
75  private $cliMode;
77  private $agent;
79  private $secret;
80 
82  private $tableAliases = [];
84  private $indexAliases = [];
87 
89  private $id;
91  private $ticket;
93  private $trxRoundId = false;
95  private $trxRoundStage = self::ROUND_CURSORY;
96 
98  protected $readOnlyReason = false;
99 
101  private $defaultGroup = null;
102 
104  protected $maxLag;
105 
106  const ROUND_CURSORY = 'cursory';
107  const ROUND_BEGINNING = 'within-begin';
108  const ROUND_COMMITTING = 'within-commit';
109  const ROUND_ROLLING_BACK = 'within-rollback';
110  const ROUND_COMMIT_CALLBACKS = 'within-commit-callbacks';
111  const ROUND_ROLLBACK_CALLBACKS = 'within-rollback-callbacks';
112 
113  private static $loggerFields =
114  [ 'replLogger', 'connLogger', 'queryLogger', 'perfLogger' ];
115 
116  public function __construct( array $conf ) {
117  $this->localDomain = isset( $conf['localDomain'] )
118  ? DatabaseDomain::newFromId( $conf['localDomain'] )
120 
121  $this->maxLag = $conf['maxLag'] ?? null;
122  if ( isset( $conf['readOnlyReason'] ) && is_string( $conf['readOnlyReason'] ) ) {
123  $this->readOnlyReason = $conf['readOnlyReason'];
124  }
125 
126  $this->srvCache = $conf['srvCache'] ?? new EmptyBagOStuff();
127  $this->memStash = $conf['memStash'] ?? new EmptyBagOStuff();
128  $this->wanCache = $conf['wanCache'] ?? WANObjectCache::newEmpty();
129 
130  foreach ( self::$loggerFields as $key ) {
131  $this->$key = $conf[$key] ?? new NullLogger();
132  }
133  $this->errorLogger = $conf['errorLogger'] ?? function ( Exception $e ) {
134  trigger_error( get_class( $e ) . ': ' . $e->getMessage(), E_USER_WARNING );
135  };
136  $this->deprecationLogger = $conf['deprecationLogger'] ?? function ( $msg ) {
137  trigger_error( $msg, E_USER_DEPRECATED );
138  };
139 
140  $this->profiler = $conf['profiler'] ?? null;
141  $this->trxProfiler = $conf['trxProfiler'] ?? new TransactionProfiler();
142 
143  $this->requestInfo = [
144  'IPAddress' => $_SERVER[ 'REMOTE_ADDR' ] ?? '',
145  'UserAgent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
146  // Headers application can inject via LBFactory::setRequestInfo()
147  'ChronologyProtection' => null,
148  'ChronologyClientId' => null, // prior $cpClientId value from LBFactory::shutdown()
149  'ChronologyPositionIndex' => null // prior $cpIndex value from LBFactory::shutdown()
150  ];
151 
152  $this->cliMode = $conf['cliMode'] ?? ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' );
153  $this->hostname = $conf['hostname'] ?? gethostname();
154  $this->agent = $conf['agent'] ?? '';
155  $this->defaultGroup = $conf['defaultGroup'] ?? null;
156  $this->secret = $conf['secret'] ?? '';
157 
158  $this->id = mt_rand();
159  $this->ticket = mt_rand();
160  }
161 
162  public function destroy() {
163  $this->shutdown( self::SHUTDOWN_NO_CHRONPROT );
164  $this->forEachLBCallMethod( 'disable' );
165  }
166 
167  public function getLocalDomainID() {
168  return $this->localDomain->getId();
169  }
170 
171  public function resolveDomainID( $domain ) {
172  return ( $domain !== false ) ? (string)$domain : $this->getLocalDomainID();
173  }
174 
175  public function shutdown(
176  $mode = self::SHUTDOWN_CHRONPROT_SYNC,
177  callable $workCallback = null,
178  &$cpIndex = null,
179  &$cpClientId = null
180  ) {
182  if ( $mode === self::SHUTDOWN_CHRONPROT_SYNC ) {
183  $this->shutdownChronologyProtector( $chronProt, $workCallback, 'sync', $cpIndex );
184  } elseif ( $mode === self::SHUTDOWN_CHRONPROT_ASYNC ) {
185  $this->shutdownChronologyProtector( $chronProt, null, 'async', $cpIndex );
186  }
187 
188  $cpClientId = $chronProt->getClientId();
189 
190  $this->commitMasterChanges( __METHOD__ ); // sanity
191  }
192 
198  abstract public function newMainLB( $domain = false );
199 
205  abstract public function getMainLB( $domain = false );
206 
212  abstract public function newExternalLB( $cluster );
213 
219  abstract public function getExternalLB( $cluster );
220 
227  protected function forEachLBCallMethod( $methodName, array $args = [] ) {
228  $this->forEachLB(
229  function ( ILoadBalancer $loadBalancer, $methodName, array $args ) {
230  $loadBalancer->$methodName( ...$args );
231  },
232  [ $methodName, $args ]
233  );
234  }
235 
236  public function flushReplicaSnapshots( $fname = __METHOD__ ) {
237  $this->forEachLBCallMethod( 'flushReplicaSnapshots', [ $fname ] );
238  }
239 
240  final public function commitAll( $fname = __METHOD__, array $options = [] ) {
242  $this->forEachLBCallMethod( 'flushMasterSnapshots', [ $fname ] );
243  $this->forEachLBCallMethod( 'flushReplicaSnapshots', [ $fname ] );
244  }
245 
246  final public function beginMasterChanges( $fname = __METHOD__ ) {
247  $this->assertTransactionRoundStage( self::ROUND_CURSORY );
248  $this->trxRoundStage = self::ROUND_BEGINNING;
249  if ( $this->trxRoundId !== false ) {
250  throw new DBTransactionError(
251  null,
252  "$fname: transaction round '{$this->trxRoundId}' already started."
253  );
254  }
255  $this->trxRoundId = $fname;
256  // Set DBO_TRX flags on all appropriate DBs
257  $this->forEachLBCallMethod( 'beginMasterChanges', [ $fname, $this->id ] );
258  $this->trxRoundStage = self::ROUND_CURSORY;
259  }
260 
261  final public function commitMasterChanges( $fname = __METHOD__, array $options = [] ) {
262  $this->assertTransactionRoundStage( self::ROUND_CURSORY );
263  $this->trxRoundStage = self::ROUND_COMMITTING;
264  if ( $this->trxRoundId !== false && $this->trxRoundId !== $fname ) {
265  throw new DBTransactionError(
266  null,
267  "$fname: transaction round '{$this->trxRoundId}' still running."
268  );
269  }
271  $scope = ScopedCallback::newScopedIgnoreUserAbort(); // try to ignore client aborts
272  // Run pre-commit callbacks and suppress post-commit callbacks, aborting on failure
273  do {
274  $count = 0; // number of callbacks executed this iteration
275  $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$count, $fname ) {
276  $count += $lb->finalizeMasterChanges( $fname, $this->id );
277  } );
278  } while ( $count > 0 );
279  $this->trxRoundId = false;
280  // Perform pre-commit checks, aborting on failure
281  $this->forEachLBCallMethod( 'approveMasterChanges', [ $options, $fname, $this->id ] );
282  // Log the DBs and methods involved in multi-DB transactions
283  $this->logIfMultiDbTransaction();
284  // Actually perform the commit on all master DB connections and revert DBO_TRX
285  $this->forEachLBCallMethod( 'commitMasterChanges', [ $fname, $this->id ] );
286  // Run all post-commit callbacks in a separate step
287  $this->trxRoundStage = self::ROUND_COMMIT_CALLBACKS;
289  $this->trxRoundStage = self::ROUND_CURSORY;
290  // Throw any last post-commit callback error
291  if ( $e instanceof Exception ) {
292  throw $e;
293  }
294  }
295 
296  final public function rollbackMasterChanges( $fname = __METHOD__ ) {
297  $this->trxRoundStage = self::ROUND_ROLLING_BACK;
298  $this->trxRoundId = false;
299  // Actually perform the rollback on all master DB connections and revert DBO_TRX
300  $this->forEachLBCallMethod( 'rollbackMasterChanges', [ $fname, $this->id ] );
301  // Run all post-commit callbacks in a separate step
302  $this->trxRoundStage = self::ROUND_ROLLBACK_CALLBACKS;
304  $this->trxRoundStage = self::ROUND_CURSORY;
305  }
306 
310  private function executePostTransactionCallbacks() {
311  $fname = __METHOD__;
312  // Run all post-commit callbacks until new ones stop getting added
313  $e = null; // first callback exception
314  do {
315  $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$e, $fname ) {
316  $ex = $lb->runMasterTransactionIdleCallbacks( $fname, $this->id );
317  $e = $e ?: $ex;
318  } );
319  } while ( $this->hasMasterChanges() );
320  // Run all listener callbacks once
321  $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$e, $fname ) {
322  $ex = $lb->runMasterTransactionListenerCallbacks( $fname, $this->id );
323  $e = $e ?: $ex;
324  } );
325 
326  return $e;
327  }
328 
329  public function hasTransactionRound() {
330  return ( $this->trxRoundId !== false );
331  }
332 
333  public function isReadyForRoundOperations() {
334  return ( $this->trxRoundStage === self::ROUND_CURSORY );
335  }
336 
340  private function logIfMultiDbTransaction() {
341  $callersByDB = [];
342  $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$callersByDB ) {
343  $masterName = $lb->getServerName( $lb->getWriterIndex() );
344  $callers = $lb->pendingMasterChangeCallers();
345  if ( $callers ) {
346  $callersByDB[$masterName] = $callers;
347  }
348  } );
349 
350  if ( count( $callersByDB ) >= 2 ) {
351  $dbs = implode( ', ', array_keys( $callersByDB ) );
352  $msg = "Multi-DB transaction [{$dbs}]:\n";
353  foreach ( $callersByDB as $db => $callers ) {
354  $msg .= "$db: " . implode( '; ', $callers ) . "\n";
355  }
356  $this->queryLogger->info( $msg );
357  }
358  }
359 
360  public function hasMasterChanges() {
361  $ret = false;
362  $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$ret ) {
363  $ret = $ret || $lb->hasMasterChanges();
364  } );
365 
366  return $ret;
367  }
368 
369  public function laggedReplicaUsed() {
370  $ret = false;
371  $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$ret ) {
372  $ret = $ret || $lb->laggedReplicaUsed();
373  } );
374 
375  return $ret;
376  }
377 
378  public function hasOrMadeRecentMasterChanges( $age = null ) {
379  $ret = false;
380  $this->forEachLB( function ( ILoadBalancer $lb ) use ( $age, &$ret ) {
381  $ret = $ret || $lb->hasOrMadeRecentMasterChanges( $age );
382  } );
383  return $ret;
384  }
385 
386  public function waitForReplication( array $opts = [] ) {
387  $opts += [
388  'domain' => false,
389  'cluster' => false,
390  'timeout' => $this->cliMode ? 60 : 1,
391  'ifWritesSince' => null
392  ];
393 
394  if ( $opts['domain'] === false && isset( $opts['wiki'] ) ) {
395  $opts['domain'] = $opts['wiki']; // b/c
396  }
397 
398  // Figure out which clusters need to be checked
400  $lbs = [];
401  if ( $opts['cluster'] !== false ) {
402  $lbs[] = $this->getExternalLB( $opts['cluster'] );
403  } elseif ( $opts['domain'] !== false ) {
404  $lbs[] = $this->getMainLB( $opts['domain'] );
405  } else {
406  $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$lbs ) {
407  $lbs[] = $lb;
408  } );
409  if ( !$lbs ) {
410  return true; // nothing actually used
411  }
412  }
413 
414  // Get all the master positions of applicable DBs right now.
415  // This can be faster since waiting on one cluster reduces the
416  // time needed to wait on the next clusters.
417  $masterPositions = array_fill( 0, count( $lbs ), false );
418  foreach ( $lbs as $i => $lb ) {
419  if ( $lb->getServerCount() <= 1 ) {
420  // T29975 - Don't try to wait for replica DBs if there are none
421  // Prevents permission error when getting master position
422  continue;
423  } elseif ( $opts['ifWritesSince']
424  && $lb->lastMasterChangeTimestamp() < $opts['ifWritesSince']
425  ) {
426  continue; // no writes since the last wait
427  }
428  $masterPositions[$i] = $lb->getMasterPos();
429  }
430 
431  // Run any listener callbacks *after* getting the DB positions. The more
432  // time spent in the callbacks, the less time is spent in waitForAll().
433  foreach ( $this->replicationWaitCallbacks as $callback ) {
434  $callback();
435  }
436 
437  $failed = [];
438  foreach ( $lbs as $i => $lb ) {
439  if ( $masterPositions[$i] ) {
440  // The RDBMS may not support getMasterPos()
441  if ( !$lb->waitForAll( $masterPositions[$i], $opts['timeout'] ) ) {
442  $failed[] = $lb->getServerName( $lb->getWriterIndex() );
443  }
444  }
445  }
446 
447  return !$failed;
448  }
449 
450  public function setWaitForReplicationListener( $name, callable $callback = null ) {
451  if ( $callback ) {
452  $this->replicationWaitCallbacks[$name] = $callback;
453  } else {
454  unset( $this->replicationWaitCallbacks[$name] );
455  }
456  }
457 
458  public function getEmptyTransactionTicket( $fname ) {
459  if ( $this->hasMasterChanges() ) {
460  $this->queryLogger->error( __METHOD__ . ": $fname does not have outer scope.\n" .
461  ( new RuntimeException() )->getTraceAsString() );
462 
463  return null;
464  }
465 
466  return $this->ticket;
467  }
468 
469  final public function commitAndWaitForReplication( $fname, $ticket, array $opts = [] ) {
470  if ( $ticket !== $this->ticket ) {
471  $this->perfLogger->error( __METHOD__ . ": $fname does not have outer scope.\n" .
472  ( new RuntimeException() )->getTraceAsString() );
473 
474  return false;
475  }
476 
477  // The transaction owner and any caller with the empty transaction ticket can commit
478  // so that getEmptyTransactionTicket() callers don't risk seeing DBTransactionError.
479  if ( $this->trxRoundId !== false && $fname !== $this->trxRoundId ) {
480  $this->queryLogger->info( "$fname: committing on behalf of {$this->trxRoundId}." );
481  $fnameEffective = $this->trxRoundId;
482  } else {
483  $fnameEffective = $fname;
484  }
485 
486  $this->commitMasterChanges( $fnameEffective );
487  $waitSucceeded = $this->waitForReplication( $opts );
488  // If a nested caller committed on behalf of $fname, start another empty $fname
489  // transaction, leaving the caller with the same empty transaction state as before.
490  if ( $fnameEffective !== $fname ) {
491  $this->beginMasterChanges( $fnameEffective );
492  }
493 
494  return $waitSucceeded;
495  }
496 
497  public function getChronologyProtectorTouched( $dbName ) {
498  return $this->getChronologyProtector()->getTouched( $dbName );
499  }
500 
501  public function disableChronologyProtection() {
502  $this->getChronologyProtector()->setEnabled( false );
503  }
504 
508  protected function getChronologyProtector() {
509  if ( $this->chronProt ) {
510  return $this->chronProt;
511  }
512 
513  $this->chronProt = new ChronologyProtector(
514  $this->memStash,
515  [
516  'ip' => $this->requestInfo['IPAddress'],
517  'agent' => $this->requestInfo['UserAgent'],
518  'clientId' => $this->requestInfo['ChronologyClientId'] ?: null
519  ],
520  $this->requestInfo['ChronologyPositionIndex'],
521  $this->secret
522  );
523  $this->chronProt->setLogger( $this->replLogger );
524 
525  if ( $this->cliMode ) {
526  $this->chronProt->setEnabled( false );
527  } elseif ( $this->requestInfo['ChronologyProtection'] === 'false' ) {
528  // Request opted out of using position wait logic. This is useful for requests
529  // done by the job queue or background ETL that do not have a meaningful session.
530  $this->chronProt->setWaitEnabled( false );
531  } elseif ( $this->memStash instanceof EmptyBagOStuff ) {
532  // No where to store any DB positions and wait for them to appear
533  $this->chronProt->setEnabled( false );
534  $this->replLogger->info( 'Cannot use ChronologyProtector with EmptyBagOStuff.' );
535  }
536 
537  $this->replLogger->debug( __METHOD__ . ': using request info ' .
538  json_encode( $this->requestInfo, JSON_PRETTY_PRINT ) );
539 
540  return $this->chronProt;
541  }
542 
551  protected function shutdownChronologyProtector(
552  ChronologyProtector $cp, $workCallback, $mode, &$cpIndex = null
553  ) {
554  // Record all the master positions needed
555  $this->forEachLB( function ( ILoadBalancer $lb ) use ( $cp ) {
557  } );
558  // Write them to the persistent stash. Try to do something useful by running $work
559  // while ChronologyProtector waits for the stash write to replicate to all DCs.
560  $unsavedPositions = $cp->shutdown( $workCallback, $mode, $cpIndex );
561  if ( $unsavedPositions && $workCallback ) {
562  // Invoke callback in case it did not cache the result yet
563  $workCallback(); // work now to block for less time in waitForAll()
564  }
565  // If the positions failed to write to the stash, at least wait on local datacenter
566  // replica DBs to catch up before responding. Even if there are several DCs, this increases
567  // the chance that the user will see their own changes immediately afterwards. As long
568  // as the sticky DC cookie applies (same domain), this is not even an issue.
569  $this->forEachLB( function ( ILoadBalancer $lb ) use ( $unsavedPositions ) {
570  $masterName = $lb->getServerName( $lb->getWriterIndex() );
571  if ( isset( $unsavedPositions[$masterName] ) ) {
572  $lb->waitForAll( $unsavedPositions[$masterName] );
573  }
574  } );
575  }
576 
581  final protected function baseLoadBalancerParams() {
582  if ( $this->trxRoundStage === self::ROUND_COMMIT_CALLBACKS ) {
583  $initStage = ILoadBalancer::STAGE_POSTCOMMIT_CALLBACKS;
584  } elseif ( $this->trxRoundStage === self::ROUND_ROLLBACK_CALLBACKS ) {
585  $initStage = ILoadBalancer::STAGE_POSTROLLBACK_CALLBACKS;
586  } else {
587  $initStage = null;
588  }
589 
590  return [
591  'localDomain' => $this->localDomain,
592  'readOnlyReason' => $this->readOnlyReason,
593  'srvCache' => $this->srvCache,
594  'wanCache' => $this->wanCache,
595  'profiler' => $this->profiler,
596  'trxProfiler' => $this->trxProfiler,
597  'queryLogger' => $this->queryLogger,
598  'connLogger' => $this->connLogger,
599  'replLogger' => $this->replLogger,
600  'errorLogger' => $this->errorLogger,
601  'deprecationLogger' => $this->deprecationLogger,
602  'hostname' => $this->hostname,
603  'cliMode' => $this->cliMode,
604  'agent' => $this->agent,
605  'maxLag' => $this->maxLag,
606  'defaultGroup' => $this->defaultGroup,
607  'chronologyCallback' => function ( ILoadBalancer $lb ) {
608  // Defer ChronologyProtector construction in case setRequestInfo() ends up
609  // being called later (but before the first connection attempt) (T192611)
610  $this->getChronologyProtector()->applySessionReplicationPosition( $lb );
611  },
612  'roundStage' => $initStage,
613  'ownerId' => $this->id
614  ];
615  }
616 
620  protected function initLoadBalancer( ILoadBalancer $lb ) {
621  if ( $this->trxRoundId !== false ) {
622  $lb->beginMasterChanges( $this->trxRoundId, $this->id ); // set DBO_TRX
623  }
624 
625  $lb->setTableAliases( $this->tableAliases );
626  $lb->setIndexAliases( $this->indexAliases );
627  }
628 
629  public function setTableAliases( array $aliases ) {
630  $this->tableAliases = $aliases;
631  }
632 
633  public function setIndexAliases( array $aliases ) {
634  $this->indexAliases = $aliases;
635  }
636 
641  public function setDomainPrefix( $prefix ) {
642  $this->setLocalDomainPrefix( $prefix );
643  }
644 
645  public function setLocalDomainPrefix( $prefix ) {
646  $this->localDomain = new DatabaseDomain(
647  $this->localDomain->getDatabase(),
648  $this->localDomain->getSchema(),
649  $prefix
650  );
651 
652  $this->forEachLB( function ( ILoadBalancer $lb ) use ( $prefix ) {
653  $lb->setLocalDomainPrefix( $prefix );
654  } );
655  }
656 
657  public function redefineLocalDomain( $domain ) {
658  $this->closeAll();
659 
660  $this->localDomain = DatabaseDomain::newFromId( $domain );
661 
662  $this->forEachLB( function ( ILoadBalancer $lb ) {
663  $lb->redefineLocalDomain( $this->localDomain );
664  } );
665  }
666 
667  public function closeAll() {
668  $this->forEachLBCallMethod( 'closeAll' );
669  }
670 
671  public function setAgentName( $agent ) {
672  $this->agent = $agent;
673  }
674 
675  public function appendShutdownCPIndexAsQuery( $url, $index ) {
676  $usedCluster = 0;
677  $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$usedCluster ) {
678  $usedCluster |= ( $lb->getServerCount() > 1 );
679  } );
680 
681  if ( !$usedCluster ) {
682  return $url; // no master/replica clusters touched
683  }
684 
685  return strpos( $url, '?' ) === false ? "$url?cpPosIndex=$index" : "$url&cpPosIndex=$index";
686  }
687 
688  public function getChronologyProtectorClientId() {
689  return $this->getChronologyProtector()->getClientId();
690  }
691 
699  public static function makeCookieValueFromCPIndex( $index, $time, $clientId ) {
700  return "$index@$time#$clientId";
701  }
702 
709  public static function getCPInfoFromCookieValue( $value, $minTimestamp ) {
710  static $placeholder = [ 'index' => null, 'clientId' => null ];
711 
712  if ( !preg_match( '/^(\d+)@(\d+)#([0-9a-f]{32})$/', $value, $m ) ) {
713  return $placeholder; // invalid
714  }
715 
716  $index = (int)$m[1];
717  if ( $index <= 0 ) {
718  return $placeholder; // invalid
719  } elseif ( isset( $m[2] ) && $m[2] !== '' && (int)$m[2] < $minTimestamp ) {
720  return $placeholder; // expired
721  }
722 
723  $clientId = ( isset( $m[3] ) && $m[3] !== '' ) ? $m[3] : null;
724 
725  return [ 'index' => $index, 'clientId' => $clientId ];
726  }
727 
728  public function setRequestInfo( array $info ) {
729  if ( $this->chronProt ) {
730  throw new LogicException( 'ChronologyProtector already initialized.' );
731  }
732 
733  $this->requestInfo = $info + $this->requestInfo;
734  }
735 
739  private function assertTransactionRoundStage( $stage ) {
740  if ( $this->trxRoundStage !== $stage ) {
741  throw new DBTransactionError(
742  null,
743  "Transaction round stage must be '$stage' (not '{$this->trxRoundStage}')"
744  );
745  }
746  }
747 
748  function __destruct() {
749  $this->destroy();
750  }
751 }
Helper class that detects high-contention DB queries via profiling calls.
disableChronologyProtection()
Disable the ChronologyProtector for all load balancers.
Definition: LBFactory.php:501
ChronologyProtector $chronProt
Definition: LBFactory.php:42
array $requestInfo
Web request information about the client.
Definition: LBFactory.php:73
flushReplicaSnapshots( $fname=__METHOD__)
Commit all replica DB transactions so as to flush any REPEATABLE-READ or SSI snapshot.
Definition: LBFactory.php:236
commitAll( $fname=__METHOD__, array $options=[])
Commit open transactions on all connections.
Definition: LBFactory.php:240
string bool $readOnlyReason
Reason all LBs are read-only or false if not.
Definition: LBFactory.php:98
shutdown(callable $workCallback=null, $mode='sync', &$cpIndex=null)
Notify the ChronologyProtector that the LBFactory is done calling shutdownLB() for now...
An interface for generating database load balancers.
Definition: LBFactory.php:40
LoggerInterface $replLogger
Definition: LBFactory.php:48
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:1982
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
setRequestInfo(array $info)
Definition: LBFactory.php:728
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging $e
Definition: hooks.txt:2159
hasTransactionRound()
Check if an explicit transaction round is active.
Definition: LBFactory.php:329
getChronologyProtectorClientId()
Get the client ID of the ChronologyProtector instance.
Definition: LBFactory.php:688
TransactionProfiler $trxProfiler
Definition: LBFactory.php:46
string $hostname
Local hostname of the app server.
Definition: LBFactory.php:71
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:175
$value
getLocalDomainID()
Get the local (and default) database domain ID of connection handles.
Definition: LBFactory.php:167
commitAndWaitForReplication( $fname, $ticket, array $opts=[])
Convenience method for safely running commitMasterChanges()/waitForReplication()
Definition: LBFactory.php:469
laggedReplicaUsed()
Detemine if any lagged replica DB connection was used.
Definition: LBFactory.php:369
DatabaseDomain $localDomain
Local domain.
Definition: LBFactory.php:68
redefineLocalDomain( $domain)
Close all connection and redefine the local domain for testing or schema creation.
__construct(array $conf)
Construct a manager of ILoadBalancer objects.
Definition: LBFactory.php:116
setLocalDomainPrefix( $prefix)
Set a new table prefix for the existing local domain ID for testing.
forEachLBCallMethod( $methodName, array $args=[])
Call a method of each tracked load balancer.
Definition: LBFactory.php:227
LoggerInterface $connLogger
Definition: LBFactory.php:50
isReadyForRoundOperations()
Check if transaction rounds can be started, committed, or rolled back right now.
Definition: LBFactory.php:333
see documentation in includes Linker php for Linker::makeImageLink & $time
Definition: hooks.txt:1799
commitMasterChanges( $fname=__METHOD__, array $options=[])
Commit changes and clear view snapshots on all master connections.
Definition: LBFactory.php:261
runMasterTransactionIdleCallbacks( $fname=__METHOD__, $owner=null)
Consume and run all pending post-COMMIT/ROLLBACK callbacks and commit dangling transactions.
LoggerInterface $perfLogger
Definition: LBFactory.php:54
waitForAll( $pos, $timeout=null)
Set the master wait position and wait for ALL replica DBs to catch up to it.
getServerCount()
Get the number of defined servers (not the number of open connections)
forEachLB( $callback, array $params=[])
Execute a function for each tracked load balancer The callback is called with the load balancer as th...
if( $line===false) $args
Definition: cdb.php:64
hasMasterChanges()
Whether there are pending changes or callbacks in a transaction by this thread.
getServerName( $i)
Get the host name or IP address of the server with the specified index.
static newEmpty()
Get an instance that wraps EmptyBagOStuff.
setIndexAliases(array $aliases)
Convert certain index names to alternative names before querying the DB.
string [] $indexAliases
Map of (index alias => index)
Definition: LBFactory.php:84
Helper class for mitigating DB replication lag in order to provide "session consistency".
hasMasterChanges()
Determine if any master connection has pending changes.
Definition: LBFactory.php:360
initLoadBalancer(ILoadBalancer $lb)
Definition: LBFactory.php:620
setTableAliases(array $aliases)
Make certain table names use their own database, schema, and table prefix when passed into SQL querie...
Definition: LBFactory.php:629
LoggerInterface $queryLogger
Definition: LBFactory.php:52
callable [] $replicationWaitCallbacks
Definition: LBFactory.php:86
string bool $trxRoundId
String if a requested DBO_TRX transaction round is active.
Definition: LBFactory.php:93
static makeCookieValueFromCPIndex( $index, $time, $clientId)
Definition: LBFactory.php:699
getEmptyTransactionTicket( $fname)
Get a token asserting that no transaction writes are active.
Definition: LBFactory.php:458
hasOrMadeRecentMasterChanges( $age=null)
Determine if any master connection has pending/written changes from this request. ...
Definition: LBFactory.php:378
laggedReplicaUsed()
Checks whether the database for generic connections this request was both:
storeSessionReplicationPosition(ILoadBalancer $lb)
Save the "session consistency" DB replication position for an end-of-life ILoadBalancer.
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:1982
getChronologyProtectorTouched( $dbName)
Definition: LBFactory.php:497
shutdownChronologyProtector(ChronologyProtector $cp, $workCallback, $mode, &$cpIndex=null)
Get and record all of the staged DB positions into persistent memory storage.
Definition: LBFactory.php:551
bool $cliMode
Whether this PHP instance is for a CLI script.
Definition: LBFactory.php:75
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that When $user is not null
Definition: hooks.txt:780
WANObjectCache $wanCache
Definition: LBFactory.php:65
beginMasterChanges( $fname=__METHOD__, $owner=null)
Flush any master transaction snapshots and set DBO_TRX (if DBO_DEFAULT is set)
appendShutdownCPIndexAsQuery( $url, $index)
Append ?cpPosIndex parameter to a URL for ChronologyProtector purposes if needed. ...
Definition: LBFactory.php:675
setWaitForReplicationListener( $name, callable $callback=null)
Add a callback to be run in every call to waitForReplication() before waiting.
Definition: LBFactory.php:450
int null $ticket
Ticket used to delegate transaction ownership.
Definition: LBFactory.php:91
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
Definition: distributors.txt:9
destroy()
Disables all load balancers.
Definition: LBFactory.php:162
if(defined( 'MW_SETUP_CALLBACK')) $fname
Customization point after all loading (constants, functions, classes, DefaultSettings, LocalSettings).
Definition: Setup.php:123
string $agent
Agent name for query profiling.
Definition: LBFactory.php:77
setIndexAliases(array $aliases)
Convert certain index names to alternative names before querying the DB.
Definition: LBFactory.php:633
waitForReplication(array $opts=[])
Waits for the replica DBs to catch up to the current master position.
Definition: LBFactory.php:386
hasOrMadeRecentMasterChanges( $age=null)
Check if this load balancer object had any recent or still pending writes issued against it by this P...
shutdown( $mode=self::SHUTDOWN_CHRONPROT_SYNC, callable $workCallback=null, &$cpIndex=null, &$cpClientId=null)
Prepare all tracked load balancers for shutdown.
Definition: LBFactory.php:175
$id
var int An identifier for this class instance
Definition: LBFactory.php:89
runMasterTransactionListenerCallbacks( $fname=__METHOD__, $owner=null)
Run all recurring post-COMMIT/ROLLBACK listener callbacks.
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:35
Database cluster connection, tracking, load balancing, and transaction manager interface.
callable $deprecationLogger
Deprecation logger.
Definition: LBFactory.php:58
An interface for generating database load balancers.
Definition: ILBFactory.php:33
object string $profiler
Class name or object With profileIn/profileOut methods.
Definition: LBFactory.php:44
Class to handle database/prefix specification for IDatabase domains.
static getCPInfoFromCookieValue( $value, $minTimestamp)
Definition: LBFactory.php:709
rollbackMasterChanges( $fname=__METHOD__)
Rollback changes on all master connections.
Definition: LBFactory.php:296
baseLoadBalancerParams()
Base parameters to ILoadBalancer::__construct()
Definition: LBFactory.php:581
string $secret
Secret string for HMAC hashing.
Definition: LBFactory.php:79
setTableAliases(array $aliases)
Make certain table names use their own database, schema, and table prefix when passed into SQL querie...
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:271
beginMasterChanges( $fname=__METHOD__)
Flush any master transaction snapshots and set DBO_TRX (if DBO_DEFAULT is set)
Definition: LBFactory.php:246
getMainLB( $domain=false)
newMainLB( $domain=false)
logIfMultiDbTransaction()
Log query info if multi DB transactions are going to be committed now.
Definition: LBFactory.php:340
callable $errorLogger
Error logger.
Definition: LBFactory.php:56
string $trxRoundStage
One of the ROUND_* class constants.
Definition: LBFactory.php:95
closeAll()
Close all open database connections on all open load balancers.
Definition: LBFactory.php:667
finalizeMasterChanges( $fname=__METHOD__, $owner=null)
Run pre-commit callbacks and defer execution of post-commit callbacks.
pendingMasterChangeCallers()
Get the list of callers that have pending master changes.
assertTransactionRoundStage( $stage)
Definition: LBFactory.php:739
redefineLocalDomain( $domain)
Close all connection and redefine the local domain for testing or schema creation.
Definition: LBFactory.php:657
setLocalDomainPrefix( $prefix)
Set a new table prefix for the existing local domain ID for testing.
Definition: LBFactory.php:645