MediaWiki  1.31.0
LoadBalancer.php
Go to the documentation of this file.
1 <?php
22 namespace Wikimedia\Rdbms;
23 
24 use Psr\Log\LoggerInterface;
25 use Psr\Log\NullLogger;
26 use Wikimedia\ScopedCallback;
31 use InvalidArgumentException;
32 use RuntimeException;
33 use Exception;
34 
40 class LoadBalancer implements ILoadBalancer {
42  private $servers;
44  private $conns;
46  private $loads;
48  private $groupLoads;
50  private $allowLagged;
52  private $waitTimeout;
56  private $tableAliases = [];
58  private $indexAliases = [];
59 
61  private $loadMonitor;
65  private $srvCache;
67  private $wanCache;
69  protected $profiler;
71  protected $trxProfiler;
73  protected $replLogger;
75  protected $connLogger;
77  protected $queryLogger;
79  protected $perfLogger;
80 
84  private $readIndex;
86  private $waitForPos;
88  private $laggedReplicaMode = false;
90  private $allReplicasDownMode = false;
92  private $lastError = 'Unknown error';
94  private $readOnlyReason = false;
96  private $connsOpened = 0;
98  private $trxRoundId = false;
102  private $localDomain;
106  private $host;
108  protected $cliMode;
110  protected $agent;
111 
113  private $errorLogger;
116 
118  private $disabled = false;
120  private $connectionAttempted = false;
122  private $maxLag = self::MAX_LAG_DEFAULT;
123 
125  const CONN_HELD_WARN_THRESHOLD = 10;
126 
128  const MAX_LAG_DEFAULT = 10;
130  const MAX_WAIT_DEFAULT = 10;
132  const TTL_CACHE_READONLY = 5;
133 
134  const KEY_LOCAL = 'local';
135  const KEY_FOREIGN_FREE = 'foreignFree';
136  const KEY_FOREIGN_INUSE = 'foreignInUse';
137 
138  const KEY_LOCAL_NOROUND = 'localAutoCommit';
139  const KEY_FOREIGN_FREE_NOROUND = 'foreignFreeAutoCommit';
140  const KEY_FOREIGN_INUSE_NOROUND = 'foreignInUseAutoCommit';
141 
142  public function __construct( array $params ) {
143  if ( !isset( $params['servers'] ) ) {
144  throw new InvalidArgumentException( __CLASS__ . ': missing servers parameter' );
145  }
146  $this->servers = $params['servers'];
147  foreach ( $this->servers as $i => $server ) {
148  if ( $i == 0 ) {
149  $this->servers[$i]['master'] = true;
150  } else {
151  $this->servers[$i]['replica'] = true;
152  }
153  }
154 
155  $localDomain = isset( $params['localDomain'] )
156  ? DatabaseDomain::newFromId( $params['localDomain'] )
158  $this->setLocalDomain( $localDomain );
159 
160  $this->waitTimeout = isset( $params['waitTimeout'] )
161  ? $params['waitTimeout']
162  : self::MAX_WAIT_DEFAULT;
163 
164  $this->readIndex = -1;
165  $this->conns = [
166  // Connection were transaction rounds may be applied
167  self::KEY_LOCAL => [],
168  self::KEY_FOREIGN_INUSE => [],
169  self::KEY_FOREIGN_FREE => [],
170  // Auto-committing counterpart connections that ignore transaction rounds
171  self::KEY_LOCAL_NOROUND => [],
172  self::KEY_FOREIGN_INUSE_NOROUND => [],
173  self::KEY_FOREIGN_FREE_NOROUND => []
174  ];
175  $this->loads = [];
176  $this->waitForPos = false;
177  $this->allowLagged = false;
178 
179  if ( isset( $params['readOnlyReason'] ) && is_string( $params['readOnlyReason'] ) ) {
180  $this->readOnlyReason = $params['readOnlyReason'];
181  }
182 
183  if ( isset( $params['maxLag'] ) ) {
184  $this->maxLag = $params['maxLag'];
185  }
186 
187  if ( isset( $params['loadMonitor'] ) ) {
188  $this->loadMonitorConfig = $params['loadMonitor'];
189  } else {
190  $this->loadMonitorConfig = [ 'class' => 'LoadMonitorNull' ];
191  }
192  $this->loadMonitorConfig += [ 'lagWarnThreshold' => $this->maxLag ];
193 
194  foreach ( $params['servers'] as $i => $server ) {
195  $this->loads[$i] = $server['load'];
196  if ( isset( $server['groupLoads'] ) ) {
197  foreach ( $server['groupLoads'] as $group => $ratio ) {
198  if ( !isset( $this->groupLoads[$group] ) ) {
199  $this->groupLoads[$group] = [];
200  }
201  $this->groupLoads[$group][$i] = $ratio;
202  }
203  }
204  }
205 
206  if ( isset( $params['srvCache'] ) ) {
207  $this->srvCache = $params['srvCache'];
208  } else {
209  $this->srvCache = new EmptyBagOStuff();
210  }
211  if ( isset( $params['wanCache'] ) ) {
212  $this->wanCache = $params['wanCache'];
213  } else {
214  $this->wanCache = WANObjectCache::newEmpty();
215  }
216  $this->profiler = isset( $params['profiler'] ) ? $params['profiler'] : null;
217  if ( isset( $params['trxProfiler'] ) ) {
218  $this->trxProfiler = $params['trxProfiler'];
219  } else {
220  $this->trxProfiler = new TransactionProfiler();
221  }
222 
223  $this->errorLogger = isset( $params['errorLogger'] )
224  ? $params['errorLogger']
225  : function ( Exception $e ) {
226  trigger_error( get_class( $e ) . ': ' . $e->getMessage(), E_USER_WARNING );
227  };
228  $this->deprecationLogger = isset( $params['deprecationLogger'] )
229  ? $params['deprecationLogger']
230  : function ( $msg ) {
231  trigger_error( $msg, E_USER_DEPRECATED );
232  };
233 
234  foreach ( [ 'replLogger', 'connLogger', 'queryLogger', 'perfLogger' ] as $key ) {
235  $this->$key = isset( $params[$key] ) ? $params[$key] : new NullLogger();
236  }
237 
238  $this->host = isset( $params['hostname'] )
239  ? $params['hostname']
240  : ( gethostname() ?: 'unknown' );
241  $this->cliMode = isset( $params['cliMode'] )
242  ? $params['cliMode']
243  : ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' );
244  $this->agent = isset( $params['agent'] ) ? $params['agent'] : '';
245 
246  if ( isset( $params['chronologyCallback'] ) ) {
247  $this->chronologyCallback = $params['chronologyCallback'];
248  }
249  }
250 
258  public function getLocalDomainID() {
259  return $this->localDomain->getId();
260  }
261 
267  private function getLoadMonitor() {
268  if ( !isset( $this->loadMonitor ) ) {
269  $compat = [
270  'LoadMonitor' => LoadMonitor::class,
271  'LoadMonitorNull' => LoadMonitorNull::class,
272  'LoadMonitorMySQL' => LoadMonitorMySQL::class,
273  ];
274 
275  $class = $this->loadMonitorConfig['class'];
276  if ( isset( $compat[$class] ) ) {
277  $class = $compat[$class];
278  }
279 
280  $this->loadMonitor = new $class(
281  $this, $this->srvCache, $this->wanCache, $this->loadMonitorConfig );
282  $this->loadMonitor->setLogger( $this->replLogger );
283  }
284 
285  return $this->loadMonitor;
286  }
287 
294  private function getRandomNonLagged( array $loads, $domain = false, $maxLag = INF ) {
295  $lags = $this->getLagTimes( $domain );
296 
297  # Unset excessively lagged servers
298  foreach ( $lags as $i => $lag ) {
299  if ( $i != 0 ) {
300  # How much lag this server nominally is allowed to have
301  $maxServerLag = isset( $this->servers[$i]['max lag'] )
302  ? $this->servers[$i]['max lag']
303  : $this->maxLag; // default
304  # Constrain that futher by $maxLag argument
305  $maxServerLag = min( $maxServerLag, $maxLag );
306 
307  $host = $this->getServerName( $i );
308  if ( $lag === false && !is_infinite( $maxServerLag ) ) {
309  $this->replLogger->error(
310  __METHOD__ .
311  ": server {host} is not replicating?", [ 'host' => $host ] );
312  unset( $loads[$i] );
313  } elseif ( $lag > $maxServerLag ) {
314  $this->replLogger->info(
315  __METHOD__ .
316  ": server {host} has {lag} seconds of lag (>= {maxlag})",
317  [ 'host' => $host, 'lag' => $lag, 'maxlag' => $maxServerLag ]
318  );
319  unset( $loads[$i] );
320  }
321  }
322  }
323 
324  # Find out if all the replica DBs with non-zero load are lagged
325  $sum = 0;
326  foreach ( $loads as $load ) {
327  $sum += $load;
328  }
329  if ( $sum == 0 ) {
330  # No appropriate DB servers except maybe the master and some replica DBs with zero load
331  # Do NOT use the master
332  # Instead, this function will return false, triggering read-only mode,
333  # and a lagged replica DB will be used instead.
334  return false;
335  }
336 
337  if ( count( $loads ) == 0 ) {
338  return false;
339  }
340 
341  # Return a random representative of the remainder
342  return ArrayUtils::pickRandom( $loads );
343  }
344 
345  public function getReaderIndex( $group = false, $domain = false ) {
346  if ( count( $this->servers ) == 1 ) {
347  // Skip the load balancing if there's only one server
348  return $this->getWriterIndex();
349  } elseif ( $group === false && $this->readIndex >= 0 ) {
350  // Shortcut if the generic reader index was already cached
351  return $this->readIndex;
352  }
353 
354  if ( $group !== false ) {
355  // Use the server weight array for this load group
356  if ( isset( $this->groupLoads[$group] ) ) {
357  $loads = $this->groupLoads[$group];
358  } else {
359  // No loads for this group, return false and the caller can use some other group
360  $this->connLogger->info( __METHOD__ . ": no loads for group $group" );
361 
362  return false;
363  }
364  } else {
365  // Use the generic load group
367  }
368 
369  // Scale the configured load ratios according to each server's load and state
370  $this->getLoadMonitor()->scaleLoads( $loads, $domain );
371 
372  // Pick a server to use, accounting for weights, load, lag, and "waitForPos"
373  list( $i, $laggedReplicaMode ) = $this->pickReaderIndex( $loads, $domain );
374  if ( $i === false ) {
375  // Replica DB connection unsuccessful
376  return false;
377  }
378 
379  if ( $this->waitForPos && $i != $this->getWriterIndex() ) {
380  // Before any data queries are run, wait for the server to catch up to the
381  // specified position. This is used to improve session consistency. Note that
382  // when LoadBalancer::waitFor() sets "waitForPos", the waiting triggers here,
383  // so update laggedReplicaMode as needed for consistency.
384  if ( !$this->doWait( $i ) ) {
385  $laggedReplicaMode = true;
386  }
387  }
388 
389  if ( $this->readIndex <= 0 && $this->loads[$i] > 0 && $group === false ) {
390  // Cache the generic reader index for future ungrouped DB_REPLICA handles
391  $this->readIndex = $i;
392  // Record if the generic reader index is in "lagged replica DB" mode
393  if ( $laggedReplicaMode ) {
394  $this->laggedReplicaMode = true;
395  }
396  }
397 
398  $serverName = $this->getServerName( $i );
399  $this->connLogger->debug( __METHOD__ . ": using server $serverName for group '$group'" );
400 
401  return $i;
402  }
403 
409  private function pickReaderIndex( array $loads, $domain = false ) {
410  if ( !count( $loads ) ) {
411  throw new InvalidArgumentException( "Empty server array given to LoadBalancer" );
412  }
413 
415  $i = false;
417  $laggedReplicaMode = false;
418 
419  // Quickly look through the available servers for a server that meets criteria...
420  $currentLoads = $loads;
421  while ( count( $currentLoads ) ) {
422  if ( $this->allowLagged || $laggedReplicaMode ) {
423  $i = ArrayUtils::pickRandom( $currentLoads );
424  } else {
425  $i = false;
426  if ( $this->waitForPos && $this->waitForPos->asOfTime() ) {
427  // "chronologyCallback" sets "waitForPos" for session consistency.
428  // This triggers doWait() after connect, so it's especially good to
429  // avoid lagged servers so as to avoid excessive delay in that method.
430  $ago = microtime( true ) - $this->waitForPos->asOfTime();
431  // Aim for <= 1 second of waiting (being too picky can backfire)
432  $i = $this->getRandomNonLagged( $currentLoads, $domain, $ago + 1 );
433  }
434  if ( $i === false ) {
435  // Any server with less lag than it's 'max lag' param is preferable
436  $i = $this->getRandomNonLagged( $currentLoads, $domain );
437  }
438  if ( $i === false && count( $currentLoads ) != 0 ) {
439  // All replica DBs lagged. Switch to read-only mode
440  $this->replLogger->error(
441  __METHOD__ . ": all replica DBs lagged. Switch to read-only mode" );
442  $i = ArrayUtils::pickRandom( $currentLoads );
443  $laggedReplicaMode = true;
444  }
445  }
446 
447  if ( $i === false ) {
448  // pickRandom() returned false.
449  // This is permanent and means the configuration or the load monitor
450  // wants us to return false.
451  $this->connLogger->debug( __METHOD__ . ": pickRandom() returned false" );
452 
453  return [ false, false ];
454  }
455 
456  $serverName = $this->getServerName( $i );
457  $this->connLogger->debug( __METHOD__ . ": Using reader #$i: $serverName..." );
458 
459  $conn = $this->openConnection( $i, $domain );
460  if ( !$conn ) {
461  $this->connLogger->warning( __METHOD__ . ": Failed connecting to $i/$domain" );
462  unset( $currentLoads[$i] ); // avoid this server next iteration
463  $i = false;
464  continue;
465  }
466 
467  // Decrement reference counter, we are finished with this connection.
468  // It will be incremented for the caller later.
469  if ( $domain !== false ) {
470  $this->reuseConnection( $conn );
471  }
472 
473  // Return this server
474  break;
475  }
476 
477  // If all servers were down, quit now
478  if ( !count( $currentLoads ) ) {
479  $this->connLogger->error( __METHOD__ . ": all servers down" );
480  }
481 
482  return [ $i, $laggedReplicaMode ];
483  }
484 
485  public function waitFor( $pos ) {
486  $oldPos = $this->waitForPos;
487  try {
488  $this->waitForPos = $pos;
489  // If a generic reader connection was already established, then wait now
490  $i = $this->readIndex;
491  if ( $i > 0 ) {
492  if ( !$this->doWait( $i ) ) {
493  $this->laggedReplicaMode = true;
494  }
495  }
496  } finally {
497  // Restore the older position if it was higher since this is used for lag-protection
498  $this->setWaitForPositionIfHigher( $oldPos );
499  }
500  }
501 
502  public function waitForOne( $pos, $timeout = null ) {
503  $oldPos = $this->waitForPos;
504  try {
505  $this->waitForPos = $pos;
506 
507  $i = $this->readIndex;
508  if ( $i <= 0 ) {
509  // Pick a generic replica DB if there isn't one yet
510  $readLoads = $this->loads;
511  unset( $readLoads[$this->getWriterIndex()] ); // replica DBs only
512  $readLoads = array_filter( $readLoads ); // with non-zero load
513  $i = ArrayUtils::pickRandom( $readLoads );
514  }
515 
516  if ( $i > 0 ) {
517  $ok = $this->doWait( $i, true, $timeout );
518  } else {
519  $ok = true; // no applicable loads
520  }
521  } finally {
522  # Restore the old position, as this is not used for lag-protection but for throttling
523  $this->waitForPos = $oldPos;
524  }
525 
526  return $ok;
527  }
528 
529  public function waitForAll( $pos, $timeout = null ) {
530  $timeout = $timeout ?: $this->waitTimeout;
531 
532  $oldPos = $this->waitForPos;
533  try {
534  $this->waitForPos = $pos;
535  $serverCount = count( $this->servers );
536 
537  $ok = true;
538  for ( $i = 1; $i < $serverCount; $i++ ) {
539  if ( $this->loads[$i] > 0 ) {
540  $start = microtime( true );
541  $ok = $this->doWait( $i, true, $timeout ) && $ok;
542  $timeout -= intval( microtime( true ) - $start );
543  if ( $timeout <= 0 ) {
544  break; // timeout reached
545  }
546  }
547  }
548  } finally {
549  # Restore the old position, as this is not used for lag-protection but for throttling
550  $this->waitForPos = $oldPos;
551  }
552 
553  return $ok;
554  }
555 
559  private function setWaitForPositionIfHigher( $pos ) {
560  if ( !$pos ) {
561  return;
562  }
563 
564  if ( !$this->waitForPos || $pos->hasReached( $this->waitForPos ) ) {
565  $this->waitForPos = $pos;
566  }
567  }
568 
569  public function getAnyOpenConnection( $i, $flags = 0 ) {
570  $autocommit = ( ( $flags & self::CONN_TRX_AUTOCOMMIT ) == self::CONN_TRX_AUTOCOMMIT );
571  foreach ( $this->conns as $connsByServer ) {
572  if ( !isset( $connsByServer[$i] ) ) {
573  continue;
574  }
575 
576  foreach ( $connsByServer[$i] as $conn ) {
577  if ( !$autocommit || $conn->getLBInfo( 'autoCommitOnly' ) ) {
578  return $conn;
579  }
580  }
581  }
582 
583  return false;
584  }
585 
593  protected function doWait( $index, $open = false, $timeout = null ) {
594  $timeout = max( 1, intval( $timeout ?: $this->waitTimeout ) );
595 
596  // Check if we already know that the DB has reached this point
597  $server = $this->getServerName( $index );
598  $key = $this->srvCache->makeGlobalKey( __CLASS__, 'last-known-pos', $server, 'v1' );
600  $knownReachedPos = $this->srvCache->get( $key );
601  if (
602  $knownReachedPos instanceof DBMasterPos &&
603  $knownReachedPos->hasReached( $this->waitForPos )
604  ) {
605  $this->replLogger->debug(
606  __METHOD__ .
607  ': replica DB {dbserver} known to be caught up (pos >= $knownReachedPos).',
608  [ 'dbserver' => $server ]
609  );
610  return true;
611  }
612 
613  // Find a connection to wait on, creating one if needed and allowed
614  $close = false; // close the connection afterwards
615  $conn = $this->getAnyOpenConnection( $index );
616  if ( !$conn ) {
617  if ( !$open ) {
618  $this->replLogger->debug(
619  __METHOD__ . ': no connection open for {dbserver}',
620  [ 'dbserver' => $server ]
621  );
622 
623  return false;
624  } else {
625  $conn = $this->openConnection( $index, self::DOMAIN_ANY );
626  if ( !$conn ) {
627  $this->replLogger->warning(
628  __METHOD__ . ': failed to connect to {dbserver}',
629  [ 'dbserver' => $server ]
630  );
631 
632  return false;
633  }
634  // Avoid connection spam in waitForAll() when connections
635  // are made just for the sake of doing this lag check.
636  $close = true;
637  }
638  }
639 
640  $this->replLogger->info(
641  __METHOD__ .
642  ': waiting for replica DB {dbserver} to catch up...',
643  [ 'dbserver' => $server ]
644  );
645 
646  $result = $conn->masterPosWait( $this->waitForPos, $timeout );
647 
648  if ( $result === null ) {
649  $this->replLogger->warning(
650  __METHOD__ . ': Errored out waiting on {host} pos {pos}',
651  [
652  'host' => $server,
653  'pos' => $this->waitForPos,
654  'trace' => ( new RuntimeException() )->getTraceAsString()
655  ]
656  );
657  $ok = false;
658  } elseif ( $result == -1 ) {
659  $this->replLogger->warning(
660  __METHOD__ . ': Timed out waiting on {host} pos {pos}',
661  [
662  'host' => $server,
663  'pos' => $this->waitForPos,
664  'trace' => ( new RuntimeException() )->getTraceAsString()
665  ]
666  );
667  $ok = false;
668  } else {
669  $this->replLogger->debug( __METHOD__ . ": done waiting" );
670  $ok = true;
671  // Remember that the DB reached this point
672  $this->srvCache->set( $key, $this->waitForPos, BagOStuff::TTL_DAY );
673  }
674 
675  if ( $close ) {
676  $this->closeConnection( $conn );
677  }
678 
679  return $ok;
680  }
681 
682  public function getConnection( $i, $groups = [], $domain = false, $flags = 0 ) {
683  if ( $i === null || $i === false ) {
684  throw new InvalidArgumentException( 'Attempt to call ' . __METHOD__ .
685  ' with invalid server index' );
686  }
687 
688  if ( $this->localDomain->equals( $domain ) || $domain === $this->localDomainIdAlias ) {
689  $domain = false; // local connection requested
690  }
691 
692  if ( ( $flags & self::CONN_TRX_AUTOCOMMIT ) === self::CONN_TRX_AUTOCOMMIT ) {
693  // Assuming all servers are of the same type (or similar), which is overwhelmingly
694  // the case, use the master server information to get the attributes. The information
695  // for $i cannot be used since it might be DB_REPLICA, which might require connection
696  // attempts in order to be resolved into a real server index.
697  $attributes = $this->getServerAttributes( $this->getWriterIndex() );
698  if ( $attributes[Database::ATTR_DB_LEVEL_LOCKING] ) {
699  // Callers sometimes want to (a) escape REPEATABLE-READ stateness without locking
700  // rows (e.g. FOR UPDATE) or (b) make small commits during a larger transactions
701  // to reduce lock contention. None of these apply for sqlite and using separate
702  // connections just causes self-deadlocks.
703  $flags &= ~self::CONN_TRX_AUTOCOMMIT;
704  $this->connLogger->info( __METHOD__ .
705  ': ignoring CONN_TRX_AUTOCOMMIT to avoid deadlocks.' );
706  }
707  }
708 
709  $groups = ( $groups === false || $groups === [] )
710  ? [ false ] // check one "group": the generic pool
711  : (array)$groups;
712 
713  $masterOnly = ( $i == self::DB_MASTER || $i == $this->getWriterIndex() );
714  $oldConnsOpened = $this->connsOpened; // connections open now
715 
716  if ( $i == self::DB_MASTER ) {
717  $i = $this->getWriterIndex();
718  } elseif ( $i == self::DB_REPLICA ) {
719  # Try to find an available server in any the query groups (in order)
720  foreach ( $groups as $group ) {
721  $groupIndex = $this->getReaderIndex( $group, $domain );
722  if ( $groupIndex !== false ) {
723  $i = $groupIndex;
724  break;
725  }
726  }
727  }
728 
729  # Operation-based index
730  if ( $i == self::DB_REPLICA ) {
731  $this->lastError = 'Unknown error'; // reset error string
732  # Try the general server pool if $groups are unavailable.
733  $i = ( $groups === [ false ] )
734  ? false // don't bother with this if that is what was tried above
735  : $this->getReaderIndex( false, $domain );
736  # Couldn't find a working server in getReaderIndex()?
737  if ( $i === false ) {
738  $this->lastError = 'No working replica DB server: ' . $this->lastError;
739  // Throw an exception
740  $this->reportConnectionError();
741  return null; // not reached
742  }
743  }
744 
745  # Now we have an explicit index into the servers array
746  $conn = $this->openConnection( $i, $domain, $flags );
747  if ( !$conn ) {
748  // Throw an exception
749  $this->reportConnectionError();
750  return null; // not reached
751  }
752 
753  # Profile any new connections that happen
754  if ( $this->connsOpened > $oldConnsOpened ) {
755  $host = $conn->getServer();
756  $dbname = $conn->getDBname();
757  $this->trxProfiler->recordConnection( $host, $dbname, $masterOnly );
758  }
759 
760  if ( $masterOnly ) {
761  # Make master-requested DB handles inherit any read-only mode setting
762  $conn->setLBInfo( 'readOnlyReason', $this->getReadOnlyReason( $domain, $conn ) );
763  }
764 
765  return $conn;
766  }
767 
768  public function reuseConnection( IDatabase $conn ) {
769  $serverIndex = $conn->getLBInfo( 'serverIndex' );
770  $refCount = $conn->getLBInfo( 'foreignPoolRefCount' );
771  if ( $serverIndex === null || $refCount === null ) {
782  return;
783  } elseif ( $conn instanceof DBConnRef ) {
784  // DBConnRef already handles calling reuseConnection() and only passes the live
785  // Database instance to this method. Any caller passing in a DBConnRef is broken.
786  $this->connLogger->error(
787  __METHOD__ . ": got DBConnRef instance.\n" .
788  ( new RuntimeException() )->getTraceAsString() );
789 
790  return;
791  }
792 
793  if ( $this->disabled ) {
794  return; // DBConnRef handle probably survived longer than the LoadBalancer
795  }
796 
797  if ( $conn->getLBInfo( 'autoCommitOnly' ) ) {
798  $connFreeKey = self::KEY_FOREIGN_FREE_NOROUND;
799  $connInUseKey = self::KEY_FOREIGN_INUSE_NOROUND;
800  } else {
801  $connFreeKey = self::KEY_FOREIGN_FREE;
802  $connInUseKey = self::KEY_FOREIGN_INUSE;
803  }
804 
805  $domain = $conn->getDomainID();
806  if ( !isset( $this->conns[$connInUseKey][$serverIndex][$domain] ) ) {
807  throw new InvalidArgumentException( __METHOD__ .
808  ": connection $serverIndex/$domain not found; it may have already been freed." );
809  } elseif ( $this->conns[$connInUseKey][$serverIndex][$domain] !== $conn ) {
810  throw new InvalidArgumentException( __METHOD__ .
811  ": connection $serverIndex/$domain mismatched; it may have already been freed." );
812  }
813 
814  $conn->setLBInfo( 'foreignPoolRefCount', --$refCount );
815  if ( $refCount <= 0 ) {
816  $this->conns[$connFreeKey][$serverIndex][$domain] = $conn;
817  unset( $this->conns[$connInUseKey][$serverIndex][$domain] );
818  if ( !$this->conns[$connInUseKey][$serverIndex] ) {
819  unset( $this->conns[$connInUseKey][$serverIndex] ); // clean up
820  }
821  $this->connLogger->debug( __METHOD__ . ": freed connection $serverIndex/$domain" );
822  } else {
823  $this->connLogger->debug( __METHOD__ .
824  ": reference count for $serverIndex/$domain reduced to $refCount" );
825  }
826  }
827 
828  public function getConnectionRef( $db, $groups = [], $domain = false, $flags = 0 ) {
829  $domain = ( $domain !== false ) ? $domain : $this->localDomain;
830 
831  return new DBConnRef( $this, $this->getConnection( $db, $groups, $domain, $flags ) );
832  }
833 
834  public function getLazyConnectionRef( $db, $groups = [], $domain = false, $flags = 0 ) {
835  $domain = ( $domain !== false ) ? $domain : $this->localDomain;
836 
837  return new DBConnRef( $this, [ $db, $groups, $domain, $flags ] );
838  }
839 
840  public function getMaintenanceConnectionRef( $db, $groups = [], $domain = false, $flags = 0 ) {
841  $domain = ( $domain !== false ) ? $domain : $this->localDomain;
842 
843  return new MaintainableDBConnRef(
844  $this, $this->getConnection( $db, $groups, $domain, $flags ) );
845  }
846 
847  public function openConnection( $i, $domain = false, $flags = 0 ) {
848  if ( $this->localDomain->equals( $domain ) || $domain === $this->localDomainIdAlias ) {
849  $domain = false; // local connection requested
850  }
851 
852  if ( !$this->connectionAttempted && $this->chronologyCallback ) {
853  $this->connLogger->debug( __METHOD__ . ': calling initLB() before first connection.' );
854  // Load any "waitFor" positions before connecting so that doWait() is triggered
855  $this->connectionAttempted = true;
856  call_user_func( $this->chronologyCallback, $this );
857  }
858 
859  // Check if an auto-commit connection is being requested. If so, it will not reuse the
860  // main set of DB connections but rather its own pool since:
861  // a) those are usually set to implicitly use transaction rounds via DBO_TRX
862  // b) those must support the use of explicit transaction rounds via beginMasterChanges()
863  $autoCommit = ( ( $flags & self::CONN_TRX_AUTOCOMMIT ) == self::CONN_TRX_AUTOCOMMIT );
864 
865  if ( $domain !== false ) {
866  // Connection is to a foreign domain
867  $conn = $this->openForeignConnection( $i, $domain, $flags );
868  } else {
869  // Connection is to the local domain
870  $connKey = $autoCommit ? self::KEY_LOCAL_NOROUND : self::KEY_LOCAL;
871  if ( isset( $this->conns[$connKey][$i][0] ) ) {
872  $conn = $this->conns[$connKey][$i][0];
873  } else {
874  if ( !isset( $this->servers[$i] ) || !is_array( $this->servers[$i] ) ) {
875  throw new InvalidArgumentException( "No server with index '$i'." );
876  }
877  // Open a new connection
878  $server = $this->servers[$i];
879  $server['serverIndex'] = $i;
880  $server['autoCommitOnly'] = $autoCommit;
881  if ( $this->localDomain->getDatabase() !== null ) {
882  // Use the local domain table prefix if the local domain is specified
883  $server['tablePrefix'] = $this->localDomain->getTablePrefix();
884  }
885  $conn = $this->reallyOpenConnection( $server, $this->localDomain );
886  $host = $this->getServerName( $i );
887  if ( $conn->isOpen() ) {
888  $this->connLogger->debug(
889  __METHOD__ . ": connected to database $i at '$host'." );
890  $this->conns[$connKey][$i][0] = $conn;
891  } else {
892  $this->connLogger->warning(
893  __METHOD__ . ": failed to connect to database $i at '$host'." );
894  $this->errorConnection = $conn;
895  $conn = false;
896  }
897  }
898  }
899 
900  if ( $conn instanceof IDatabase && !$conn->isOpen() ) {
901  // Connection was made but later unrecoverably lost for some reason.
902  // Do not return a handle that will just throw exceptions on use,
903  // but let the calling code (e.g. getReaderIndex) try another server.
904  // See DatabaseMyslBase::ping() for how this can happen.
905  $this->errorConnection = $conn;
906  $conn = false;
907  }
908 
909  if ( $autoCommit && $conn instanceof IDatabase ) {
910  $conn->clearFlag( $conn::DBO_TRX ); // auto-commit mode
911  }
912 
913  return $conn;
914  }
915 
937  private function openForeignConnection( $i, $domain, $flags = 0 ) {
938  $domainInstance = DatabaseDomain::newFromId( $domain );
939  $dbName = $domainInstance->getDatabase();
940  $prefix = $domainInstance->getTablePrefix();
941  $autoCommit = ( ( $flags & self::CONN_TRX_AUTOCOMMIT ) == self::CONN_TRX_AUTOCOMMIT );
942 
943  if ( $autoCommit ) {
944  $connFreeKey = self::KEY_FOREIGN_FREE_NOROUND;
945  $connInUseKey = self::KEY_FOREIGN_INUSE_NOROUND;
946  } else {
947  $connFreeKey = self::KEY_FOREIGN_FREE;
948  $connInUseKey = self::KEY_FOREIGN_INUSE;
949  }
950 
951  if ( isset( $this->conns[$connInUseKey][$i][$domain] ) ) {
952  // Reuse an in-use connection for the same domain
953  $conn = $this->conns[$connInUseKey][$i][$domain];
954  $this->connLogger->debug( __METHOD__ . ": reusing connection $i/$domain" );
955  } elseif ( isset( $this->conns[$connFreeKey][$i][$domain] ) ) {
956  // Reuse a free connection for the same domain
957  $conn = $this->conns[$connFreeKey][$i][$domain];
958  unset( $this->conns[$connFreeKey][$i][$domain] );
959  $this->conns[$connInUseKey][$i][$domain] = $conn;
960  $this->connLogger->debug( __METHOD__ . ": reusing free connection $i/$domain" );
961  } elseif ( !empty( $this->conns[$connFreeKey][$i] ) ) {
962  // Reuse a free connection from another domain
963  $conn = reset( $this->conns[$connFreeKey][$i] );
964  $oldDomain = key( $this->conns[$connFreeKey][$i] );
965  if ( strlen( $dbName ) && !$conn->selectDB( $dbName ) ) {
966  $this->lastError = "Error selecting database '$dbName' on server " .
967  $conn->getServer() . " from client host {$this->host}";
968  $this->errorConnection = $conn;
969  $conn = false;
970  } else {
971  $conn->tablePrefix( $prefix );
972  unset( $this->conns[$connFreeKey][$i][$oldDomain] );
973  // Note that if $domain is an empty string, getDomainID() might not match it
974  $this->conns[$connInUseKey][$i][$conn->getDomainId()] = $conn;
975  $this->connLogger->debug( __METHOD__ .
976  ": reusing free connection from $oldDomain for $domain" );
977  }
978  } else {
979  if ( !isset( $this->servers[$i] ) || !is_array( $this->servers[$i] ) ) {
980  throw new InvalidArgumentException( "No server with index '$i'." );
981  }
982  // Open a new connection
983  $server = $this->servers[$i];
984  $server['serverIndex'] = $i;
985  $server['foreignPoolRefCount'] = 0;
986  $server['foreign'] = true;
987  $server['autoCommitOnly'] = $autoCommit;
988  $conn = $this->reallyOpenConnection( $server, $domainInstance );
989  if ( !$conn->isOpen() ) {
990  $this->connLogger->warning( __METHOD__ . ": connection error for $i/$domain" );
991  $this->errorConnection = $conn;
992  $conn = false;
993  } else {
994  $conn->tablePrefix( $prefix ); // as specified
995  // Note that if $domain is an empty string, getDomainID() might not match it
996  $this->conns[$connInUseKey][$i][$conn->getDomainID()] = $conn;
997  $this->connLogger->debug( __METHOD__ . ": opened new connection for $i/$domain" );
998  }
999  }
1000 
1001  // Increment reference count
1002  if ( $conn instanceof IDatabase ) {
1003  $refCount = $conn->getLBInfo( 'foreignPoolRefCount' );
1004  $conn->setLBInfo( 'foreignPoolRefCount', $refCount + 1 );
1005  }
1006 
1007  return $conn;
1008  }
1009 
1010  public function getServerAttributes( $i ) {
1012  $this->getServerType( $i ),
1013  isset( $this->servers[$i]['driver'] ) ? $this->servers[$i]['driver'] : null
1014  );
1015  }
1016 
1024  private function isOpen( $index ) {
1025  if ( !is_int( $index ) ) {
1026  return false;
1027  }
1028 
1029  return (bool)$this->getAnyOpenConnection( $index );
1030  }
1031 
1043  protected function reallyOpenConnection( array $server, DatabaseDomain $domainOverride ) {
1044  if ( $this->disabled ) {
1045  throw new DBAccessError();
1046  }
1047 
1048  // Handle $domainOverride being a specified or an unspecified domain
1049  if ( $domainOverride->getDatabase() === null ) {
1050  // Normally, an RDBMS requires a DB name specified on connection and the $server
1051  // configuration array is assumed to already specify an appropriate DB name.
1052  if ( $server['type'] === 'mysql' ) {
1053  // For MySQL, DATABASE and SCHEMA are synonyms, connections need not specify a DB,
1054  // and the DB name in $server might not exist due to legacy reasons (the default
1055  // domain used to ignore the local LB domain, even when mismatched).
1056  $server['dbname'] = null;
1057  }
1058  } else {
1059  $server['dbname'] = $domainOverride->getDatabase();
1060  $server['schema'] = $domainOverride->getSchema();
1061  }
1062 
1063  // Let the handle know what the cluster master is (e.g. "db1052")
1064  $masterName = $this->getServerName( $this->getWriterIndex() );
1065  $server['clusterMasterHost'] = $masterName;
1066 
1067  // Log when many connection are made on requests
1068  if ( ++$this->connsOpened >= self::CONN_HELD_WARN_THRESHOLD ) {
1069  $this->perfLogger->warning( __METHOD__ . ": " .
1070  "{$this->connsOpened}+ connections made (master=$masterName)" );
1071  }
1072 
1073  $server['srvCache'] = $this->srvCache;
1074  // Set loggers and profilers
1075  $server['connLogger'] = $this->connLogger;
1076  $server['queryLogger'] = $this->queryLogger;
1077  $server['errorLogger'] = $this->errorLogger;
1078  $server['deprecationLogger'] = $this->deprecationLogger;
1079  $server['profiler'] = $this->profiler;
1080  $server['trxProfiler'] = $this->trxProfiler;
1081  // Use the same agent and PHP mode for all DB handles
1082  $server['cliMode'] = $this->cliMode;
1083  $server['agent'] = $this->agent;
1084  // Use DBO_DEFAULT flags by default for LoadBalancer managed databases. Assume that the
1085  // application calls LoadBalancer::commitMasterChanges() before the PHP script completes.
1086  $server['flags'] = isset( $server['flags'] ) ? $server['flags'] : IDatabase::DBO_DEFAULT;
1087 
1088  // Create a live connection object
1089  try {
1090  $db = Database::factory( $server['type'], $server );
1091  } catch ( DBConnectionError $e ) {
1092  // FIXME: This is probably the ugliest thing I have ever done to
1093  // PHP. I'm half-expecting it to segfault, just out of disgust. -- TS
1094  $db = $e->db;
1095  }
1096 
1097  $db->setLBInfo( $server );
1098  $db->setLazyMasterHandle(
1099  $this->getLazyConnectionRef( self::DB_MASTER, [], $db->getDomainID() )
1100  );
1101  $db->setTableAliases( $this->tableAliases );
1102  $db->setIndexAliases( $this->indexAliases );
1103 
1104  if ( $server['serverIndex'] === $this->getWriterIndex() ) {
1105  if ( $this->trxRoundId !== false ) {
1106  $this->applyTransactionRoundFlags( $db );
1107  }
1108  foreach ( $this->trxRecurringCallbacks as $name => $callback ) {
1109  $db->setTransactionListener( $name, $callback );
1110  }
1111  }
1112 
1113  return $db;
1114  }
1115 
1119  private function reportConnectionError() {
1120  $conn = $this->errorConnection; // the connection which caused the error
1121  $context = [
1122  'method' => __METHOD__,
1123  'last_error' => $this->lastError,
1124  ];
1125 
1126  if ( $conn instanceof IDatabase ) {
1127  $context['db_server'] = $conn->getServer();
1128  $this->connLogger->warning(
1129  __METHOD__ . ": connection error: {last_error} ({db_server})",
1130  $context
1131  );
1132 
1133  // throws DBConnectionError
1134  $conn->reportConnectionError( "{$this->lastError} ({$context['db_server']})" );
1135  } else {
1136  // No last connection, probably due to all servers being too busy
1137  $this->connLogger->error(
1138  __METHOD__ .
1139  ": LB failure with no last connection. Connection error: {last_error}",
1140  $context
1141  );
1142 
1143  // If all servers were busy, "lastError" will contain something sensible
1144  throw new DBConnectionError( null, $this->lastError );
1145  }
1146  }
1147 
1148  public function getWriterIndex() {
1149  return 0;
1150  }
1151 
1152  public function haveIndex( $i ) {
1153  return array_key_exists( $i, $this->servers );
1154  }
1155 
1156  public function isNonZeroLoad( $i ) {
1157  return array_key_exists( $i, $this->servers ) && $this->loads[$i] != 0;
1158  }
1159 
1160  public function getServerCount() {
1161  return count( $this->servers );
1162  }
1163 
1164  public function getServerName( $i ) {
1165  if ( isset( $this->servers[$i]['hostName'] ) ) {
1166  $name = $this->servers[$i]['hostName'];
1167  } elseif ( isset( $this->servers[$i]['host'] ) ) {
1168  $name = $this->servers[$i]['host'];
1169  } else {
1170  $name = '';
1171  }
1172 
1173  return ( $name != '' ) ? $name : 'localhost';
1174  }
1175 
1176  public function getServerType( $i ) {
1177  return isset( $this->servers[$i]['type'] ) ? $this->servers[$i]['type'] : 'unknown';
1178  }
1179 
1180  public function getMasterPos() {
1181  # If this entire request was served from a replica DB without opening a connection to the
1182  # master (however unlikely that may be), then we can fetch the position from the replica DB.
1183  $masterConn = $this->getAnyOpenConnection( $this->getWriterIndex() );
1184  if ( !$masterConn ) {
1185  $serverCount = count( $this->servers );
1186  for ( $i = 1; $i < $serverCount; $i++ ) {
1187  $conn = $this->getAnyOpenConnection( $i );
1188  if ( $conn ) {
1189  return $conn->getReplicaPos();
1190  }
1191  }
1192  } else {
1193  return $masterConn->getMasterPos();
1194  }
1195 
1196  return false;
1197  }
1198 
1199  public function disable() {
1200  $this->closeAll();
1201  $this->disabled = true;
1202  }
1203 
1204  public function closeAll() {
1205  $this->forEachOpenConnection( function ( IDatabase $conn ) {
1206  $host = $conn->getServer();
1207  $this->connLogger->debug(
1208  __METHOD__ . ": closing connection to database '$host'." );
1209  $conn->close();
1210  } );
1211 
1212  $this->conns = [
1213  self::KEY_LOCAL => [],
1214  self::KEY_FOREIGN_INUSE => [],
1215  self::KEY_FOREIGN_FREE => [],
1216  self::KEY_LOCAL_NOROUND => [],
1217  self::KEY_FOREIGN_INUSE_NOROUND => [],
1218  self::KEY_FOREIGN_FREE_NOROUND => []
1219  ];
1220  $this->connsOpened = 0;
1221  }
1222 
1223  public function closeConnection( IDatabase $conn ) {
1224  $serverIndex = $conn->getLBInfo( 'serverIndex' );
1225  foreach ( $this->conns as $type => $connsByServer ) {
1226  if ( !isset( $connsByServer[$serverIndex] ) ) {
1227  continue;
1228  }
1229 
1230  foreach ( $connsByServer[$serverIndex] as $i => $trackedConn ) {
1231  if ( $conn === $trackedConn ) {
1232  $host = $this->getServerName( $i );
1233  $this->connLogger->debug(
1234  __METHOD__ . ": closing connection to database $i at '$host'." );
1235  unset( $this->conns[$type][$serverIndex][$i] );
1237  break 2;
1238  }
1239  }
1240  }
1241 
1242  $conn->close();
1243  }
1244 
1245  public function commitAll( $fname = __METHOD__ ) {
1246  $failures = [];
1247 
1248  $restore = ( $this->trxRoundId !== false );
1249  $this->trxRoundId = false;
1250  $this->forEachOpenConnection(
1251  function ( IDatabase $conn ) use ( $fname, $restore, &$failures ) {
1252  try {
1253  $conn->commit( $fname, $conn::FLUSHING_ALL_PEERS );
1254  } catch ( DBError $e ) {
1255  call_user_func( $this->errorLogger, $e );
1256  $failures[] = "{$conn->getServer()}: {$e->getMessage()}";
1257  }
1258  if ( $restore && $conn->getLBInfo( 'master' ) ) {
1259  $this->undoTransactionRoundFlags( $conn );
1260  }
1261  }
1262  );
1263 
1264  if ( $failures ) {
1265  throw new DBExpectedError(
1266  null,
1267  "Commit failed on server(s) " . implode( "\n", array_unique( $failures ) )
1268  );
1269  }
1270  }
1271 
1272  public function finalizeMasterChanges() {
1273  $this->forEachOpenMasterConnection( function ( Database $conn ) {
1274  // Any error should cause all DB transactions to be rolled back together
1275  $conn->setTrxEndCallbackSuppression( false );
1277  // Defer post-commit callbacks until COMMIT finishes for all DBs
1278  $conn->setTrxEndCallbackSuppression( true );
1279  } );
1280  }
1281 
1282  public function approveMasterChanges( array $options ) {
1283  $limit = isset( $options['maxWriteDuration'] ) ? $options['maxWriteDuration'] : 0;
1284  $this->forEachOpenMasterConnection( function ( IDatabase $conn ) use ( $limit ) {
1285  // If atomic sections or explicit transactions are still open, some caller must have
1286  // caught an exception but failed to properly rollback any changes. Detect that and
1287  // throw and error (causing rollback).
1288  if ( $conn->explicitTrxActive() ) {
1289  throw new DBTransactionError(
1290  $conn,
1291  "Explicit transaction still active. A caller may have caught an error."
1292  );
1293  }
1294  // Assert that the time to replicate the transaction will be sane.
1295  // If this fails, then all DB transactions will be rollback back together.
1296  $time = $conn->pendingWriteQueryDuration( $conn::ESTIMATE_DB_APPLY );
1297  if ( $limit > 0 && $time > $limit ) {
1298  throw new DBTransactionSizeError(
1299  $conn,
1300  "Transaction spent $time second(s) in writes, exceeding the limit of $limit.",
1301  [ $time, $limit ]
1302  );
1303  }
1304  // If a connection sits idle while slow queries execute on another, that connection
1305  // may end up dropped before the commit round is reached. Ping servers to detect this.
1306  if ( $conn->writesOrCallbacksPending() && !$conn->ping() ) {
1307  throw new DBTransactionError(
1308  $conn,
1309  "A connection to the {$conn->getDBname()} database was lost before commit."
1310  );
1311  }
1312  } );
1313  }
1314 
1315  public function beginMasterChanges( $fname = __METHOD__ ) {
1316  if ( $this->trxRoundId !== false ) {
1317  throw new DBTransactionError(
1318  null,
1319  "$fname: Transaction round '{$this->trxRoundId}' already started."
1320  );
1321  }
1322  $this->trxRoundId = $fname;
1323 
1324  $failures = [];
1326  function ( Database $conn ) use ( $fname, &$failures ) {
1327  $conn->setTrxEndCallbackSuppression( true );
1328  try {
1329  $conn->flushSnapshot( $fname );
1330  } catch ( DBError $e ) {
1331  call_user_func( $this->errorLogger, $e );
1332  $failures[] = "{$conn->getServer()}: {$e->getMessage()}";
1333  }
1334  $conn->setTrxEndCallbackSuppression( false );
1335  $this->applyTransactionRoundFlags( $conn );
1336  }
1337  );
1338 
1339  if ( $failures ) {
1340  throw new DBExpectedError(
1341  null,
1342  "$fname: Flush failed on server(s) " . implode( "\n", array_unique( $failures ) )
1343  );
1344  }
1345  }
1346 
1347  public function commitMasterChanges( $fname = __METHOD__ ) {
1348  $failures = [];
1349 
1351  $scope = $this->getScopedPHPBehaviorForCommit(); // try to ignore client aborts
1352 
1353  $restore = ( $this->trxRoundId !== false );
1354  $this->trxRoundId = false;
1356  function ( IDatabase $conn ) use ( $fname, $restore, &$failures ) {
1357  try {
1358  if ( $conn->writesOrCallbacksPending() ) {
1359  $conn->commit( $fname, $conn::FLUSHING_ALL_PEERS );
1360  } elseif ( $restore ) {
1361  $conn->flushSnapshot( $fname );
1362  }
1363  } catch ( DBError $e ) {
1364  call_user_func( $this->errorLogger, $e );
1365  $failures[] = "{$conn->getServer()}: {$e->getMessage()}";
1366  }
1367  if ( $restore ) {
1368  $this->undoTransactionRoundFlags( $conn );
1369  }
1370  }
1371  );
1372 
1373  if ( $failures ) {
1374  throw new DBExpectedError(
1375  null,
1376  "$fname: Commit failed on server(s) " . implode( "\n", array_unique( $failures ) )
1377  );
1378  }
1379  }
1380 
1381  public function runMasterPostTrxCallbacks( $type ) {
1382  $e = null; // first exception
1383  $this->forEachOpenMasterConnection( function ( Database $conn ) use ( $type, &$e ) {
1384  $conn->setTrxEndCallbackSuppression( false );
1385  // Callbacks run in AUTO-COMMIT mode, so make sure no transactions are pending...
1386  if ( $conn->writesPending() ) {
1387  // This happens if onTransactionIdle() callbacks write to *other* handles
1388  // (which already finished their callbacks). Let any callbacks run in the final
1389  // commitMasterChanges() in LBFactory::shutdown(), when the transaction is gone.
1390  $this->queryLogger->warning( __METHOD__ . ": found writes pending." );
1391  return;
1392  } elseif ( $conn->trxLevel() ) {
1393  // This happens for single-DB setups where DB_REPLICA uses the master DB,
1394  // thus leaving an implicit read-only transaction open at this point. It
1395  // also happens if onTransactionIdle() callbacks leave implicit transactions
1396  // open on *other* DBs (which is slightly improper). Let these COMMIT on the
1397  // next call to commitMasterChanges(), possibly in LBFactory::shutdown().
1398  return;
1399  }
1400  try {
1402  } catch ( Exception $ex ) {
1403  $e = $e ?: $ex;
1404  }
1405  try {
1407  } catch ( Exception $ex ) {
1408  $e = $e ?: $ex;
1409  }
1410  } );
1411 
1412  return $e;
1413  }
1414 
1415  public function rollbackMasterChanges( $fname = __METHOD__ ) {
1416  $restore = ( $this->trxRoundId !== false );
1417  $this->trxRoundId = false;
1419  function ( IDatabase $conn ) use ( $fname, $restore ) {
1420  $conn->rollback( $fname, $conn::FLUSHING_ALL_PEERS );
1421  if ( $restore ) {
1422  $this->undoTransactionRoundFlags( $conn );
1423  }
1424  }
1425  );
1426  }
1427 
1429  $this->forEachOpenMasterConnection( function ( Database $conn ) {
1430  $conn->setTrxEndCallbackSuppression( true );
1431  } );
1432  }
1433 
1443  private function applyTransactionRoundFlags( IDatabase $conn ) {
1444  if ( $conn->getLBInfo( 'autoCommitOnly' ) ) {
1445  return; // transaction rounds do not apply to these connections
1446  }
1447 
1448  if ( $conn->getFlag( $conn::DBO_DEFAULT ) ) {
1449  // DBO_TRX is controlled entirely by CLI mode presence with DBO_DEFAULT.
1450  // Force DBO_TRX even in CLI mode since a commit round is expected soon.
1451  $conn->setFlag( $conn::DBO_TRX, $conn::REMEMBER_PRIOR );
1452  }
1453 
1454  if ( $conn->getFlag( $conn::DBO_TRX ) ) {
1455  $conn->setLBInfo( 'trxRoundId', $this->trxRoundId );
1456  }
1457  }
1458 
1462  private function undoTransactionRoundFlags( IDatabase $conn ) {
1463  if ( $conn->getLBInfo( 'autoCommitOnly' ) ) {
1464  return; // transaction rounds do not apply to these connections
1465  }
1466 
1467  if ( $conn->getFlag( $conn::DBO_TRX ) ) {
1468  $conn->setLBInfo( 'trxRoundId', false );
1469  }
1470 
1471  if ( $conn->getFlag( $conn::DBO_DEFAULT ) ) {
1472  $conn->restoreFlags( $conn::RESTORE_PRIOR );
1473  }
1474  }
1475 
1476  public function flushReplicaSnapshots( $fname = __METHOD__ ) {
1477  $this->forEachOpenReplicaConnection( function ( IDatabase $conn ) {
1478  $conn->flushSnapshot( __METHOD__ );
1479  } );
1480  }
1481 
1482  public function hasMasterConnection() {
1483  return $this->isOpen( $this->getWriterIndex() );
1484  }
1485 
1486  public function hasMasterChanges() {
1487  $pending = 0;
1488  $this->forEachOpenMasterConnection( function ( IDatabase $conn ) use ( &$pending ) {
1489  $pending |= $conn->writesOrCallbacksPending();
1490  } );
1491 
1492  return (bool)$pending;
1493  }
1494 
1495  public function lastMasterChangeTimestamp() {
1496  $lastTime = false;
1497  $this->forEachOpenMasterConnection( function ( IDatabase $conn ) use ( &$lastTime ) {
1498  $lastTime = max( $lastTime, $conn->lastDoneWrites() );
1499  } );
1500 
1501  return $lastTime;
1502  }
1503 
1504  public function hasOrMadeRecentMasterChanges( $age = null ) {
1505  $age = ( $age === null ) ? $this->waitTimeout : $age;
1506 
1507  return ( $this->hasMasterChanges()
1508  || $this->lastMasterChangeTimestamp() > microtime( true ) - $age );
1509  }
1510 
1511  public function pendingMasterChangeCallers() {
1512  $fnames = [];
1513  $this->forEachOpenMasterConnection( function ( IDatabase $conn ) use ( &$fnames ) {
1514  $fnames = array_merge( $fnames, $conn->pendingWriteCallers() );
1515  } );
1516 
1517  return $fnames;
1518  }
1519 
1520  public function getLaggedReplicaMode( $domain = false ) {
1521  // No-op if there is only one DB (also avoids recursion)
1522  if ( !$this->laggedReplicaMode && $this->getServerCount() > 1 ) {
1523  try {
1524  // See if laggedReplicaMode gets set
1525  $conn = $this->getConnection( self::DB_REPLICA, false, $domain );
1526  $this->reuseConnection( $conn );
1527  } catch ( DBConnectionError $e ) {
1528  // Avoid expensive re-connect attempts and failures
1529  $this->allReplicasDownMode = true;
1530  $this->laggedReplicaMode = true;
1531  }
1532  }
1533 
1534  return $this->laggedReplicaMode;
1535  }
1536 
1542  public function getLaggedSlaveMode( $domain = false ) {
1543  return $this->getLaggedReplicaMode( $domain );
1544  }
1545 
1546  public function laggedReplicaUsed() {
1547  return $this->laggedReplicaMode;
1548  }
1549 
1555  public function laggedSlaveUsed() {
1556  return $this->laggedReplicaUsed();
1557  }
1558 
1559  public function getReadOnlyReason( $domain = false, IDatabase $conn = null ) {
1560  if ( $this->readOnlyReason !== false ) {
1561  return $this->readOnlyReason;
1562  } elseif ( $this->getLaggedReplicaMode( $domain ) ) {
1563  if ( $this->allReplicasDownMode ) {
1564  return 'The database has been automatically locked ' .
1565  'until the replica database servers become available';
1566  } else {
1567  return 'The database has been automatically locked ' .
1568  'while the replica database servers catch up to the master.';
1569  }
1570  } elseif ( $this->masterRunningReadOnly( $domain, $conn ) ) {
1571  return 'The database master is running in read-only mode.';
1572  }
1573 
1574  return false;
1575  }
1576 
1582  private function masterRunningReadOnly( $domain, IDatabase $conn = null ) {
1584  $masterServer = $this->getServerName( $this->getWriterIndex() );
1585 
1586  return (bool)$cache->getWithSetCallback(
1587  $cache->makeGlobalKey( __CLASS__, 'server-read-only', $masterServer ),
1588  self::TTL_CACHE_READONLY,
1589  function () use ( $domain, $conn ) {
1590  $old = $this->trxProfiler->setSilenced( true );
1591  try {
1592  $dbw = $conn ?: $this->getConnection( self::DB_MASTER, [], $domain );
1593  $readOnly = (int)$dbw->serverIsReadOnly();
1594  if ( !$conn ) {
1595  $this->reuseConnection( $dbw );
1596  }
1597  } catch ( DBError $e ) {
1598  $readOnly = 0;
1599  }
1600  $this->trxProfiler->setSilenced( $old );
1601  return $readOnly;
1602  },
1603  [ 'pcTTL' => $cache::TTL_PROC_LONG, 'busyValue' => 0 ]
1604  );
1605  }
1606 
1607  public function allowLagged( $mode = null ) {
1608  if ( $mode === null ) {
1609  return $this->allowLagged;
1610  }
1611  $this->allowLagged = $mode;
1612 
1613  return $this->allowLagged;
1614  }
1615 
1616  public function pingAll() {
1617  $success = true;
1618  $this->forEachOpenConnection( function ( IDatabase $conn ) use ( &$success ) {
1619  if ( !$conn->ping() ) {
1620  $success = false;
1621  }
1622  } );
1623 
1624  return $success;
1625  }
1626 
1627  public function forEachOpenConnection( $callback, array $params = [] ) {
1628  foreach ( $this->conns as $connsByServer ) {
1629  foreach ( $connsByServer as $serverConns ) {
1630  foreach ( $serverConns as $conn ) {
1631  $mergedParams = array_merge( [ $conn ], $params );
1632  call_user_func_array( $callback, $mergedParams );
1633  }
1634  }
1635  }
1636  }
1637 
1638  public function forEachOpenMasterConnection( $callback, array $params = [] ) {
1639  $masterIndex = $this->getWriterIndex();
1640  foreach ( $this->conns as $connsByServer ) {
1641  if ( isset( $connsByServer[$masterIndex] ) ) {
1643  foreach ( $connsByServer[$masterIndex] as $conn ) {
1644  $mergedParams = array_merge( [ $conn ], $params );
1645  call_user_func_array( $callback, $mergedParams );
1646  }
1647  }
1648  }
1649  }
1650 
1651  public function forEachOpenReplicaConnection( $callback, array $params = [] ) {
1652  foreach ( $this->conns as $connsByServer ) {
1653  foreach ( $connsByServer as $i => $serverConns ) {
1654  if ( $i === $this->getWriterIndex() ) {
1655  continue; // skip master
1656  }
1657  foreach ( $serverConns as $conn ) {
1658  $mergedParams = array_merge( [ $conn ], $params );
1659  call_user_func_array( $callback, $mergedParams );
1660  }
1661  }
1662  }
1663  }
1664 
1665  public function getMaxLag( $domain = false ) {
1666  $maxLag = -1;
1667  $host = '';
1668  $maxIndex = 0;
1669 
1670  if ( $this->getServerCount() <= 1 ) {
1671  return [ $host, $maxLag, $maxIndex ]; // no replication = no lag
1672  }
1673 
1674  $lagTimes = $this->getLagTimes( $domain );
1675  foreach ( $lagTimes as $i => $lag ) {
1676  if ( $this->loads[$i] > 0 && $lag > $maxLag ) {
1677  $maxLag = $lag;
1678  $host = $this->servers[$i]['host'];
1679  $maxIndex = $i;
1680  }
1681  }
1682 
1683  return [ $host, $maxLag, $maxIndex ];
1684  }
1685 
1686  public function getLagTimes( $domain = false ) {
1687  if ( $this->getServerCount() <= 1 ) {
1688  return [ $this->getWriterIndex() => 0 ]; // no replication = no lag
1689  }
1690 
1691  $knownLagTimes = []; // map of (server index => 0 seconds)
1692  $indexesWithLag = [];
1693  foreach ( $this->servers as $i => $server ) {
1694  if ( empty( $server['is static'] ) ) {
1695  $indexesWithLag[] = $i; // DB server might have replication lag
1696  } else {
1697  $knownLagTimes[$i] = 0; // DB server is a non-replicating and read-only archive
1698  }
1699  }
1700 
1701  return $this->getLoadMonitor()->getLagTimes( $indexesWithLag, $domain ) + $knownLagTimes;
1702  }
1703 
1704  public function safeGetLag( IDatabase $conn ) {
1705  if ( $this->getServerCount() <= 1 ) {
1706  return 0;
1707  } else {
1708  return $conn->getLag();
1709  }
1710  }
1711 
1718  public function safeWaitForMasterPos( IDatabase $conn, $pos = false, $timeout = null ) {
1719  $timeout = max( 1, $timeout ?: $this->waitTimeout );
1720 
1721  if ( $this->getServerCount() <= 1 || !$conn->getLBInfo( 'replica' ) ) {
1722  return true; // server is not a replica DB
1723  }
1724 
1725  if ( !$pos ) {
1726  // Get the current master position, opening a connection if needed
1727  $masterConn = $this->getAnyOpenConnection( $this->getWriterIndex() );
1728  if ( $masterConn ) {
1729  $pos = $masterConn->getMasterPos();
1730  } else {
1731  $masterConn = $this->openConnection( $this->getWriterIndex(), self::DOMAIN_ANY );
1732  $pos = $masterConn->getMasterPos();
1733  $this->closeConnection( $masterConn );
1734  }
1735  }
1736 
1737  if ( $pos instanceof DBMasterPos ) {
1738  $result = $conn->masterPosWait( $pos, $timeout );
1739  if ( $result == -1 || is_null( $result ) ) {
1740  $msg = __METHOD__ . ': timed out waiting on {host} pos {pos}';
1741  $this->replLogger->warning( $msg, [
1742  'host' => $conn->getServer(),
1743  'pos' => $pos,
1744  'trace' => ( new RuntimeException() )->getTraceAsString()
1745  ] );
1746  $ok = false;
1747  } else {
1748  $this->replLogger->debug( __METHOD__ . ': done waiting' );
1749  $ok = true;
1750  }
1751  } else {
1752  $ok = false; // something is misconfigured
1753  $this->replLogger->error(
1754  __METHOD__ . ': could not get master pos for {host}',
1755  [
1756  'host' => $conn->getServer(),
1757  'trace' => ( new RuntimeException() )->getTraceAsString()
1758  ]
1759  );
1760  }
1761 
1762  return $ok;
1763  }
1764 
1765  public function setTransactionListener( $name, callable $callback = null ) {
1766  if ( $callback ) {
1767  $this->trxRecurringCallbacks[$name] = $callback;
1768  } else {
1769  unset( $this->trxRecurringCallbacks[$name] );
1770  }
1772  function ( IDatabase $conn ) use ( $name, $callback ) {
1773  $conn->setTransactionListener( $name, $callback );
1774  }
1775  );
1776  }
1777 
1778  public function setTableAliases( array $aliases ) {
1779  $this->tableAliases = $aliases;
1780  }
1781 
1782  public function setIndexAliases( array $aliases ) {
1783  $this->indexAliases = $aliases;
1784  }
1785 
1786  public function setDomainPrefix( $prefix ) {
1787  // Find connections to explicit foreign domains still marked as in-use...
1788  $domainsInUse = [];
1789  $this->forEachOpenConnection( function ( IDatabase $conn ) use ( &$domainsInUse ) {
1790  // Once reuseConnection() is called on a handle, its reference count goes from 1 to 0.
1791  // Until then, it is still in use by the caller (explicitly or via DBConnRef scope).
1792  if ( $conn->getLBInfo( 'foreignPoolRefCount' ) > 0 ) {
1793  $domainsInUse[] = $conn->getDomainID();
1794  }
1795  } );
1796 
1797  // Do not switch connections to explicit foreign domains unless marked as safe
1798  if ( $domainsInUse ) {
1799  $domains = implode( ', ', $domainsInUse );
1800  throw new DBUnexpectedError( null,
1801  "Foreign domain connections are still in use ($domains)." );
1802  }
1803 
1804  $oldDomain = $this->localDomain->getId();
1805  $this->setLocalDomain( new DatabaseDomain(
1806  $this->localDomain->getDatabase(),
1807  $this->localDomain->getSchema(),
1808  $prefix
1809  ) );
1810 
1811  $this->forEachOpenConnection( function ( IDatabase $db ) use ( $prefix, $oldDomain ) {
1812  if ( !$db->getLBInfo( 'foreign' ) ) {
1813  $db->tablePrefix( $prefix );
1814  }
1815  } );
1816  }
1817 
1821  private function setLocalDomain( DatabaseDomain $domain ) {
1822  $this->localDomain = $domain;
1823  // In case a caller assumes that the domain ID is simply <db>-<prefix>, which is almost
1824  // always true, gracefully handle the case when they fail to account for escaping.
1825  if ( $this->localDomain->getTablePrefix() != '' ) {
1826  $this->localDomainIdAlias =
1827  $this->localDomain->getDatabase() . '-' . $this->localDomain->getTablePrefix();
1828  } else {
1829  $this->localDomainIdAlias = $this->localDomain->getDatabase();
1830  }
1831  }
1832 
1839  final protected function getScopedPHPBehaviorForCommit() {
1840  if ( PHP_SAPI != 'cli' ) { // https://bugs.php.net/bug.php?id=47540
1841  $old = ignore_user_abort( true ); // avoid half-finished operations
1842  return new ScopedCallback( function () use ( $old ) {
1843  ignore_user_abort( $old );
1844  } );
1845  }
1846 
1847  return null;
1848  }
1849 
1850  function __destruct() {
1851  // Avoid connection leaks for sanity
1852  $this->disable();
1853  }
1854 }
1855 
1856 class_alias( LoadBalancer::class, 'LoadBalancer' );
Wikimedia\Rdbms\LoadBalancer\$allowLagged
bool $allowLagged
Whether to disregard replica DB lag as a factor in replica DB selection.
Definition: LoadBalancer.php:50
Wikimedia\Rdbms\LoadBalancer\getServerType
getServerType( $i)
Get DB type of the server with the specified index.
Definition: LoadBalancer.php:1176
Wikimedia\Rdbms\LoadBalancer\setWaitForPositionIfHigher
setWaitForPositionIfHigher( $pos)
Definition: LoadBalancer.php:559
Wikimedia\Rdbms\DBTransactionSizeError
Definition: DBTransactionSizeError.php:27
Wikimedia\Rdbms\IDatabase\flushSnapshot
flushSnapshot( $fname=__METHOD__)
Commit any transaction but error out if writes or callbacks are pending.
Wikimedia\Rdbms\LoadBalancer\setDomainPrefix
setDomainPrefix( $prefix)
Set a new table prefix for the existing local domain ID for testing.
Definition: LoadBalancer.php:1786
Wikimedia\Rdbms\Database
Relational database abstraction object.
Definition: Database.php:48
Wikimedia\Rdbms\IDatabase\isOpen
isOpen()
Is a connection to the database open?
Wikimedia\Rdbms\LoadBalancer\getLazyConnectionRef
getLazyConnectionRef( $db, $groups=[], $domain=false, $flags=0)
Get a database connection handle reference without connecting yet.
Definition: LoadBalancer.php:834
Wikimedia\Rdbms\LoadBalancer\$trxProfiler
TransactionProfiler $trxProfiler
Definition: LoadBalancer.php:71
Wikimedia\Rdbms\LoadBalancer\isOpen
isOpen( $index)
Test if the specified index represents an open connection.
Definition: LoadBalancer.php:1024
Wikimedia\Rdbms\LoadBalancer\$trxRoundId
string bool $trxRoundId
String if a requested DBO_TRX transaction round is active.
Definition: LoadBalancer.php:98
false
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:187
Wikimedia\Rdbms\IDatabase\getServer
getServer()
Get the server hostname or IP address.
Wikimedia\Rdbms\LoadBalancer\hasOrMadeRecentMasterChanges
hasOrMadeRecentMasterChanges( $age=null)
Check if this load balancer object had any recent or still pending writes issued against it by this P...
Definition: LoadBalancer.php:1504
servers
storage can be distributed across multiple servers
Definition: memcached.txt:33
Wikimedia\Rdbms\Database\factory
static factory( $dbType, $p=[], $connect=self::NEW_CONNECTED)
Construct a Database subclass instance given a database type and parameters.
Definition: Database.php:422
$context
do that in ParserLimitReportFormat instead use this to modify the parameters of the image all existing parser cache entries will be invalid To avoid you ll need to handle that somehow(e.g. with the RejectParserCacheValue hook) because MediaWiki won 't do it for you. & $defaults also a ContextSource after deleting those rows but within the same transaction you ll probably need to make sure the header is varied on and they can depend only on the ResourceLoaderContext $context
Definition: hooks.txt:2604
Wikimedia\Rdbms\IDatabase\pendingWriteQueryDuration
pendingWriteQueryDuration( $type=self::ESTIMATE_TOTAL)
Get the time spend running write queries for this transaction.
Wikimedia\Rdbms\DatabaseDomain\newFromId
static newFromId( $domain)
Definition: DatabaseDomain.php:63
Wikimedia\Rdbms\IDatabase\tablePrefix
tablePrefix( $prefix=null)
Get/set the table prefix.
Wikimedia\Rdbms\LoadBalancer\getWriterIndex
getWriterIndex()
Definition: LoadBalancer.php:1148
EmptyBagOStuff
A BagOStuff object with no objects in it.
Definition: EmptyBagOStuff.php:29
Wikimedia\Rdbms\LoadBalancer\reallyOpenConnection
reallyOpenConnection(array $server, DatabaseDomain $domainOverride)
Open a new network connection to a server (uncached)
Definition: LoadBalancer.php:1043
Wikimedia\Rdbms\DBAccessError
Exception class for attempted DB access.
Definition: DBAccessError.php:28
Wikimedia\Rdbms\LoadBalancer\$waitTimeout
int $waitTimeout
Seconds to spend waiting on replica DB lag to resolve.
Definition: LoadBalancer.php:52
Wikimedia\Rdbms\LoadBalancer\undoTransactionRoundFlags
undoTransactionRoundFlags(IDatabase $conn)
Definition: LoadBalancer.php:1462
Wikimedia\Rdbms\ILoadMonitor
An interface for database load monitoring.
Definition: ILoadMonitor.php:35
captcha-old.count
count
Definition: captcha-old.py:249
Wikimedia\Rdbms\LoadBalancer\disable
disable()
Disable this load balancer.
Definition: LoadBalancer.php:1199
Wikimedia\Rdbms\LoadBalancer\getMasterPos
getMasterPos()
Get the current master position for chronology control purposes.
Definition: LoadBalancer.php:1180
Wikimedia\Rdbms\IDatabase\ping
ping(&$rtt=null)
Ping the server and try to reconnect if it there is no connection.
Wikimedia\Rdbms\LoadBalancer\$host
string $host
Current server name.
Definition: LoadBalancer.php:106
$result
The index of the header message $result[1]=The index of the body text message $result[2 through n]=Parameters passed to body text message. Please note the header message cannot receive/use parameters. 'ImportHandleLogItemXMLTag':When parsing a XML tag in a log item. Return false to stop further processing of the tag $reader:XMLReader object $logInfo:Array of information 'ImportHandlePageXMLTag':When parsing a XML tag in a page. Return false to stop further processing of the tag $reader:XMLReader object & $pageInfo:Array of information 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision. Return false to stop further processing of the tag $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information 'ImportHandleToplevelXMLTag':When parsing a top level XML tag. Return false to stop further processing of the tag $reader:XMLReader object 'ImportHandleUnknownUser':When a user doesn 't exist locally, this hook is called to give extensions an opportunity to auto-create it. If the auto-creation is successful, return false. $name:User name 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload. Return false to stop further processing of the tag $reader:XMLReader object $revisionInfo:Array of information 'ImportLogInterwikiLink':Hook to change the interwiki link used in log entries and edit summaries for transwiki imports. & $fullInterwikiPrefix:Interwiki prefix, may contain colons. & $pageTitle:String that contains page title. 'ImportSources':Called when reading from the $wgImportSources configuration variable. Can be used to lazy-load the import sources list. & $importSources:The value of $wgImportSources. Modify as necessary. See the comment in DefaultSettings.php for the detail of how to structure this array. 'InfoAction':When building information to display on the action=info page. $context:IContextSource object & $pageInfo:Array of information 'InitializeArticleMaybeRedirect':MediaWiki check to see if title is a redirect. & $title:Title object for the current page & $request:WebRequest & $ignoreRedirect:boolean to skip redirect check & $target:Title/string of redirect target & $article:Article object 'InternalParseBeforeLinks':during Parser 's internalParse method before links but after nowiki/noinclude/includeonly/onlyinclude and other processings. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InternalParseBeforeSanitize':during Parser 's internalParse method just before the parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/onlyinclude and other processings. Ideal for syntax-extensions after template/parser function execution which respect nowiki and HTML-comments. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InterwikiLoadPrefix':When resolving if a given prefix is an interwiki or not. Return true without providing an interwiki to continue interwiki search. $prefix:interwiki prefix we are looking for. & $iwData:output array describing the interwiki with keys iw_url, iw_local, iw_trans and optionally iw_api and iw_wikiid. 'InvalidateEmailComplete':Called after a user 's email has been invalidated successfully. $user:user(object) whose email is being invalidated 'IRCLineURL':When constructing the URL to use in an IRC notification. Callee may modify $url and $query, URL will be constructed as $url . $query & $url:URL to index.php & $query:Query string $rc:RecentChange object that triggered url generation 'IsFileCacheable':Override the result of Article::isFileCacheable()(if true) & $article:article(object) being checked 'IsTrustedProxy':Override the result of IP::isTrustedProxy() & $ip:IP being check & $result:Change this value to override the result of IP::isTrustedProxy() 'IsUploadAllowedFromUrl':Override the result of UploadFromUrl::isAllowedUrl() $url:URL used to upload from & $allowed:Boolean indicating if uploading is allowed for given URL 'isValidEmailAddr':Override the result of Sanitizer::validateEmail(), for instance to return false if the domain name doesn 't match your organization. $addr:The e-mail address entered by the user & $result:Set this and return false to override the internal checks 'isValidPassword':Override the result of User::isValidPassword() $password:The password entered by the user & $result:Set this and return false to override the internal checks $user:User the password is being validated for 'Language::getMessagesFileName':$code:The language code or the language we 're looking for a messages file for & $file:The messages file path, you can override this to change the location. 'LanguageGetMagic':DEPRECATED! Use $magicWords in a file listed in $wgExtensionMessagesFiles instead. Use this to define synonyms of magic words depending of the language & $magicExtensions:associative array of magic words synonyms $lang:language code(string) 'LanguageGetNamespaces':Provide custom ordering for namespaces or remove namespaces. Do not use this hook to add namespaces. Use CanonicalNamespaces for that. & $namespaces:Array of namespaces indexed by their numbers 'LanguageGetSpecialPageAliases':DEPRECATED! Use $specialPageAliases in a file listed in $wgExtensionMessagesFiles instead. Use to define aliases of special pages names depending of the language & $specialPageAliases:associative array of magic words synonyms $lang:language code(string) 'LanguageGetTranslatedLanguageNames':Provide translated language names. & $names:array of language code=> language name $code:language of the preferred translations 'LanguageLinks':Manipulate a page 's language links. This is called in various places to allow extensions to define the effective language links for a page. $title:The page 's Title. & $links:Array with elements of the form "language:title" in the order that they will be output. & $linkFlags:Associative array mapping prefixed links to arrays of flags. Currently unused, but planned to provide support for marking individual language links in the UI, e.g. for featured articles. 'LanguageSelector':Hook to change the language selector available on a page. $out:The output page. $cssClassName:CSS class name of the language selector. 'LinkBegin':DEPRECATED! Use HtmlPageLinkRendererBegin instead. Used when generating internal and interwiki links in Linker::link(), before processing starts. Return false to skip default processing and return $ret. See documentation for Linker::link() for details on the expected meanings of parameters. $skin:the Skin object $target:the Title that the link is pointing to & $html:the contents that the< a > tag should have(raw HTML) $result
Definition: hooks.txt:1985
Wikimedia\Rdbms\LoadBalancer\safeGetLag
safeGetLag(IDatabase $conn)
Get the lag in seconds for a given connection, or zero if this load balancer does not have replicatio...
Definition: LoadBalancer.php:1704
use
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
Definition: MIT-LICENSE.txt:10
Wikimedia\Rdbms\IDatabase\close
close()
Close the database connection.
Wikimedia\Rdbms
Definition: ChronologyProtector.php:24
Wikimedia\Rdbms\LoadBalancer\$loadMonitorConfig
array $loadMonitorConfig
The LoadMonitor configuration.
Definition: LoadBalancer.php:54
Wikimedia\Rdbms\LoadBalancer\getServerName
getServerName( $i)
Get the host name or IP address of the server with the specified index.
Definition: LoadBalancer.php:1164
Wikimedia\Rdbms\DBMasterPos
An object representing a master or replica DB position in a replicated setup.
Definition: DBMasterPos.php:12
$params
$params
Definition: styleTest.css.php:40
Wikimedia\Rdbms\LoadBalancer\forEachOpenMasterConnection
forEachOpenMasterConnection( $callback, array $params=[])
Call a function with each open connection object to a master.
Definition: LoadBalancer.php:1638
BagOStuff
interface is intended to be more or less compatible with the PHP memcached client.
Definition: BagOStuff.php:47
Wikimedia\Rdbms\LoadBalancer\$maxLag
int $maxLag
Definition: LoadBalancer.php:122
Wikimedia\Rdbms\LoadBalancer\getConnection
getConnection( $i, $groups=[], $domain=false, $flags=0)
Get a connection handle by server index.
Definition: LoadBalancer.php:682
Wikimedia\Rdbms\DatabaseDomain\getDatabase
getDatabase()
Definition: DatabaseDomain.php:116
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:302
Wikimedia\Rdbms\LoadBalancer\runMasterPostTrxCallbacks
runMasterPostTrxCallbacks( $type)
Issue all pending post-COMMIT/ROLLBACK callbacks.
Definition: LoadBalancer.php:1381
Wikimedia\Rdbms\LoadBalancer\getLagTimes
getLagTimes( $domain=false)
Get an estimate of replication lag (in seconds) for each server.
Definition: LoadBalancer.php:1686
$success
$success
Definition: NoLocalSettings.php:42
Wikimedia\Rdbms\IDatabase\getLag
getLag()
Get the amount of replication lag for this database server.
Wikimedia\Rdbms\LoadBalancer\flushReplicaSnapshots
flushReplicaSnapshots( $fname=__METHOD__)
Commit all replica DB transactions so as to flush any REPEATABLE-READ or SSI snapshot.
Definition: LoadBalancer.php:1476
Wikimedia\Rdbms\LoadBalancer\$perfLogger
LoggerInterface $perfLogger
Definition: LoadBalancer.php:79
Wikimedia\Rdbms\LoadBalancer\KEY_FOREIGN_INUSE
const KEY_FOREIGN_INUSE
Definition: LoadBalancer.php:136
Wikimedia\Rdbms\LoadBalancer\$replLogger
LoggerInterface $replLogger
Definition: LoadBalancer.php:73
Wikimedia\Rdbms\LoadBalancer\doWait
doWait( $index, $open=false, $timeout=null)
Wait for a given replica DB to catch up to the master pos stored in $this.
Definition: LoadBalancer.php:593
Wikimedia\Rdbms\DBError
Database error base class.
Definition: DBError.php:30
Wikimedia\Rdbms\LoadBalancer\hasMasterChanges
hasMasterChanges()
Determine if there are pending changes in a transaction by this thread.
Definition: LoadBalancer.php:1486
Wikimedia\Rdbms\LoadBalancer\getServerAttributes
getServerAttributes( $i)
Definition: LoadBalancer.php:1010
DBO_TRX
const DBO_TRX
Definition: defines.php:12
Wikimedia\Rdbms\LoadBalancer\KEY_FOREIGN_INUSE_NOROUND
const KEY_FOREIGN_INUSE_NOROUND
Definition: LoadBalancer.php:140
Wikimedia\Rdbms\LoadBalancer\getMaxLag
getMaxLag( $domain=false)
Get the hostname and lag time of the most-lagged replica DB.
Definition: LoadBalancer.php:1665
php
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
Wikimedia\Rdbms\IDatabase
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:38
Wikimedia\Rdbms\IDatabase\lastDoneWrites
lastDoneWrites()
Returns the last time the connection may have been used for write queries.
Wikimedia\Rdbms\LoadBalancer\$deprecationLogger
callable $deprecationLogger
Deprecation logger.
Definition: LoadBalancer.php:115
Wikimedia\Rdbms\IDatabase\commit
commit( $fname=__METHOD__, $flush='')
Commits a transaction previously started using begin().
Wikimedia\Rdbms\DatabaseDomain\newUnspecified
static newUnspecified()
Definition: DatabaseDomain.php:93
Wikimedia\Rdbms\LoadBalancer\$connectionAttempted
bool $connectionAttempted
Whether any connection has been attempted yet.
Definition: LoadBalancer.php:120
Wikimedia\Rdbms\LoadBalancer\allowLagged
allowLagged( $mode=null)
Disables/enables lag checks.
Definition: LoadBalancer.php:1607
key
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add in any and then calling but I prefer the flexibility This should also do the output encoding The system allocates a global one in $wgOut Title Represents the title of an and does all the work of translating among various forms such as plain database key
Definition: design.txt:25
WANObjectCache\newEmpty
static newEmpty()
Get an instance that wraps EmptyBagOStuff.
Definition: WANObjectCache.php:253
Wikimedia\Rdbms\Database\runOnTransactionIdleCallbacks
runOnTransactionIdleCallbacks( $trigger)
Actually run and consume any "on transaction idle/resolution" callbacks.
Definition: Database.php:3371
Wikimedia\Rdbms\MaintainableDBConnRef
Helper class to handle automatically marking connections as reusable (via RAII pattern) as well handl...
Definition: MaintainableDBConnRef.php:13
Wikimedia\Rdbms\LoadBalancer\setLocalDomain
setLocalDomain(DatabaseDomain $domain)
Definition: LoadBalancer.php:1821
Wikimedia\Rdbms\LoadBalancer\getReaderIndex
getReaderIndex( $group=false, $domain=false)
Get the index of the reader connection, which may be a replica DB.
Definition: LoadBalancer.php:345
Wikimedia\Rdbms\LoadBalancer\$trxRecurringCallbacks
array[] $trxRecurringCallbacks
Map of (name => callable)
Definition: LoadBalancer.php:100
Wikimedia\Rdbms\LoadBalancer\KEY_LOCAL_NOROUND
const KEY_LOCAL_NOROUND
Definition: LoadBalancer.php:138
Wikimedia\Rdbms\Database\runOnTransactionPreCommitCallbacks
runOnTransactionPreCommitCallbacks()
Actually run and consume any "on transaction pre-commit" callbacks.
Definition: Database.php:3421
Wikimedia\Rdbms\LoadBalancer\KEY_LOCAL
const KEY_LOCAL
Definition: LoadBalancer.php:134
Wikimedia\Rdbms\LoadBalancer\$groupLoads
array[] $groupLoads
Map of (group => server index => weight)
Definition: LoadBalancer.php:48
Wikimedia\Rdbms\LoadBalancer\KEY_FOREIGN_FREE
const KEY_FOREIGN_FREE
Definition: LoadBalancer.php:135
Wikimedia\Rdbms\LoadBalancer\getRandomNonLagged
getRandomNonLagged(array $loads, $domain=false, $maxLag=INF)
Definition: LoadBalancer.php:294
Wikimedia\Rdbms\LoadBalancer\getReadOnlyReason
getReadOnlyReason( $domain=false, IDatabase $conn=null)
Definition: LoadBalancer.php:1559
Wikimedia\Rdbms\LoadBalancer\pickReaderIndex
pickReaderIndex(array $loads, $domain=false)
Definition: LoadBalancer.php:409
Wikimedia\Rdbms\LoadBalancer\pendingMasterChangeCallers
pendingMasterChangeCallers()
Get the list of callers that have pending master changes.
Definition: LoadBalancer.php:1511
Wikimedia\Rdbms\Database\trxLevel
trxLevel()
Gets the current transaction level.
Definition: Database.php:577
Wikimedia\Rdbms\LoadBalancer\__destruct
__destruct()
Definition: LoadBalancer.php:1850
IExpiringStore\TTL_DAY
const TTL_DAY
Definition: IExpiringStore.php:36
Wikimedia\Rdbms\LoadBalancer\hasMasterConnection
hasMasterConnection()
Definition: LoadBalancer.php:1482
Wikimedia\Rdbms\LoadBalancer\suppressTransactionEndCallbacks
suppressTransactionEndCallbacks()
Suppress all pending post-COMMIT/ROLLBACK callbacks.
Definition: LoadBalancer.php:1428
Wikimedia\Rdbms\LoadBalancer\commitMasterChanges
commitMasterChanges( $fname=__METHOD__)
Issue COMMIT on all master connections where writes where done.
Definition: LoadBalancer.php:1347
Wikimedia\Rdbms\LoadBalancer\forEachOpenConnection
forEachOpenConnection( $callback, array $params=[])
Call a function with each open connection object.
Definition: LoadBalancer.php:1627
Wikimedia\Rdbms\LoadBalancer\$srvCache
BagOStuff $srvCache
Definition: LoadBalancer.php:65
Wikimedia\Rdbms\LoadBalancer\isNonZeroLoad
isNonZeroLoad( $i)
Returns true if the specified index is valid and has non-zero load.
Definition: LoadBalancer.php:1156
Wikimedia\Rdbms\LoadBalancer\__construct
__construct(array $params)
Construct a manager of IDatabase connection objects.
Definition: LoadBalancer.php:142
Wikimedia\Rdbms\LoadBalancer\applyTransactionRoundFlags
applyTransactionRoundFlags(IDatabase $conn)
Make all DB servers with DBO_DEFAULT/DBO_TRX set join the transaction round.
Definition: LoadBalancer.php:1443
Wikimedia\Rdbms\LoadBalancer\waitForOne
waitForOne( $pos, $timeout=null)
Set the master wait position and wait for a "generic" replica DB to catch up to it.
Definition: LoadBalancer.php:502
$time
see documentation in includes Linker php for Linker::makeImageLink & $time
Definition: hooks.txt:1795
Wikimedia\Rdbms\LoadBalancer\$errorLogger
callable $errorLogger
Exception logger.
Definition: LoadBalancer.php:113
Wikimedia\Rdbms\LoadBalancer\$loads
float[] $loads
Map of (server index => weight)
Definition: LoadBalancer.php:46
Wikimedia\Rdbms\LoadBalancer\pingAll
pingAll()
Definition: LoadBalancer.php:1616
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
Wikimedia\Rdbms\LoadBalancer\$localDomainIdAlias
string $localDomainIdAlias
Alternate ID string for the domain instead of DatabaseDomain::getId()
Definition: LoadBalancer.php:104
Wikimedia\Rdbms\LoadBalancer\$lastError
string $lastError
The last DB selection or connection error.
Definition: LoadBalancer.php:92
Wikimedia\Rdbms\LoadBalancer\$tableAliases
$tableAliases
Definition: LoadBalancer.php:56
Wikimedia\Rdbms\LoadBalancer\forEachOpenReplicaConnection
forEachOpenReplicaConnection( $callback, array $params=[])
Call a function with each open replica DB connection object.
Definition: LoadBalancer.php:1651
Wikimedia\Rdbms\LoadBalancer\beginMasterChanges
beginMasterChanges( $fname=__METHOD__)
Flush any master transaction snapshots and set DBO_TRX (if DBO_DEFAULT is set)
Definition: LoadBalancer.php:1315
DB_MASTER
const DB_MASTER
Definition: defines.php:26
Wikimedia\Rdbms\LoadBalancer\$connsOpened
int $connsOpened
Total connections opened.
Definition: LoadBalancer.php:96
list
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
Wikimedia\Rdbms\LoadBalancer\commitAll
commitAll( $fname=__METHOD__)
Commit transactions on all open connections.
Definition: LoadBalancer.php:1245
Wikimedia\Rdbms\LoadBalancer
Database connection, tracking, load balancing, and transaction manager for a cluster.
Definition: LoadBalancer.php:40
$fname
if(defined( 'MW_SETUP_CALLBACK')) $fname
Customization point after all loading (constants, functions, classes, DefaultSettings,...
Definition: Setup.php:112
Wikimedia\Rdbms\LoadBalancer\getScopedPHPBehaviorForCommit
getScopedPHPBehaviorForCommit()
Make PHP ignore user aborts/disconnects until the returned value leaves scope.
Definition: LoadBalancer.php:1839
Wikimedia\Rdbms\IDatabase\setLBInfo
setLBInfo( $name, $value=null)
Set the LB info array, or a member of it.
Wikimedia\Rdbms\LoadBalancer\$chronologyCallback
callable null $chronologyCallback
Callback to run before the first connection attempt.
Definition: LoadBalancer.php:63
$e
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging $e
Definition: hooks.txt:2163
Wikimedia\Rdbms\LoadBalancer\getLoadMonitor
getLoadMonitor()
Get a LoadMonitor instance.
Definition: LoadBalancer.php:267
Wikimedia\Rdbms\LoadBalancer\$servers
array[] $servers
Map of (server index => server config array)
Definition: LoadBalancer.php:42
Wikimedia\Rdbms\LoadBalancer\setTransactionListener
setTransactionListener( $name, callable $callback=null)
Set a callback via IDatabase::setTransactionListener() on all current and future master connections o...
Definition: LoadBalancer.php:1765
Wikimedia\Rdbms\IDatabase\explicitTrxActive
explicitTrxActive()
Wikimedia\Rdbms\LoadBalancer\laggedSlaveUsed
laggedSlaveUsed()
Definition: LoadBalancer.php:1555
Wikimedia\Rdbms\IDatabase\getDomainID
getDomainID()
Wikimedia\Rdbms\IDatabase\setTransactionListener
setTransactionListener( $name, callable $callback=null)
Run a callback each time any transaction commits or rolls back.
WANObjectCache
Multi-datacenter aware caching interface.
Definition: WANObjectCache.php:87
Wikimedia\Rdbms\LoadBalancer\getLocalDomainID
getLocalDomainID()
Get the local (and default) database domain ID of connection handles.
Definition: LoadBalancer.php:258
Wikimedia\Rdbms\IDatabase\rollback
rollback( $fname=__METHOD__, $flush='')
Rollback a transaction previously started using begin().
Wikimedia\Rdbms\Database\setTrxEndCallbackSuppression
setTrxEndCallbackSuppression( $suppress)
Whether to disable running of post-COMMIT/ROLLBACK callbacks.
Definition: Database.php:3358
Wikimedia\Rdbms\IDatabase\pendingWriteCallers
pendingWriteCallers()
Get the list of method names that did write queries for this transaction.
Wikimedia\Rdbms\LoadBalancer\safeWaitForMasterPos
safeWaitForMasterPos(IDatabase $conn, $pos=false, $timeout=null)
Definition: LoadBalancer.php:1718
Wikimedia\Rdbms\LoadBalancer\$allReplicasDownMode
bool $allReplicasDownMode
Whether the generic reader fell back to a lagged replica DB.
Definition: LoadBalancer.php:90
Wikimedia\Rdbms\LoadBalancer\$conns
Database[][][] $conns
Map of (connection category => server index => IDatabase[])
Definition: LoadBalancer.php:44
Wikimedia\Rdbms\LoadBalancer\rollbackMasterChanges
rollbackMasterChanges( $fname=__METHOD__)
Issue ROLLBACK only on master, only if queries were done on connection.
Definition: LoadBalancer.php:1415
Wikimedia\Rdbms\LoadBalancer\$profiler
object string $profiler
Class name or object With profileIn/profileOut methods.
Definition: LoadBalancer.php:69
Wikimedia\Rdbms\IDatabase\getFlag
getFlag( $flag)
Returns a boolean whether the flag $flag is set for this connection.
Wikimedia\Rdbms\LoadBalancer\getMaintenanceConnectionRef
getMaintenanceConnectionRef( $db, $groups=[], $domain=false, $flags=0)
Get a maintenance database connection handle reference for migrations and schema changes.
Definition: LoadBalancer.php:840
Wikimedia\Rdbms\DBUnexpectedError
Definition: DBUnexpectedError.php:27
Wikimedia\Rdbms\IDatabase\masterPosWait
masterPosWait(DBMasterPos $pos, $timeout)
Wait for the replica DB to catch up to a given master position.
Wikimedia\Rdbms\LoadBalancer\$indexAliases
string[] $indexAliases
Map of (index alias => index)
Definition: LoadBalancer.php:58
Wikimedia\Rdbms\LoadBalancer\approveMasterChanges
approveMasterChanges(array $options)
Perform all pre-commit checks for things like replication safety.
Definition: LoadBalancer.php:1282
Wikimedia\Rdbms\LoadBalancer\$readOnlyReason
string bool $readOnlyReason
Reason the LB is read-only or false if not.
Definition: LoadBalancer.php:94
Wikimedia\Rdbms\LoadBalancer\getConnectionRef
getConnectionRef( $db, $groups=[], $domain=false, $flags=0)
Get a database connection handle reference.
Definition: LoadBalancer.php:828
Wikimedia\Rdbms\DBConnRef
Helper class to handle automatically marking connections as reusable (via RAII pattern) as well handl...
Definition: DBConnRef.php:15
Wikimedia\Rdbms\LoadBalancer\masterRunningReadOnly
masterRunningReadOnly( $domain, IDatabase $conn=null)
Definition: LoadBalancer.php:1582
Wikimedia\Rdbms\LoadBalancer\setTableAliases
setTableAliases(array $aliases)
Make certain table names use their own database, schema, and table prefix when passed into SQL querie...
Definition: LoadBalancer.php:1778
Wikimedia\Rdbms\DBTransactionError
Definition: DBTransactionError.php:27
Wikimedia\Rdbms\LoadBalancer\$localDomain
DatabaseDomain $localDomain
Local Domain ID and default for selectDB() calls.
Definition: LoadBalancer.php:102
$cache
$cache
Definition: mcc.php:33
$options
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition: hooks.txt:1987
Wikimedia\Rdbms\LoadBalancer\$waitForPos
bool DBMasterPos $waitForPos
False if not set.
Definition: LoadBalancer.php:86
Wikimedia\Rdbms\DBExpectedError
Base class for the more common types of database errors.
Definition: DBExpectedError.php:32
ArrayUtils\pickRandom
static pickRandom( $weights)
Given an array of non-normalised probabilities, this function will select an element and return the a...
Definition: ArrayUtils.php:66
Wikimedia\Rdbms\LoadBalancer\getAnyOpenConnection
getAnyOpenConnection( $i, $flags=0)
Get any open connection to a given server index, local or foreign.
Definition: LoadBalancer.php:569
ArrayUtils
A collection of static methods to play with arrays.
Definition: ArrayUtils.php:28
Wikimedia\Rdbms\LoadBalancer\$disabled
bool $disabled
Definition: LoadBalancer.php:118
Wikimedia\Rdbms\LoadBalancer\getLaggedReplicaMode
getLaggedReplicaMode( $domain=false)
Definition: LoadBalancer.php:1520
Wikimedia\Rdbms\Database\flushSnapshot
flushSnapshot( $fname=__METHOD__)
Commit any transaction but error out if writes or callbacks are pending.
Definition: Database.php:3875
Wikimedia\Rdbms\Database\writesPending
writesPending()
Definition: Database.php:667
Wikimedia\Rdbms\DatabaseDomain\getSchema
getSchema()
Definition: DatabaseDomain.php:123
as
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
Wikimedia\Rdbms\Database\runTransactionListenerCallbacks
runTransactionListenerCallbacks( $trigger)
Actually run any "transaction listener" callbacks.
Definition: Database.php:3451
Wikimedia\Rdbms\LoadBalancer\$errorConnection
Database $errorConnection
DB connection object that caused a problem.
Definition: LoadBalancer.php:82
Wikimedia\Rdbms\DBConnectionError
Definition: DBConnectionError.php:26
Wikimedia\Rdbms\LoadBalancer\finalizeMasterChanges
finalizeMasterChanges()
Perform all pre-commit callbacks that remain part of the atomic transactions and disable any post-com...
Definition: LoadBalancer.php:1272
Wikimedia\Rdbms\IDatabase\getLBInfo
getLBInfo( $name=null)
Get properties passed down from the server info array of the load balancer.
Wikimedia\Rdbms\LoadBalancer\haveIndex
haveIndex( $i)
Returns true if the specified index is a valid server index.
Definition: LoadBalancer.php:1152
Wikimedia\Rdbms\DBMasterPos\hasReached
hasReached(DBMasterPos $pos)
Wikimedia\Rdbms\Database\attributesFromType
static attributesFromType( $dbType, $driver=null)
Definition: Database.php:479
Wikimedia\Rdbms\LoadBalancer\getLaggedSlaveMode
getLaggedSlaveMode( $domain=false)
Definition: LoadBalancer.php:1542
Wikimedia\Rdbms\LoadBalancer\reuseConnection
reuseConnection(IDatabase $conn)
Mark a foreign connection as being available for reuse under a different DB domain.
Definition: LoadBalancer.php:768
class
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:52
Wikimedia\Rdbms\LoadBalancer\setIndexAliases
setIndexAliases(array $aliases)
Convert certain index names to alternative names before querying the DB.
Definition: LoadBalancer.php:1782
Wikimedia\Rdbms\LoadBalancer\laggedReplicaUsed
laggedReplicaUsed()
Checks whether the database for generic connections this request was both:
Definition: LoadBalancer.php:1546
Wikimedia\Rdbms\LoadBalancer\$readIndex
int $readIndex
The generic (not query grouped) replica DB index (of $mServers)
Definition: LoadBalancer.php:84
Wikimedia\Rdbms\LoadBalancer\$wanCache
WANObjectCache $wanCache
Definition: LoadBalancer.php:67
Wikimedia\Rdbms\LoadBalancer\$agent
string $agent
Agent name for query profiling.
Definition: LoadBalancer.php:110
Wikimedia\Rdbms\DatabaseDomain
Class to handle database/prefix specification for IDatabase domains.
Definition: DatabaseDomain.php:28
Wikimedia\Rdbms\TransactionProfiler
Helper class that detects high-contention DB queries via profiling calls.
Definition: TransactionProfiler.php:38
Wikimedia\Rdbms\LoadBalancer\$laggedReplicaMode
bool $laggedReplicaMode
Whether the generic reader fell back to a lagged replica DB.
Definition: LoadBalancer.php:88
Wikimedia\Rdbms\LoadBalancer\$queryLogger
LoggerInterface $queryLogger
Definition: LoadBalancer.php:77
Wikimedia\Rdbms\LoadBalancer\$loadMonitor
ILoadMonitor $loadMonitor
Definition: LoadBalancer.php:61
Wikimedia\Rdbms\LoadBalancer\openConnection
openConnection( $i, $domain=false, $flags=0)
Open a connection to the server given by the specified index.
Definition: LoadBalancer.php:847
DBO_DEFAULT
const DBO_DEFAULT
Definition: defines.php:13
Wikimedia\Rdbms\IDatabase\setFlag
setFlag( $flag, $remember=self::REMEMBER_NOTHING)
Set a flag for this connection.
Wikimedia\Rdbms\LoadBalancer\waitForAll
waitForAll( $pos, $timeout=null)
Set the master wait position and wait for ALL replica DBs to catch up to it.
Definition: LoadBalancer.php:529
Wikimedia\Rdbms\LoadBalancer\closeAll
closeAll()
Close all open connections.
Definition: LoadBalancer.php:1204
Wikimedia\Rdbms\LoadBalancer\$connLogger
LoggerInterface $connLogger
Definition: LoadBalancer.php:75
Wikimedia\Rdbms\IDatabase\restoreFlags
restoreFlags( $state=self::RESTORE_PRIOR)
Restore the flags to their prior state before the last setFlag/clearFlag call.
Wikimedia\Rdbms\LoadBalancer\waitFor
waitFor( $pos)
Set the master wait position.
Definition: LoadBalancer.php:485
Wikimedia\Rdbms\LoadBalancer\openForeignConnection
openForeignConnection( $i, $domain, $flags=0)
Open a connection to a foreign DB, or return one if it is already open.
Definition: LoadBalancer.php:937
Wikimedia\Rdbms\LoadBalancer\lastMasterChangeTimestamp
lastMasterChangeTimestamp()
Get the timestamp of the latest write query done by this thread.
Definition: LoadBalancer.php:1495
Wikimedia\Rdbms\LoadBalancer\$cliMode
bool $cliMode
Whether this PHP instance is for a CLI script.
Definition: LoadBalancer.php:108
Wikimedia\Rdbms\ILoadBalancer
Database cluster connection, tracking, load balancing, and transaction manager interface.
Definition: ILoadBalancer.php:78
Wikimedia\Rdbms\LoadBalancer\getServerCount
getServerCount()
Get the number of defined servers (not the number of open connections)
Definition: LoadBalancer.php:1160
Wikimedia\Rdbms\LoadBalancer\closeConnection
closeConnection(IDatabase $conn)
Close a connection.
Definition: LoadBalancer.php:1223
Wikimedia\Rdbms\LoadBalancer\KEY_FOREIGN_FREE_NOROUND
const KEY_FOREIGN_FREE_NOROUND
Definition: LoadBalancer.php:139
Wikimedia\Rdbms\LoadBalancer\reportConnectionError
reportConnectionError()
Definition: LoadBalancer.php:1119
array
the array() calling protocol came about after MediaWiki 1.4rc1.
$type
$type
Definition: testCompression.php:48
Wikimedia\Rdbms\IDatabase\writesOrCallbacksPending
writesOrCallbacksPending()
Returns true if there is a transaction/round open with possible write queries or transaction pre-comm...