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  if ( $this->trxRoundId !== false && $this->trxRoundId !== $fname ) {
238  $this->queryLogger->warning(
239  "$fname: transaction round '{$this->trxRoundId}' still running",
240  [ 'trace' => ( new RuntimeException() )->getTraceAsString() ]
241  );
242  }
243  $this->forEachLBCallMethod( 'flushReplicaSnapshots', [ $fname ] );
244  }
245 
246  final public function commitAll( $fname = __METHOD__, array $options = [] ) {
248  $this->forEachLBCallMethod( 'flushMasterSnapshots', [ $fname ] );
249  $this->forEachLBCallMethod( 'flushReplicaSnapshots', [ $fname ] );
250  }
251 
252  final public function beginMasterChanges( $fname = __METHOD__ ) {
253  $this->assertTransactionRoundStage( self::ROUND_CURSORY );
254  $this->trxRoundStage = self::ROUND_BEGINNING;
255  if ( $this->trxRoundId !== false ) {
256  throw new DBTransactionError(
257  null,
258  "$fname: transaction round '{$this->trxRoundId}' already started"
259  );
260  }
261  $this->trxRoundId = $fname;
262  // Set DBO_TRX flags on all appropriate DBs
263  $this->forEachLBCallMethod( 'beginMasterChanges', [ $fname, $this->id ] );
264  $this->trxRoundStage = self::ROUND_CURSORY;
265  }
266 
267  final public function commitMasterChanges( $fname = __METHOD__, array $options = [] ) {
268  $this->assertTransactionRoundStage( self::ROUND_CURSORY );
269  $this->trxRoundStage = self::ROUND_COMMITTING;
270  if ( $this->trxRoundId !== false && $this->trxRoundId !== $fname ) {
271  throw new DBTransactionError(
272  null,
273  "$fname: transaction round '{$this->trxRoundId}' still running"
274  );
275  }
277  $scope = ScopedCallback::newScopedIgnoreUserAbort(); // try to ignore client aborts
278  // Run pre-commit callbacks and suppress post-commit callbacks, aborting on failure
279  do {
280  $count = 0; // number of callbacks executed this iteration
281  $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$count, $fname ) {
282  $count += $lb->finalizeMasterChanges( $fname, $this->id );
283  } );
284  } while ( $count > 0 );
285  $this->trxRoundId = false;
286  // Perform pre-commit checks, aborting on failure
287  $this->forEachLBCallMethod( 'approveMasterChanges', [ $options, $fname, $this->id ] );
288  // Log the DBs and methods involved in multi-DB transactions
289  $this->logIfMultiDbTransaction();
290  // Actually perform the commit on all master DB connections and revert DBO_TRX
291  $this->forEachLBCallMethod( 'commitMasterChanges', [ $fname, $this->id ] );
292  // Run all post-commit callbacks in a separate step
293  $this->trxRoundStage = self::ROUND_COMMIT_CALLBACKS;
295  $this->trxRoundStage = self::ROUND_CURSORY;
296  // Throw any last post-commit callback error
297  if ( $e instanceof Exception ) {
298  throw $e;
299  }
300  }
301 
302  final public function rollbackMasterChanges( $fname = __METHOD__ ) {
303  $this->trxRoundStage = self::ROUND_ROLLING_BACK;
304  $this->trxRoundId = false;
305  // Actually perform the rollback on all master DB connections and revert DBO_TRX
306  $this->forEachLBCallMethod( 'rollbackMasterChanges', [ $fname, $this->id ] );
307  // Run all post-commit callbacks in a separate step
308  $this->trxRoundStage = self::ROUND_ROLLBACK_CALLBACKS;
310  $this->trxRoundStage = self::ROUND_CURSORY;
311  }
312 
316  private function executePostTransactionCallbacks() {
317  $fname = __METHOD__;
318  // Run all post-commit callbacks until new ones stop getting added
319  $e = null; // first callback exception
320  do {
321  $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$e, $fname ) {
322  $ex = $lb->runMasterTransactionIdleCallbacks( $fname, $this->id );
323  $e = $e ?: $ex;
324  } );
325  } while ( $this->hasMasterChanges() );
326  // Run all listener callbacks once
327  $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$e, $fname ) {
328  $ex = $lb->runMasterTransactionListenerCallbacks( $fname, $this->id );
329  $e = $e ?: $ex;
330  } );
331 
332  return $e;
333  }
334 
335  public function hasTransactionRound() {
336  return ( $this->trxRoundId !== false );
337  }
338 
339  public function isReadyForRoundOperations() {
340  return ( $this->trxRoundStage === self::ROUND_CURSORY );
341  }
342 
346  private function logIfMultiDbTransaction() {
347  $callersByDB = [];
348  $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$callersByDB ) {
349  $masterName = $lb->getServerName( $lb->getWriterIndex() );
350  $callers = $lb->pendingMasterChangeCallers();
351  if ( $callers ) {
352  $callersByDB[$masterName] = $callers;
353  }
354  } );
355 
356  if ( count( $callersByDB ) >= 2 ) {
357  $dbs = implode( ', ', array_keys( $callersByDB ) );
358  $msg = "Multi-DB transaction [{$dbs}]:\n";
359  foreach ( $callersByDB as $db => $callers ) {
360  $msg .= "$db: " . implode( '; ', $callers ) . "\n";
361  }
362  $this->queryLogger->info( $msg );
363  }
364  }
365 
366  public function hasMasterChanges() {
367  $ret = false;
368  $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$ret ) {
369  $ret = $ret || $lb->hasMasterChanges();
370  } );
371 
372  return $ret;
373  }
374 
375  public function laggedReplicaUsed() {
376  $ret = false;
377  $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$ret ) {
378  $ret = $ret || $lb->laggedReplicaUsed();
379  } );
380 
381  return $ret;
382  }
383 
384  public function hasOrMadeRecentMasterChanges( $age = null ) {
385  $ret = false;
386  $this->forEachLB( function ( ILoadBalancer $lb ) use ( $age, &$ret ) {
387  $ret = $ret || $lb->hasOrMadeRecentMasterChanges( $age );
388  } );
389  return $ret;
390  }
391 
392  public function waitForReplication( array $opts = [] ) {
393  $opts += [
394  'domain' => false,
395  'cluster' => false,
396  'timeout' => $this->cliMode ? 60 : 1,
397  'ifWritesSince' => null
398  ];
399 
400  if ( $opts['domain'] === false && isset( $opts['wiki'] ) ) {
401  $opts['domain'] = $opts['wiki']; // b/c
402  }
403 
404  // Figure out which clusters need to be checked
406  $lbs = [];
407  if ( $opts['cluster'] !== false ) {
408  $lbs[] = $this->getExternalLB( $opts['cluster'] );
409  } elseif ( $opts['domain'] !== false ) {
410  $lbs[] = $this->getMainLB( $opts['domain'] );
411  } else {
412  $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$lbs ) {
413  $lbs[] = $lb;
414  } );
415  if ( !$lbs ) {
416  return true; // nothing actually used
417  }
418  }
419 
420  // Get all the master positions of applicable DBs right now.
421  // This can be faster since waiting on one cluster reduces the
422  // time needed to wait on the next clusters.
423  $masterPositions = array_fill( 0, count( $lbs ), false );
424  foreach ( $lbs as $i => $lb ) {
425  if (
426  // No writes to wait on getting replicated
427  !$lb->hasMasterConnection() ||
428  // No replication; avoid getMasterPos() permissions errors (T29975)
429  !$lb->hasStreamingReplicaServers() ||
430  // No writes since the last replication wait
431  (
432  $opts['ifWritesSince'] &&
433  $lb->lastMasterChangeTimestamp() < $opts['ifWritesSince']
434  )
435  ) {
436  continue; // no need to wait
437  }
438 
439  $masterPositions[$i] = $lb->getMasterPos();
440  }
441 
442  // Run any listener callbacks *after* getting the DB positions. The more
443  // time spent in the callbacks, the less time is spent in waitForAll().
444  foreach ( $this->replicationWaitCallbacks as $callback ) {
445  $callback();
446  }
447 
448  $failed = [];
449  foreach ( $lbs as $i => $lb ) {
450  if ( $masterPositions[$i] ) {
451  // The RDBMS may not support getMasterPos()
452  if ( !$lb->waitForAll( $masterPositions[$i], $opts['timeout'] ) ) {
453  $failed[] = $lb->getServerName( $lb->getWriterIndex() );
454  }
455  }
456  }
457 
458  return !$failed;
459  }
460 
461  public function setWaitForReplicationListener( $name, callable $callback = null ) {
462  if ( $callback ) {
463  $this->replicationWaitCallbacks[$name] = $callback;
464  } else {
465  unset( $this->replicationWaitCallbacks[$name] );
466  }
467  }
468 
469  public function getEmptyTransactionTicket( $fname ) {
470  if ( $this->hasMasterChanges() ) {
471  $this->queryLogger->error(
472  __METHOD__ . ": $fname does not have outer scope",
473  [ 'trace' => ( new RuntimeException() )->getTraceAsString() ]
474  );
475 
476  return null;
477  }
478 
479  return $this->ticket;
480  }
481 
482  final public function commitAndWaitForReplication( $fname, $ticket, array $opts = [] ) {
483  if ( $ticket !== $this->ticket ) {
484  $this->perfLogger->error(
485  __METHOD__ . ": $fname does not have outer scope",
486  [ 'trace' => ( new RuntimeException() )->getTraceAsString() ]
487  );
488 
489  return false;
490  }
491 
492  // The transaction owner and any caller with the empty transaction ticket can commit
493  // so that getEmptyTransactionTicket() callers don't risk seeing DBTransactionError.
494  if ( $this->trxRoundId !== false && $fname !== $this->trxRoundId ) {
495  $this->queryLogger->info( "$fname: committing on behalf of {$this->trxRoundId}" );
496  $fnameEffective = $this->trxRoundId;
497  } else {
498  $fnameEffective = $fname;
499  }
500 
501  $this->commitMasterChanges( $fnameEffective );
502  $waitSucceeded = $this->waitForReplication( $opts );
503  // If a nested caller committed on behalf of $fname, start another empty $fname
504  // transaction, leaving the caller with the same empty transaction state as before.
505  if ( $fnameEffective !== $fname ) {
506  $this->beginMasterChanges( $fnameEffective );
507  }
508 
509  return $waitSucceeded;
510  }
511 
512  public function getChronologyProtectorTouched( $dbName ) {
513  return $this->getChronologyProtector()->getTouched( $dbName );
514  }
515 
516  public function disableChronologyProtection() {
517  $this->getChronologyProtector()->setEnabled( false );
518  }
519 
523  protected function getChronologyProtector() {
524  if ( $this->chronProt ) {
525  return $this->chronProt;
526  }
527 
528  $this->chronProt = new ChronologyProtector(
529  $this->memStash,
530  [
531  'ip' => $this->requestInfo['IPAddress'],
532  'agent' => $this->requestInfo['UserAgent'],
533  'clientId' => $this->requestInfo['ChronologyClientId'] ?: null
534  ],
535  $this->requestInfo['ChronologyPositionIndex'],
536  $this->secret
537  );
538  $this->chronProt->setLogger( $this->replLogger );
539 
540  if ( $this->cliMode ) {
541  $this->chronProt->setEnabled( false );
542  } elseif ( $this->requestInfo['ChronologyProtection'] === 'false' ) {
543  // Request opted out of using position wait logic. This is useful for requests
544  // done by the job queue or background ETL that do not have a meaningful session.
545  $this->chronProt->setWaitEnabled( false );
546  } elseif ( $this->memStash instanceof EmptyBagOStuff ) {
547  // No where to store any DB positions and wait for them to appear
548  $this->chronProt->setEnabled( false );
549  $this->replLogger->info( 'Cannot use ChronologyProtector with EmptyBagOStuff' );
550  }
551 
552  $this->replLogger->debug(
553  __METHOD__ . ': request info ' .
554  json_encode( $this->requestInfo, JSON_PRETTY_PRINT )
555  );
556 
557  return $this->chronProt;
558  }
559 
568  protected function shutdownChronologyProtector(
569  ChronologyProtector $cp, $workCallback, $mode, &$cpIndex = null
570  ) {
571  // Record all the master positions needed
572  $this->forEachLB( function ( ILoadBalancer $lb ) use ( $cp ) {
574  } );
575  // Write them to the persistent stash. Try to do something useful by running $work
576  // while ChronologyProtector waits for the stash write to replicate to all DCs.
577  $unsavedPositions = $cp->shutdown( $workCallback, $mode, $cpIndex );
578  if ( $unsavedPositions && $workCallback ) {
579  // Invoke callback in case it did not cache the result yet
580  $workCallback(); // work now to block for less time in waitForAll()
581  }
582  // If the positions failed to write to the stash, at least wait on local datacenter
583  // replica DBs to catch up before responding. Even if there are several DCs, this increases
584  // the chance that the user will see their own changes immediately afterwards. As long
585  // as the sticky DC cookie applies (same domain), this is not even an issue.
586  $this->forEachLB( function ( ILoadBalancer $lb ) use ( $unsavedPositions ) {
587  $masterName = $lb->getServerName( $lb->getWriterIndex() );
588  if ( isset( $unsavedPositions[$masterName] ) ) {
589  $lb->waitForAll( $unsavedPositions[$masterName] );
590  }
591  } );
592  }
593 
598  final protected function baseLoadBalancerParams() {
599  if ( $this->trxRoundStage === self::ROUND_COMMIT_CALLBACKS ) {
600  $initStage = ILoadBalancer::STAGE_POSTCOMMIT_CALLBACKS;
601  } elseif ( $this->trxRoundStage === self::ROUND_ROLLBACK_CALLBACKS ) {
602  $initStage = ILoadBalancer::STAGE_POSTROLLBACK_CALLBACKS;
603  } else {
604  $initStage = null;
605  }
606 
607  return [
608  'localDomain' => $this->localDomain,
609  'readOnlyReason' => $this->readOnlyReason,
610  'srvCache' => $this->srvCache,
611  'wanCache' => $this->wanCache,
612  'profiler' => $this->profiler,
613  'trxProfiler' => $this->trxProfiler,
614  'queryLogger' => $this->queryLogger,
615  'connLogger' => $this->connLogger,
616  'replLogger' => $this->replLogger,
617  'errorLogger' => $this->errorLogger,
618  'deprecationLogger' => $this->deprecationLogger,
619  'hostname' => $this->hostname,
620  'cliMode' => $this->cliMode,
621  'agent' => $this->agent,
622  'maxLag' => $this->maxLag,
623  'defaultGroup' => $this->defaultGroup,
624  'chronologyCallback' => function ( ILoadBalancer $lb ) {
625  // Defer ChronologyProtector construction in case setRequestInfo() ends up
626  // being called later (but before the first connection attempt) (T192611)
627  $this->getChronologyProtector()->applySessionReplicationPosition( $lb );
628  },
629  'roundStage' => $initStage,
630  'ownerId' => $this->id
631  ];
632  }
633 
637  protected function initLoadBalancer( ILoadBalancer $lb ) {
638  if ( $this->trxRoundId !== false ) {
639  $lb->beginMasterChanges( $this->trxRoundId, $this->id ); // set DBO_TRX
640  }
641 
642  $lb->setTableAliases( $this->tableAliases );
643  $lb->setIndexAliases( $this->indexAliases );
644  }
645 
646  public function setTableAliases( array $aliases ) {
647  $this->tableAliases = $aliases;
648  }
649 
650  public function setIndexAliases( array $aliases ) {
651  $this->indexAliases = $aliases;
652  }
653 
654  public function setLocalDomainPrefix( $prefix ) {
655  $this->localDomain = new DatabaseDomain(
656  $this->localDomain->getDatabase(),
657  $this->localDomain->getSchema(),
658  $prefix
659  );
660 
661  $this->forEachLB( function ( ILoadBalancer $lb ) use ( $prefix ) {
662  $lb->setLocalDomainPrefix( $prefix );
663  } );
664  }
665 
666  public function redefineLocalDomain( $domain ) {
667  $this->closeAll();
668 
669  $this->localDomain = DatabaseDomain::newFromId( $domain );
670 
671  $this->forEachLB( function ( ILoadBalancer $lb ) {
672  $lb->redefineLocalDomain( $this->localDomain );
673  } );
674  }
675 
676  public function closeAll() {
677  $this->forEachLBCallMethod( 'closeAll' );
678  }
679 
680  public function setAgentName( $agent ) {
681  $this->agent = $agent;
682  }
683 
684  public function appendShutdownCPIndexAsQuery( $url, $index ) {
685  $usedCluster = 0;
686  $this->forEachLB( function ( ILoadBalancer $lb ) use ( &$usedCluster ) {
687  $usedCluster |= $lb->hasStreamingReplicaServers();
688  } );
689 
690  if ( !$usedCluster ) {
691  return $url; // no master/replica clusters touched
692  }
693 
694  return strpos( $url, '?' ) === false ? "$url?cpPosIndex=$index" : "$url&cpPosIndex=$index";
695  }
696 
697  public function getChronologyProtectorClientId() {
698  return $this->getChronologyProtector()->getClientId();
699  }
700 
708  public static function makeCookieValueFromCPIndex( $index, $time, $clientId ) {
709  return "$index@$time#$clientId";
710  }
711 
718  public static function getCPInfoFromCookieValue( $value, $minTimestamp ) {
719  static $placeholder = [ 'index' => null, 'clientId' => null ];
720 
721  if ( !preg_match( '/^(\d+)@(\d+)#([0-9a-f]{32})$/', $value, $m ) ) {
722  return $placeholder; // invalid
723  }
724 
725  $index = (int)$m[1];
726  if ( $index <= 0 ) {
727  return $placeholder; // invalid
728  } elseif ( isset( $m[2] ) && $m[2] !== '' && (int)$m[2] < $minTimestamp ) {
729  return $placeholder; // expired
730  }
731 
732  $clientId = ( isset( $m[3] ) && $m[3] !== '' ) ? $m[3] : null;
733 
734  return [ 'index' => $index, 'clientId' => $clientId ];
735  }
736 
737  public function setRequestInfo( array $info ) {
738  if ( $this->chronProt ) {
739  throw new LogicException( 'ChronologyProtector already initialized' );
740  }
741 
742  $this->requestInfo = $info + $this->requestInfo;
743  }
744 
748  private function assertTransactionRoundStage( $stage ) {
749  if ( $this->trxRoundStage !== $stage ) {
750  throw new DBTransactionError(
751  null,
752  "Transaction round stage must be '$stage' (not '{$this->trxRoundStage}')"
753  );
754  }
755  }
756 
757  function __destruct() {
758  $this->destroy();
759  }
760 }
Helper class that detects high-contention DB queries via profiling calls.
disableChronologyProtection()
Disable the ChronologyProtector for all load balancers.
Definition: LBFactory.php:516
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:246
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:1972
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
setRequestInfo(array $info)
Definition: LBFactory.php:737
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging $e
Definition: hooks.txt:2147
hasTransactionRound()
Check if an explicit transaction round is active.
Definition: LBFactory.php:335
getChronologyProtectorClientId()
Get the client ID of the ChronologyProtector instance.
Definition: LBFactory.php:697
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:482
laggedReplicaUsed()
Detemine if any lagged replica DB connection was used.
Definition: LBFactory.php:375
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:339
see documentation in includes Linker php for Linker::makeImageLink & $time
Definition: hooks.txt:1781
commitMasterChanges( $fname=__METHOD__, array $options=[])
Commit changes and clear view snapshots on all master connections.
Definition: LBFactory.php:267
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.
forEachLB( $callback, array $params=[])
Execute a function for each currently tracked (instantiated) load balancer.
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:366
initLoadBalancer(ILoadBalancer $lb)
Definition: LBFactory.php:637
setTableAliases(array $aliases)
Make certain table names use their own database, schema, and table prefix when passed into SQL querie...
Definition: LBFactory.php:646
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:708
getEmptyTransactionTicket( $fname)
Get a token asserting that no transaction writes are active.
Definition: LBFactory.php:469
hasOrMadeRecentMasterChanges( $age=null)
Determine if any master connection has pending/written changes from this request. ...
Definition: LBFactory.php:384
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:1972
hasStreamingReplicaServers()
Whether any replica servers use streaming replication from the master server.
getChronologyProtectorTouched( $dbName)
Definition: LBFactory.php:512
shutdownChronologyProtector(ChronologyProtector $cp, $workCallback, $mode, &$cpIndex=null)
Get and record all of the staged DB positions into persistent memory storage.
Definition: LBFactory.php:568
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:767
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:684
setWaitForReplicationListener( $name, callable $callback=null)
Add a callback to be run in every call to waitForReplication() before waiting.
Definition: LBFactory.php:461
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:131
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:650
waitForReplication(array $opts=[])
Waits for the replica DBs to catch up to the current master position.
Definition: LBFactory.php:392
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 currently tracked (instantiated) 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
getWriterIndex()
Get the server index of the master server.
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:718
rollbackMasterChanges( $fname=__METHOD__)
Rollback changes on all master connections.
Definition: LBFactory.php:302
baseLoadBalancerParams()
Base parameters to ILoadBalancer::__construct()
Definition: LBFactory.php:598
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:252
getMainLB( $domain=false)
newMainLB( $domain=false)
logIfMultiDbTransaction()
Log query info if multi DB transactions are going to be committed now.
Definition: LBFactory.php:346
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:676
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:748
redefineLocalDomain( $domain)
Close all connection and redefine the local domain for testing or schema creation.
Definition: LBFactory.php:666
setLocalDomainPrefix( $prefix)
Set a new table prefix for the existing local domain ID for testing.
Definition: LBFactory.php:654