MediaWiki  1.27.2
LoadBalancer.php
Go to the documentation of this file.
1 <?php
30 class LoadBalancer {
32  private $mServers;
34  private $mConns;
36  private $mLoads;
38  private $mGroupLoads;
40  private $mAllowLagged;
42  private $mWaitTimeout;
44  private $mParentInfo;
45 
49  private $mLoadMonitor;
51  private $srvCache;
52 
56  private $mReadIndex;
58  private $mWaitForPos;
60  private $laggedSlaveMode = false;
62  private $slavesDownMode = false;
64  private $mLastError = 'Unknown error';
66  private $readOnlyReason = false;
68  private $connsOpened = 0;
69 
71  protected $trxProfiler;
72 
74  const CONN_HELD_WARN_THRESHOLD = 10;
76  const MAX_LAG = 10;
78  const POS_WAIT_TIMEOUT = 10;
79 
87  public function __construct( array $params ) {
88  if ( !isset( $params['servers'] ) ) {
89  throw new MWException( __CLASS__ . ': missing servers parameter' );
90  }
91  $this->mServers = $params['servers'];
92  $this->mWaitTimeout = self::POS_WAIT_TIMEOUT;
93 
94  $this->mReadIndex = -1;
95  $this->mWriteIndex = -1;
96  $this->mConns = [
97  'local' => [],
98  'foreignUsed' => [],
99  'foreignFree' => [] ];
100  $this->mLoads = [];
101  $this->mWaitForPos = false;
102  $this->mErrorConnection = false;
103  $this->mAllowLagged = false;
104 
105  if ( isset( $params['readOnlyReason'] ) && is_string( $params['readOnlyReason'] ) ) {
106  $this->readOnlyReason = $params['readOnlyReason'];
107  }
108 
109  if ( isset( $params['loadMonitor'] ) ) {
110  $this->mLoadMonitorClass = $params['loadMonitor'];
111  } else {
112  $master = reset( $params['servers'] );
113  if ( isset( $master['type'] ) && $master['type'] === 'mysql' ) {
114  $this->mLoadMonitorClass = 'LoadMonitorMySQL';
115  } else {
116  $this->mLoadMonitorClass = 'LoadMonitorNull';
117  }
118  }
119 
120  foreach ( $params['servers'] as $i => $server ) {
121  $this->mLoads[$i] = $server['load'];
122  if ( isset( $server['groupLoads'] ) ) {
123  foreach ( $server['groupLoads'] as $group => $ratio ) {
124  if ( !isset( $this->mGroupLoads[$group] ) ) {
125  $this->mGroupLoads[$group] = [];
126  }
127  $this->mGroupLoads[$group][$i] = $ratio;
128  }
129  }
130  }
131 
132  $this->srvCache = ObjectCache::getLocalServerInstance();
133 
134  if ( isset( $params['trxProfiler'] ) ) {
135  $this->trxProfiler = $params['trxProfiler'];
136  } else {
137  $this->trxProfiler = new TransactionProfiler();
138  }
139  }
140 
146  private function getLoadMonitor() {
147  if ( !isset( $this->mLoadMonitor ) ) {
148  $class = $this->mLoadMonitorClass;
149  $this->mLoadMonitor = new $class( $this );
150  }
151 
152  return $this->mLoadMonitor;
153  }
154 
160  public function parentInfo( $x = null ) {
161  return wfSetVar( $this->mParentInfo, $x );
162  }
163 
170  private function getRandomNonLagged( array $loads, $wiki = false, $maxLag = self::MAX_LAG ) {
171  $lags = $this->getLagTimes( $wiki );
172 
173  # Unset excessively lagged servers
174  foreach ( $lags as $i => $lag ) {
175  if ( $i != 0 ) {
176  $maxServerLag = $maxLag;
177  if ( isset( $this->mServers[$i]['max lag'] ) ) {
178  $maxServerLag = min( $maxServerLag, $this->mServers[$i]['max lag'] );
179  }
180 
181  $host = $this->getServerName( $i );
182  if ( $lag === false ) {
183  wfDebugLog( 'replication', "Server $host (#$i) is not replicating?" );
184  unset( $loads[$i] );
185  } elseif ( $lag > $maxServerLag ) {
186  wfDebugLog( 'replication', "Server $host (#$i) has >= $lag seconds of lag" );
187  unset( $loads[$i] );
188  }
189  }
190  }
191 
192  # Find out if all the slaves with non-zero load are lagged
193  $sum = 0;
194  foreach ( $loads as $load ) {
195  $sum += $load;
196  }
197  if ( $sum == 0 ) {
198  # No appropriate DB servers except maybe the master and some slaves with zero load
199  # Do NOT use the master
200  # Instead, this function will return false, triggering read-only mode,
201  # and a lagged slave will be used instead.
202  return false;
203  }
204 
205  if ( count( $loads ) == 0 ) {
206  return false;
207  }
208 
209  # Return a random representative of the remainder
210  return ArrayUtils::pickRandom( $loads );
211  }
212 
224  public function getReaderIndex( $group = false, $wiki = false ) {
226 
227  # @todo FIXME: For now, only go through all this for mysql databases
228  if ( $wgDBtype != 'mysql' ) {
229  return $this->getWriterIndex();
230  }
231 
232  if ( count( $this->mServers ) == 1 ) {
233  # Skip the load balancing if there's only one server
234  return 0;
235  } elseif ( $group === false && $this->mReadIndex >= 0 ) {
236  # Shortcut if generic reader exists already
237  return $this->mReadIndex;
238  }
239 
240  # Find the relevant load array
241  if ( $group !== false ) {
242  if ( isset( $this->mGroupLoads[$group] ) ) {
243  $nonErrorLoads = $this->mGroupLoads[$group];
244  } else {
245  # No loads for this group, return false and the caller can use some other group
246  wfDebugLog( 'connect', __METHOD__ . ": no loads for group $group\n" );
247 
248  return false;
249  }
250  } else {
251  $nonErrorLoads = $this->mLoads;
252  }
253 
254  if ( !count( $nonErrorLoads ) ) {
255  throw new MWException( "Empty server array given to LoadBalancer" );
256  }
257 
258  # Scale the configured load ratios according to the dynamic load (if the load monitor supports it)
259  $this->getLoadMonitor()->scaleLoads( $nonErrorLoads, $group, $wiki );
260 
261  $laggedSlaveMode = false;
262 
263  # No server found yet
264  $i = false;
265  $conn = false;
266  # First try quickly looking through the available servers for a server that
267  # meets our criteria
268  $currentLoads = $nonErrorLoads;
269  while ( count( $currentLoads ) ) {
270  if ( $this->mAllowLagged || $laggedSlaveMode ) {
271  $i = ArrayUtils::pickRandom( $currentLoads );
272  } else {
273  $i = false;
274  if ( $this->mWaitForPos && $this->mWaitForPos->asOfTime() ) {
275  # ChronologyProtecter causes mWaitForPos to be set via sessions.
276  # This triggers doWait() after connect, so it's especially good to
277  # avoid lagged servers so as to avoid just blocking in that method.
278  $ago = microtime( true ) - $this->mWaitForPos->asOfTime();
279  # Aim for <= 1 second of waiting (being too picky can backfire)
280  $i = $this->getRandomNonLagged( $currentLoads, $wiki, $ago + 1 );
281  }
282  if ( $i === false ) {
283  # Any server with less lag than it's 'max lag' param is preferable
284  $i = $this->getRandomNonLagged( $currentLoads, $wiki );
285  }
286  if ( $i === false && count( $currentLoads ) != 0 ) {
287  # All slaves lagged. Switch to read-only mode
288  wfDebugLog( 'replication', "All slaves lagged. Switch to read-only mode" );
289  $i = ArrayUtils::pickRandom( $currentLoads );
290  $laggedSlaveMode = true;
291  }
292  }
293 
294  if ( $i === false ) {
295  # pickRandom() returned false
296  # This is permanent and means the configuration or the load monitor
297  # wants us to return false.
298  wfDebugLog( 'connect', __METHOD__ . ": pickRandom() returned false" );
299 
300  return false;
301  }
302 
303  $serverName = $this->getServerName( $i );
304  wfDebugLog( 'connect', __METHOD__ . ": Using reader #$i: $serverName..." );
305 
306  $conn = $this->openConnection( $i, $wiki );
307  if ( !$conn ) {
308  wfDebugLog( 'connect', __METHOD__ . ": Failed connecting to $i/$wiki" );
309  unset( $nonErrorLoads[$i] );
310  unset( $currentLoads[$i] );
311  $i = false;
312  continue;
313  }
314 
315  // Decrement reference counter, we are finished with this connection.
316  // It will be incremented for the caller later.
317  if ( $wiki !== false ) {
318  $this->reuseConnection( $conn );
319  }
320 
321  # Return this server
322  break;
323  }
324 
325  # If all servers were down, quit now
326  if ( !count( $nonErrorLoads ) ) {
327  wfDebugLog( 'connect', "All servers down" );
328  }
329 
330  if ( $i !== false ) {
331  # Slave connection successful
332  # Wait for the session master pos for a short time
333  if ( $this->mWaitForPos && $i > 0 ) {
334  if ( !$this->doWait( $i ) ) {
335  $this->mServers[$i]['slave pos'] = $conn->getSlavePos();
336  }
337  }
338  if ( $this->mReadIndex <= 0 && $this->mLoads[$i] > 0 && $group === false ) {
339  $this->mReadIndex = $i;
340  # Record if the generic reader index is in "lagged slave" mode
341  if ( $laggedSlaveMode ) {
342  $this->laggedSlaveMode = true;
343  }
344  }
345  $serverName = $this->getServerName( $i );
346  wfDebugLog( 'connect', __METHOD__ .
347  ": using server $serverName for group '$group'\n" );
348  }
349 
350  return $i;
351  }
352 
359  public function waitFor( $pos ) {
360  $this->mWaitForPos = $pos;
361  $i = $this->mReadIndex;
362 
363  if ( $i > 0 ) {
364  if ( !$this->doWait( $i ) ) {
365  $this->mServers[$i]['slave pos'] = $this->getAnyOpenConnection( $i )->getSlavePos();
366  $this->laggedSlaveMode = true;
367  }
368  }
369  }
370 
381  public function waitForOne( $pos, $timeout = null ) {
382  $this->mWaitForPos = $pos;
383 
384  $i = $this->mReadIndex;
385  if ( $i <= 0 ) {
386  // Pick a generic slave if there isn't one yet
387  $readLoads = $this->mLoads;
388  unset( $readLoads[$this->getWriterIndex()] ); // slaves only
389  $readLoads = array_filter( $readLoads ); // with non-zero load
390  $i = ArrayUtils::pickRandom( $readLoads );
391  }
392 
393  if ( $i > 0 ) {
394  $ok = $this->doWait( $i, true, $timeout );
395  } else {
396  $ok = true; // no applicable loads
397  }
398 
399  return $ok;
400  }
401 
408  public function waitForAll( $pos, $timeout = null ) {
409  $this->mWaitForPos = $pos;
410  $serverCount = count( $this->mServers );
411 
412  $ok = true;
413  for ( $i = 1; $i < $serverCount; $i++ ) {
414  if ( $this->mLoads[$i] > 0 ) {
415  $ok = $this->doWait( $i, true, $timeout ) && $ok;
416  }
417  }
418 
419  return $ok;
420  }
421 
429  public function getAnyOpenConnection( $i ) {
430  foreach ( $this->mConns as $conns ) {
431  if ( !empty( $conns[$i] ) ) {
432  return reset( $conns[$i] );
433  }
434  }
435 
436  return false;
437  }
438 
446  protected function doWait( $index, $open = false, $timeout = null ) {
447  $close = false; // close the connection afterwards
448 
449  // Check if we already know that the DB has reached this point
450  $server = $this->getServerName( $index );
451  $key = $this->srvCache->makeGlobalKey( __CLASS__, 'last-known-pos', $server );
453  $knownReachedPos = $this->srvCache->get( $key );
454  if ( $knownReachedPos && $knownReachedPos->hasReached( $this->mWaitForPos ) ) {
455  wfDebugLog( 'replication', __METHOD__ .
456  ": slave $server known to be caught up (pos >= $knownReachedPos).\n" );
457  return true;
458  }
459 
460  // Find a connection to wait on, creating one if needed and allowed
461  $conn = $this->getAnyOpenConnection( $index );
462  if ( !$conn ) {
463  if ( !$open ) {
464  wfDebugLog( 'replication', __METHOD__ . ": no connection open for $server\n" );
465 
466  return false;
467  } else {
468  $conn = $this->openConnection( $index, '' );
469  if ( !$conn ) {
470  wfDebugLog( 'replication', __METHOD__ . ": failed to connect to $server\n" );
471 
472  return false;
473  }
474  // Avoid connection spam in waitForAll() when connections
475  // are made just for the sake of doing this lag check.
476  $close = true;
477  }
478  }
479 
480  wfDebugLog( 'replication', __METHOD__ . ": Waiting for slave $server to catch up...\n" );
481  $timeout = $timeout ?: $this->mWaitTimeout;
482  $result = $conn->masterPosWait( $this->mWaitForPos, $timeout );
483 
484  if ( $result == -1 || is_null( $result ) ) {
485  // Timed out waiting for slave, use master instead
486  $msg = __METHOD__ . ": Timed out waiting on $server pos {$this->mWaitForPos}";
487  wfDebugLog( 'replication', "$msg\n" );
488  wfDebugLog( 'DBPerformance', "$msg:\n" . wfBacktrace( true ) );
489  $ok = false;
490  } else {
491  wfDebugLog( 'replication', __METHOD__ . ": Done\n" );
492  $ok = true;
493  // Remember that the DB reached this point
494  $this->srvCache->set( $key, $this->mWaitForPos, BagOStuff::TTL_DAY );
495  }
496 
497  if ( $close ) {
498  $this->closeConnection( $conn );
499  }
500 
501  return $ok;
502  }
503 
515  public function getConnection( $i, $groups = [], $wiki = false ) {
516  if ( $i === null || $i === false ) {
517  throw new MWException( 'Attempt to call ' . __METHOD__ .
518  ' with invalid server index' );
519  }
520 
521  if ( $wiki === wfWikiID() ) {
522  $wiki = false;
523  }
524 
525  $groups = ( $groups === false || $groups === [] )
526  ? [ false ] // check one "group": the generic pool
527  : (array)$groups;
528 
529  $masterOnly = ( $i == DB_MASTER || $i == $this->getWriterIndex() );
530  $oldConnsOpened = $this->connsOpened; // connections open now
531 
532  if ( $i == DB_MASTER ) {
533  $i = $this->getWriterIndex();
534  } else {
535  # Try to find an available server in any the query groups (in order)
536  foreach ( $groups as $group ) {
537  $groupIndex = $this->getReaderIndex( $group, $wiki );
538  if ( $groupIndex !== false ) {
539  $i = $groupIndex;
540  break;
541  }
542  }
543  }
544 
545  # Operation-based index
546  if ( $i == DB_SLAVE ) {
547  $this->mLastError = 'Unknown error'; // reset error string
548  # Try the general server pool if $groups are unavailable.
549  $i = in_array( false, $groups, true )
550  ? false // don't bother with this if that is what was tried above
551  : $this->getReaderIndex( false, $wiki );
552  # Couldn't find a working server in getReaderIndex()?
553  if ( $i === false ) {
554  $this->mLastError = 'No working slave server: ' . $this->mLastError;
555 
556  return $this->reportConnectionError();
557  }
558  }
559 
560  # Now we have an explicit index into the servers array
561  $conn = $this->openConnection( $i, $wiki );
562  if ( !$conn ) {
563  return $this->reportConnectionError();
564  }
565 
566  # Profile any new connections that happen
567  if ( $this->connsOpened > $oldConnsOpened ) {
568  $host = $conn->getServer();
569  $dbname = $conn->getDBname();
570  $trxProf = Profiler::instance()->getTransactionProfiler();
571  $trxProf->recordConnection( $host, $dbname, $masterOnly );
572  }
573 
574  if ( $masterOnly ) {
575  # Make master-requested DB handles inherit any read-only mode setting
576  $conn->setLBInfo( 'readOnlyReason', $this->getReadOnlyReason( $wiki ) );
577  }
578 
579  return $conn;
580  }
581 
590  public function reuseConnection( $conn ) {
591  $serverIndex = $conn->getLBInfo( 'serverIndex' );
592  $refCount = $conn->getLBInfo( 'foreignPoolRefCount' );
593  if ( $serverIndex === null || $refCount === null ) {
594  wfDebug( __METHOD__ . ": this connection was not opened as a foreign connection\n" );
605  return;
606  }
607 
608  $dbName = $conn->getDBname();
609  $prefix = $conn->tablePrefix();
610  if ( strval( $prefix ) !== '' ) {
611  $wiki = "$dbName-$prefix";
612  } else {
613  $wiki = $dbName;
614  }
615  if ( $this->mConns['foreignUsed'][$serverIndex][$wiki] !== $conn ) {
616  throw new MWException( __METHOD__ . ": connection not found, has " .
617  "the connection been freed already?" );
618  }
619  $conn->setLBInfo( 'foreignPoolRefCount', --$refCount );
620  if ( $refCount <= 0 ) {
621  $this->mConns['foreignFree'][$serverIndex][$wiki] = $conn;
622  unset( $this->mConns['foreignUsed'][$serverIndex][$wiki] );
623  wfDebug( __METHOD__ . ": freed connection $serverIndex/$wiki\n" );
624  } else {
625  wfDebug( __METHOD__ . ": reference count for $serverIndex/$wiki reduced to $refCount\n" );
626  }
627  }
628 
641  public function getConnectionRef( $db, $groups = [], $wiki = false ) {
642  return new DBConnRef( $this, $this->getConnection( $db, $groups, $wiki ) );
643  }
644 
657  public function getLazyConnectionRef( $db, $groups = [], $wiki = false ) {
658  return new DBConnRef( $this, [ $db, $groups, $wiki ] );
659  }
660 
673  public function openConnection( $i, $wiki = false ) {
674  if ( $wiki !== false ) {
675  $conn = $this->openForeignConnection( $i, $wiki );
676  } elseif ( isset( $this->mConns['local'][$i][0] ) ) {
677  $conn = $this->mConns['local'][$i][0];
678  } else {
679  $server = $this->mServers[$i];
680  $server['serverIndex'] = $i;
681  $conn = $this->reallyOpenConnection( $server, false );
682  $serverName = $this->getServerName( $i );
683  if ( $conn->isOpen() ) {
684  wfDebugLog( 'connect', "Connected to database $i at $serverName\n" );
685  $this->mConns['local'][$i][0] = $conn;
686  } else {
687  wfDebugLog( 'connect', "Failed to connect to database $i at $serverName\n" );
688  $this->mErrorConnection = $conn;
689  $conn = false;
690  }
691  }
692 
693  if ( $conn && !$conn->isOpen() ) {
694  // Connection was made but later unrecoverably lost for some reason.
695  // Do not return a handle that will just throw exceptions on use,
696  // but let the calling code (e.g. getReaderIndex) try another server.
697  // See DatabaseMyslBase::ping() for how this can happen.
698  $this->mErrorConnection = $conn;
699  $conn = false;
700  }
701 
702  return $conn;
703  }
704 
723  private function openForeignConnection( $i, $wiki ) {
724  list( $dbName, $prefix ) = wfSplitWikiID( $wiki );
725  if ( isset( $this->mConns['foreignUsed'][$i][$wiki] ) ) {
726  // Reuse an already-used connection
727  $conn = $this->mConns['foreignUsed'][$i][$wiki];
728  wfDebug( __METHOD__ . ": reusing connection $i/$wiki\n" );
729  } elseif ( isset( $this->mConns['foreignFree'][$i][$wiki] ) ) {
730  // Reuse a free connection for the same wiki
731  $conn = $this->mConns['foreignFree'][$i][$wiki];
732  unset( $this->mConns['foreignFree'][$i][$wiki] );
733  $this->mConns['foreignUsed'][$i][$wiki] = $conn;
734  wfDebug( __METHOD__ . ": reusing free connection $i/$wiki\n" );
735  } elseif ( !empty( $this->mConns['foreignFree'][$i] ) ) {
736  // Reuse a connection from another wiki
737  $conn = reset( $this->mConns['foreignFree'][$i] );
738  $oldWiki = key( $this->mConns['foreignFree'][$i] );
739 
740  // The empty string as a DB name means "don't care".
741  // DatabaseMysqlBase::open() already handle this on connection.
742  if ( $dbName !== '' && !$conn->selectDB( $dbName ) ) {
743  $this->mLastError = "Error selecting database $dbName on server " .
744  $conn->getServer() . " from client host " . wfHostname() . "\n";
745  $this->mErrorConnection = $conn;
746  $conn = false;
747  } else {
748  $conn->tablePrefix( $prefix );
749  unset( $this->mConns['foreignFree'][$i][$oldWiki] );
750  $this->mConns['foreignUsed'][$i][$wiki] = $conn;
751  wfDebug( __METHOD__ . ": reusing free connection from $oldWiki for $wiki\n" );
752  }
753  } else {
754  // Open a new connection
755  $server = $this->mServers[$i];
756  $server['serverIndex'] = $i;
757  $server['foreignPoolRefCount'] = 0;
758  $server['foreign'] = true;
759  $conn = $this->reallyOpenConnection( $server, $dbName );
760  if ( !$conn->isOpen() ) {
761  wfDebug( __METHOD__ . ": error opening connection for $i/$wiki\n" );
762  $this->mErrorConnection = $conn;
763  $conn = false;
764  } else {
765  $conn->tablePrefix( $prefix );
766  $this->mConns['foreignUsed'][$i][$wiki] = $conn;
767  wfDebug( __METHOD__ . ": opened new connection for $i/$wiki\n" );
768  }
769  }
770 
771  // Increment reference count
772  if ( $conn ) {
773  $refCount = $conn->getLBInfo( 'foreignPoolRefCount' );
774  $conn->setLBInfo( 'foreignPoolRefCount', $refCount + 1 );
775  }
776 
777  return $conn;
778  }
779 
787  private function isOpen( $index ) {
788  if ( !is_integer( $index ) ) {
789  return false;
790  }
791 
792  return (bool)$this->getAnyOpenConnection( $index );
793  }
794 
805  protected function reallyOpenConnection( $server, $dbNameOverride = false ) {
806  if ( !is_array( $server ) ) {
807  throw new MWException( 'You must update your load-balancing configuration. ' .
808  'See DefaultSettings.php entry for $wgDBservers.' );
809  }
810 
811  if ( $dbNameOverride !== false ) {
812  $server['dbname'] = $dbNameOverride;
813  }
814 
815  // Let the handle know what the cluster master is (e.g. "db1052")
816  $masterName = $this->getServerName( 0 );
817  $server['clusterMasterHost'] = $masterName;
818 
819  // Log when many connection are made on requests
820  if ( ++$this->connsOpened >= self::CONN_HELD_WARN_THRESHOLD ) {
821  wfDebugLog( 'DBPerformance', __METHOD__ . ": " .
822  "{$this->connsOpened}+ connections made (master=$masterName)\n" .
823  wfBacktrace( true ) );
824  }
825 
826  # Create object
827  try {
828  $db = DatabaseBase::factory( $server['type'], $server );
829  } catch ( DBConnectionError $e ) {
830  // FIXME: This is probably the ugliest thing I have ever done to
831  // PHP. I'm half-expecting it to segfault, just out of disgust. -- TS
832  $db = $e->db;
833  }
834 
835  $db->setLBInfo( $server );
836  $db->setLazyMasterHandle(
837  $this->getLazyConnectionRef( DB_MASTER, [], $db->getWikiID() )
838  );
839  $db->setTransactionProfiler( $this->trxProfiler );
840 
841  return $db;
842  }
843 
848  private function reportConnectionError() {
849  $conn = $this->mErrorConnection; // The connection which caused the error
850  $context = [
851  'method' => __METHOD__,
852  'last_error' => $this->mLastError,
853  ];
854 
855  if ( !is_object( $conn ) ) {
856  // No last connection, probably due to all servers being too busy
857  wfLogDBError(
858  "LB failure with no last connection. Connection error: {last_error}",
859  $context
860  );
861 
862  // If all servers were busy, mLastError will contain something sensible
863  throw new DBConnectionError( null, $this->mLastError );
864  } else {
865  $context['db_server'] = $conn->getProperty( 'mServer' );
866  wfLogDBError(
867  "Connection error: {last_error} ({db_server})",
868  $context
869  );
870 
871  // throws DBConnectionError
872  $conn->reportConnectionError( "{$this->mLastError} ({$context['db_server']})" );
873  }
874 
875  return false; /* not reached */
876  }
877 
882  public function getWriterIndex() {
883  return 0;
884  }
885 
892  public function haveIndex( $i ) {
893  return array_key_exists( $i, $this->mServers );
894  }
895 
902  public function isNonZeroLoad( $i ) {
903  return array_key_exists( $i, $this->mServers ) && $this->mLoads[$i] != 0;
904  }
905 
911  public function getServerCount() {
912  return count( $this->mServers );
913  }
914 
921  public function getServerName( $i ) {
922  if ( isset( $this->mServers[$i]['hostName'] ) ) {
923  $name = $this->mServers[$i]['hostName'];
924  } elseif ( isset( $this->mServers[$i]['host'] ) ) {
925  $name = $this->mServers[$i]['host'];
926  } else {
927  $name = '';
928  }
929 
930  return ( $name != '' ) ? $name : 'localhost';
931  }
932 
938  public function getServerInfo( $i ) {
939  if ( isset( $this->mServers[$i] ) ) {
940  return $this->mServers[$i];
941  } else {
942  return false;
943  }
944  }
945 
952  public function setServerInfo( $i, array $serverInfo ) {
953  $this->mServers[$i] = $serverInfo;
954  }
955 
960  public function getMasterPos() {
961  # If this entire request was served from a slave without opening a connection to the
962  # master (however unlikely that may be), then we can fetch the position from the slave.
963  $masterConn = $this->getAnyOpenConnection( 0 );
964  if ( !$masterConn ) {
965  $serverCount = count( $this->mServers );
966  for ( $i = 1; $i < $serverCount; $i++ ) {
967  $conn = $this->getAnyOpenConnection( $i );
968  if ( $conn ) {
969  return $conn->getSlavePos();
970  }
971  }
972  } else {
973  return $masterConn->getMasterPos();
974  }
975 
976  return false;
977  }
978 
982  public function closeAll() {
983  foreach ( $this->mConns as $conns2 ) {
984  foreach ( $conns2 as $conns3 ) {
986  foreach ( $conns3 as $conn ) {
987  $conn->close();
988  }
989  }
990  }
991  $this->mConns = [
992  'local' => [],
993  'foreignFree' => [],
994  'foreignUsed' => [],
995  ];
996  $this->connsOpened = 0;
997  }
998 
1005  public function closeConnection( $conn ) {
1006  $done = false;
1007  foreach ( $this->mConns as $i1 => $conns2 ) {
1008  foreach ( $conns2 as $i2 => $conns3 ) {
1009  foreach ( $conns3 as $i3 => $candidateConn ) {
1010  if ( $conn === $candidateConn ) {
1011  $conn->close();
1012  unset( $this->mConns[$i1][$i2][$i3] );
1014  $done = true;
1015  break;
1016  }
1017  }
1018  }
1019  }
1020  if ( !$done ) {
1021  $conn->close();
1022  }
1023  }
1024 
1029  public function commitAll( $fname = __METHOD__ ) {
1030  foreach ( $this->mConns as $conns2 ) {
1031  foreach ( $conns2 as $conns3 ) {
1033  foreach ( $conns3 as $conn ) {
1034  if ( $conn->trxLevel() ) {
1035  $conn->commit( $fname, 'flush' );
1036  }
1037  }
1038  }
1039  }
1040  }
1041 
1046  public function commitMasterChanges( $fname = __METHOD__ ) {
1047  $masterIndex = $this->getWriterIndex();
1048  foreach ( $this->mConns as $conns2 ) {
1049  if ( empty( $conns2[$masterIndex] ) ) {
1050  continue;
1051  }
1053  foreach ( $conns2[$masterIndex] as $conn ) {
1054  if ( $conn->trxLevel() && $conn->writesOrCallbacksPending() ) {
1055  $conn->commit( $fname, 'flush' );
1056  }
1057  }
1058  }
1059  }
1060 
1067  public function rollbackMasterChanges( $fname = __METHOD__ ) {
1068  $failedServers = [];
1069 
1070  $masterIndex = $this->getWriterIndex();
1071  foreach ( $this->mConns as $conns2 ) {
1072  if ( empty( $conns2[$masterIndex] ) ) {
1073  continue;
1074  }
1076  foreach ( $conns2[$masterIndex] as $conn ) {
1077  if ( $conn->trxLevel() && $conn->writesOrCallbacksPending() ) {
1078  try {
1079  $conn->rollback( $fname, 'flush' );
1080  } catch ( DBError $e ) {
1082  $failedServers[] = $conn->getServer();
1083  }
1084  }
1085  }
1086  }
1087 
1088  if ( $failedServers ) {
1089  throw new DBExpectedError( null, "Rollback failed on server(s) " .
1090  implode( ', ', array_unique( $failedServers ) ) );
1091  }
1092  }
1093 
1098  public function hasMasterConnection() {
1099  return $this->isOpen( $this->getWriterIndex() );
1100  }
1101 
1107  public function hasMasterChanges() {
1108  $masterIndex = $this->getWriterIndex();
1109  foreach ( $this->mConns as $conns2 ) {
1110  if ( empty( $conns2[$masterIndex] ) ) {
1111  continue;
1112  }
1114  foreach ( $conns2[$masterIndex] as $conn ) {
1115  if ( $conn->trxLevel() && $conn->writesOrCallbacksPending() ) {
1116  return true;
1117  }
1118  }
1119  }
1120  return false;
1121  }
1122 
1128  public function lastMasterChangeTimestamp() {
1129  $lastTime = false;
1130  $masterIndex = $this->getWriterIndex();
1131  foreach ( $this->mConns as $conns2 ) {
1132  if ( empty( $conns2[$masterIndex] ) ) {
1133  continue;
1134  }
1136  foreach ( $conns2[$masterIndex] as $conn ) {
1137  $lastTime = max( $lastTime, $conn->lastDoneWrites() );
1138  }
1139  }
1140  return $lastTime;
1141  }
1142 
1151  public function hasOrMadeRecentMasterChanges( $age = null ) {
1152  $age = ( $age === null ) ? $this->mWaitTimeout : $age;
1153 
1154  return ( $this->hasMasterChanges()
1155  || $this->lastMasterChangeTimestamp() > microtime( true ) - $age );
1156  }
1157 
1164  public function pendingMasterChangeCallers() {
1165  $fnames = [];
1166 
1167  $masterIndex = $this->getWriterIndex();
1168  foreach ( $this->mConns as $conns2 ) {
1169  if ( empty( $conns2[$masterIndex] ) ) {
1170  continue;
1171  }
1173  foreach ( $conns2[$masterIndex] as $conn ) {
1174  $fnames = array_merge( $fnames, $conn->pendingWriteCallers() );
1175  }
1176  }
1177 
1178  return $fnames;
1179  }
1180 
1185  public function waitTimeout( $value = null ) {
1186  return wfSetVar( $this->mWaitTimeout, $value );
1187  }
1188 
1195  public function getLaggedSlaveMode( $wiki = false ) {
1196  // No-op if there is only one DB (also avoids recursion)
1197  if ( !$this->laggedSlaveMode && $this->getServerCount() > 1 ) {
1198  try {
1199  // See if laggedSlaveMode gets set
1200  $conn = $this->getConnection( DB_SLAVE, false, $wiki );
1201  $this->reuseConnection( $conn );
1202  } catch ( DBConnectionError $e ) {
1203  // Avoid expensive re-connect attempts and failures
1204  $this->slavesDownMode = true;
1205  $this->laggedSlaveMode = true;
1206  }
1207  }
1208 
1209  return $this->laggedSlaveMode;
1210  }
1211 
1217  public function laggedSlaveUsed() {
1218  return $this->laggedSlaveMode;
1219  }
1220 
1227  public function getReadOnlyReason( $wiki = false ) {
1228  if ( $this->readOnlyReason !== false ) {
1229  return $this->readOnlyReason;
1230  } elseif ( $this->getLaggedSlaveMode( $wiki ) ) {
1231  if ( $this->slavesDownMode ) {
1232  return 'The database has been automatically locked ' .
1233  'until the slave database servers become available';
1234  } else {
1235  return 'The database has been automatically locked ' .
1236  'while the slave database servers catch up to the master.';
1237  }
1238  }
1239 
1240  return false;
1241  }
1242 
1248  public function allowLagged( $mode = null ) {
1249  if ( $mode === null ) {
1250  return $this->mAllowLagged;
1251  }
1252  $this->mAllowLagged = $mode;
1253 
1254  return $this->mAllowLagged;
1255  }
1256 
1260  public function pingAll() {
1261  $success = true;
1262  foreach ( $this->mConns as $conns2 ) {
1263  foreach ( $conns2 as $conns3 ) {
1265  foreach ( $conns3 as $conn ) {
1266  if ( !$conn->ping() ) {
1267  $success = false;
1268  }
1269  }
1270  }
1271  }
1272 
1273  return $success;
1274  }
1275 
1281  public function forEachOpenConnection( $callback, array $params = [] ) {
1282  foreach ( $this->mConns as $conns2 ) {
1283  foreach ( $conns2 as $conns3 ) {
1284  foreach ( $conns3 as $conn ) {
1285  $mergedParams = array_merge( [ $conn ], $params );
1286  call_user_func_array( $callback, $mergedParams );
1287  }
1288  }
1289  }
1290  }
1291 
1302  public function getMaxLag( $wiki = false ) {
1303  $maxLag = -1;
1304  $host = '';
1305  $maxIndex = 0;
1306 
1307  if ( $this->getServerCount() <= 1 ) {
1308  return [ $host, $maxLag, $maxIndex ]; // no replication = no lag
1309  }
1310 
1311  $lagTimes = $this->getLagTimes( $wiki );
1312  foreach ( $lagTimes as $i => $lag ) {
1313  if ( $lag > $maxLag ) {
1314  $maxLag = $lag;
1315  $host = $this->mServers[$i]['host'];
1316  $maxIndex = $i;
1317  }
1318  }
1319 
1320  return [ $host, $maxLag, $maxIndex ];
1321  }
1322 
1333  public function getLagTimes( $wiki = false ) {
1334  if ( $this->getServerCount() <= 1 ) {
1335  return [ 0 => 0 ]; // no replication = no lag
1336  }
1337 
1338  # Send the request to the load monitor
1339  return $this->getLoadMonitor()->getLagTimes( array_keys( $this->mServers ), $wiki );
1340  }
1341 
1356  public function safeGetLag( IDatabase $conn ) {
1357  if ( $this->getServerCount() == 1 ) {
1358  return 0;
1359  } else {
1360  return $conn->getLag();
1361  }
1362  }
1363 
1375  public function safeWaitForMasterPos( IDatabase $conn, $pos = false, $timeout = 10 ) {
1376  if ( $this->getServerCount() == 1 || !$conn->getLBInfo( 'slave' ) ) {
1377  return true; // server is not a slave DB
1378  }
1379 
1380  $pos = $pos ?: $this->getConnection( DB_MASTER )->getMasterPos();
1381  if ( !$pos ) {
1382  return false; // something is misconfigured
1383  }
1384 
1385  $result = $conn->masterPosWait( $pos, $timeout );
1386  if ( $result == -1 || is_null( $result ) ) {
1387  $msg = __METHOD__ . ": Timed out waiting on {$conn->getServer()} pos {$pos}";
1388  wfDebugLog( 'replication', "$msg\n" );
1389  wfDebugLog( 'DBPerformance', "$msg:\n" . wfBacktrace( true ) );
1390  $ok = false;
1391  } else {
1392  wfDebugLog( 'replication', __METHOD__ . ": Done\n" );
1393  $ok = true;
1394  }
1395 
1396  return $ok;
1397  }
1398 
1404  public function clearLagTimeCache() {
1405  $this->getLoadMonitor()->clearCaches();
1406  }
1407 }
getLaggedSlaveMode($wiki=false)
commitAll($fname=__METHOD__)
Commit transactions on all open connections.
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
Database error base class.
the array() calling protocol came about after MediaWiki 1.4rc1.
safeGetLag(IDatabase $conn)
Get the lag in seconds for a given connection, or zero if this load balancer does not have replicatio...
integer $mWaitTimeout
Seconds to spend waiting on slave lag to resolve.
getAnyOpenConnection($i)
Get any open connection to a given server index, local or foreign Returns false if there is no connec...
magic word the default is to use $key to get the and $key value or $key value text $key value html to format the value $key
Definition: hooks.txt:2321
$context
Definition: load.php:44
$success
reuseConnection($conn)
Mark a foreign connection as being available for reuse under a different DB name or prefix...
getServerCount()
Get the number of defined servers (not the number of open connections)
array[] $mServers
Map of (server index => server config array)
bool $slavesDownMode
Whether the generic reader fell back to a lagged slave.
array $mParentInfo
LBFactory information.
getLoadMonitor()
Get a LoadMonitor instance.
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException'returning false will NOT prevent logging $e
Definition: hooks.txt:1932
static instance()
Singleton.
Definition: Profiler.php:60
rollbackMasterChanges($fname=__METHOD__)
Issue ROLLBACK only on master, only if queries were done on connection.
array $mLoads
Map of (server index => weight)
wfHostname()
Fetch server name for use in error reporting etc.
getLazyConnectionRef($db, $groups=[], $wiki=false)
Get a database connection handle reference without connecting yet.
$wgDBtype
Database type.
wfBacktrace($raw=null)
Get a debug backtrace as a string.
wfLogDBError($text, array $context=[])
Log for database errors.
$value
getMaxLag($wiki=false)
Get the hostname and lag time of the most-lagged slave.
when a variable name is used in a it is silently declared as a new local masking the global
Definition: design.txt:93
array[] $mGroupLoads
Map of (group => server index => weight)
forEachOpenConnection($callback, array $params=[])
Call a function with each open connection object.
masterPosWait(DBMasterPos $pos, $timeout)
Wait for the slave to catch up to a given master position.
__construct(array $params)
wfDebug($text, $dest= 'all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
array[] $mConns
Map of (local/foreignUsed/foreignFree => server index => DatabaseBase array)
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 '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:Associative array mapping language codes to prefixed links of the form"language:title".&$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':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:1796
getConnection($i, $groups=[], $wiki=false)
Get a connection by index This is the main entry point for this class.
getReadOnlyReason($wiki=false)
doWait($index, $open=false, $timeout=null)
Wait for a given slave to catch up to the master pos stored in $this.
BagOStuff $srvCache
LoadMonitor $mLoadMonitor
getRandomNonLagged(array $loads, $wiki=false, $maxLag=self::MAX_LAG)
Database load balancing object.
wfDebugLog($logGroup, $text, $dest= 'all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not...
getServerInfo($i)
Return the server info structure for a given index, or false if the index is invalid.
string $mLastError
The last DB selection or connection error.
setServerInfo($i, array $serverInfo)
Sets the server info structure for the given index.
Base class for the more common types of database errors.
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
bool DatabaseBase $mErrorConnection
Database connection that caused a problem.
getLag()
Get slave lag.
Helper class that detects high-contention DB queries via profiling calls.
waitForOne($pos, $timeout=null)
Set the master wait position and wait for a "generic" slave to catch up to it.
allowLagged($mode=null)
Disables/enables lag checks.
openForeignConnection($i, $wiki)
Open a connection to a foreign DB, or return one if it is already open.
hasMasterChanges()
Determine if there are pending changes in a transaction by this thread.
integer $connsOpened
Total connections opened.
$params
openConnection($i, $wiki=false)
Open a connection to the server given by the specified index Index must be an actual index into the a...
parentInfo($x=null)
Get or set arbitrary data used by the parent object, usually an LBFactory.
Helper class to handle automatically marking connections as reusable (via RAII pattern) as well handl...
Definition: DBConnRef.php:10
static factory($dbType, $p=[])
Given a DB type, construct the name of the appropriate child class of DatabaseBase.
Definition: Database.php:580
const DB_SLAVE
Definition: Defines.php:46
clearLagTimeCache()
Clear the cache for slag lag delay times.
wfWikiID()
Get an ASCII string identifying this wiki This is used as a prefix in memcached keys.
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
hasOrMadeRecentMasterChanges($age=null)
Check if this load balancer object had any recent or still pending writes issued against it by this P...
getLagTimes($wiki=false)
Get an estimate of replication lag (in seconds) for each server.
getConnectionRef($db, $groups=[], $wiki=false)
Get a database connection handle reference.
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
wfSetVar(&$dest, $source, $force=false)
Sets dest to source and returns the original value of dest If source is NULL, it just returns the val...
if(!defined( 'MEDIAWIKI')) $fname
This file is not a valid entry point, perform no further processing unless MEDIAWIKI is defined...
Definition: Setup.php:35
closeAll()
Close all open connections.
static getLocalServerInstance($fallback=CACHE_NONE)
Factory function for CACHE_ACCEL (referenced from DefaultSettings.php)
waitTimeout($value=null)
closeConnection($conn)
Close a connection Using this function makes sure the LoadBalancer knows the connection is closed...
haveIndex($i)
Returns true if the specified index is a valid server index.
getLBInfo($name=null)
Get properties passed down from the server info array of the load balancer.
isOpen($index)
Test if the specified index represents an open connection.
static pickRandom($weights)
Given an array of non-normalised probabilities, this function will select an element and return the a...
Definition: ArrayUtils.php:66
pendingMasterChangeCallers()
Get the list of callers that have pending master changes.
const DB_MASTER
Definition: Defines.php:47
getReaderIndex($group=false, $wiki=false)
Get the index of the reader connection, which may be a slave This takes into account load ratios and ...
isNonZeroLoad($i)
Returns true if the specified index is valid and has non-zero load.
waitFor($pos)
Set the master wait position If a DB_SLAVE connection has been opened already, waits Otherwise sets a...
integer $mReadIndex
The generic (not query grouped) slave index (of $mServers)
getMasterPos()
Get the current master position for chronology control purposes.
string bool $readOnlyReason
Reason the LB is read-only or false if not.
wfSplitWikiID($wiki)
Split a wiki ID into DB name and table prefix.
bool $mAllowLagged
Whether to disregard slave lag as a factor in slave selection.
commitMasterChanges($fname=__METHOD__)
Issue COMMIT only on master, only if queries were done on connection.
bool DBMasterPos $mWaitForPos
False if not set.
getServerName($i)
Get the host name or IP address of the server with the specified index Prefer a readable name if avai...
static logException($e)
Log an exception to the exception log (if enabled).
reallyOpenConnection($server, $dbNameOverride=false)
Really opens a connection.
string $mLoadMonitorClass
The LoadMonitor subclass name.
waitForAll($pos, $timeout=null)
Set the master wait position and wait for ALL slaves to catch up to it.
bool $laggedSlaveMode
Whether the generic reader fell back to a lagged slave.
TransactionProfiler $trxProfiler
Basic database interface for live and lazy-loaded DB handles.
Definition: IDatabase.php:35
safeWaitForMasterPos(IDatabase $conn, $pos=false, $timeout=10)
Wait for a slave DB to reach a specified master position.
lastMasterChangeTimestamp()
Get the timestamp of the latest write query done by this thread.
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:310