MediaWiki  1.23.12
LoadBalancer.php
Go to the documentation of this file.
1 <?php
30 class LoadBalancer {
32 
35  private $mReadIndex, $mAllowLagged;
36 
38  private $mWaitForPos;
39 
40  private $mWaitTimeout;
41  private $mLaggedSlaveMode, $mLastError = 'Unknown error';
42  private $mParentInfo, $mLagTimes;
44 
52  function __construct( $params ) {
53  if ( !isset( $params['servers'] ) ) {
54  throw new MWException( __CLASS__ . ': missing servers parameter' );
55  }
56  $this->mServers = $params['servers'];
57 
58  if ( isset( $params['waitTimeout'] ) ) {
59  $this->mWaitTimeout = $params['waitTimeout'];
60  } else {
61  $this->mWaitTimeout = 10;
62  }
63 
64  $this->mReadIndex = -1;
65  $this->mWriteIndex = -1;
66  $this->mConns = array(
67  'local' => array(),
68  'foreignUsed' => array(),
69  'foreignFree' => array() );
70  $this->mLoads = array();
71  $this->mWaitForPos = false;
72  $this->mLaggedSlaveMode = false;
73  $this->mErrorConnection = false;
74  $this->mAllowLagged = false;
75 
76  if ( isset( $params['loadMonitor'] ) ) {
77  $this->mLoadMonitorClass = $params['loadMonitor'];
78  } else {
79  $master = reset( $params['servers'] );
80  if ( isset( $master['type'] ) && $master['type'] === 'mysql' ) {
81  $this->mLoadMonitorClass = 'LoadMonitorMySQL';
82  } else {
83  $this->mLoadMonitorClass = 'LoadMonitorNull';
84  }
85  }
86 
87  foreach ( $params['servers'] as $i => $server ) {
88  $this->mLoads[$i] = $server['load'];
89  if ( isset( $server['groupLoads'] ) ) {
90  foreach ( $server['groupLoads'] as $group => $ratio ) {
91  if ( !isset( $this->mGroupLoads[$group] ) ) {
92  $this->mGroupLoads[$group] = array();
93  }
94  $this->mGroupLoads[$group][$i] = $ratio;
95  }
96  }
97  }
98  }
99 
105  function getLoadMonitor() {
106  if ( !isset( $this->mLoadMonitor ) ) {
107  $class = $this->mLoadMonitorClass;
108  $this->mLoadMonitor = new $class( $this );
109  }
110 
111  return $this->mLoadMonitor;
112  }
113 
119  function parentInfo( $x = null ) {
120  return wfSetVar( $this->mParentInfo, $x );
121  }
122 
132  function pickRandom( $weights ) {
133  return ArrayUtils::pickRandom( $weights );
134  }
135 
141  function getRandomNonLagged( $loads, $wiki = false ) {
142  # Unset excessively lagged servers
143  $lags = $this->getLagTimes( $wiki );
144  foreach ( $lags as $i => $lag ) {
145  if ( $i != 0 ) {
146  if ( $lag === false ) {
147  wfDebugLog( 'replication', "Server #$i is not replicating" );
148  unset( $loads[$i] );
149  } elseif ( isset( $this->mServers[$i]['max lag'] ) && $lag > $this->mServers[$i]['max lag'] ) {
150  wfDebugLog( 'replication', "Server #$i is excessively lagged ($lag seconds)" );
151  unset( $loads[$i] );
152  }
153  }
154  }
155 
156  # Find out if all the slaves with non-zero load are lagged
157  $sum = 0;
158  foreach ( $loads as $load ) {
159  $sum += $load;
160  }
161  if ( $sum == 0 ) {
162  # No appropriate DB servers except maybe the master and some slaves with zero load
163  # Do NOT use the master
164  # Instead, this function will return false, triggering read-only mode,
165  # and a lagged slave will be used instead.
166  return false;
167  }
168 
169  if ( count( $loads ) == 0 ) {
170  return false;
171  }
172 
173  #wfDebugLog( 'connect', var_export( $loads, true ) );
174 
175  # Return a random representative of the remainder
176  return ArrayUtils::pickRandom( $loads );
177  }
178 
190  function getReaderIndex( $group = false, $wiki = false ) {
191  global $wgReadOnly, $wgDBtype;
192 
193  # @todo FIXME: For now, only go through all this for mysql databases
194  if ( $wgDBtype != 'mysql' ) {
195  return $this->getWriterIndex();
196  }
197 
198  if ( count( $this->mServers ) == 1 ) {
199  # Skip the load balancing if there's only one server
200  return 0;
201  } elseif ( $group === false && $this->mReadIndex >= 0 ) {
202  # Shortcut if generic reader exists already
203  return $this->mReadIndex;
204  }
205 
206  $section = new ProfileSection( __METHOD__ );
207 
208  # Find the relevant load array
209  if ( $group !== false ) {
210  if ( isset( $this->mGroupLoads[$group] ) ) {
211  $nonErrorLoads = $this->mGroupLoads[$group];
212  } else {
213  # No loads for this group, return false and the caller can use some other group
214  wfDebug( __METHOD__ . ": no loads for group $group\n" );
215 
216  return false;
217  }
218  } else {
219  $nonErrorLoads = $this->mLoads;
220  }
221 
222  if ( !count( $nonErrorLoads ) ) {
223  throw new MWException( "Empty server array given to LoadBalancer" );
224  }
225 
226  # Scale the configured load ratios according to the dynamic load (if the load monitor supports it)
227  $this->getLoadMonitor()->scaleLoads( $nonErrorLoads, $group, $wiki );
228 
229  $laggedSlaveMode = false;
230 
231  # No server found yet
232  $i = false;
233  # First try quickly looking through the available servers for a server that
234  # meets our criteria
235  $currentLoads = $nonErrorLoads;
236  while ( count( $currentLoads ) ) {
237  if ( $wgReadOnly || $this->mAllowLagged || $laggedSlaveMode ) {
238  $i = ArrayUtils::pickRandom( $currentLoads );
239  } else {
240  $i = $this->getRandomNonLagged( $currentLoads, $wiki );
241  if ( $i === false && count( $currentLoads ) != 0 ) {
242  # All slaves lagged. Switch to read-only mode
243  wfDebugLog( 'replication', "All slaves lagged. Switch to read-only mode" );
244  $wgReadOnly = 'The database has been automatically locked ' .
245  'while the slave database servers catch up to the master';
246  $i = ArrayUtils::pickRandom( $currentLoads );
247  $laggedSlaveMode = true;
248  }
249  }
250 
251  if ( $i === false ) {
252  # pickRandom() returned false
253  # This is permanent and means the configuration or the load monitor
254  # wants us to return false.
255  wfDebugLog( 'connect', __METHOD__ . ": pickRandom() returned false" );
256 
257  return false;
258  }
259 
260  wfDebugLog( 'connect', __METHOD__ .
261  ": Using reader #$i: {$this->mServers[$i]['host']}..." );
262 
263  $conn = $this->openConnection( $i, $wiki );
264  if ( !$conn ) {
265  wfDebugLog( 'connect', __METHOD__ . ": Failed connecting to $i/$wiki" );
266  unset( $nonErrorLoads[$i] );
267  unset( $currentLoads[$i] );
268  $i = false;
269  continue;
270  }
271 
272  // Decrement reference counter, we are finished with this connection.
273  // It will be incremented for the caller later.
274  if ( $wiki !== false ) {
275  $this->reuseConnection( $conn );
276  }
277 
278  # Return this server
279  break;
280  }
281 
282  # If all servers were down, quit now
283  if ( !count( $nonErrorLoads ) ) {
284  wfDebugLog( 'connect', "All servers down" );
285  }
286 
287  if ( $i !== false ) {
288  # Slave connection successful
289  # Wait for the session master pos for a short time
290  if ( $this->mWaitForPos && $i > 0 ) {
291  if ( !$this->doWait( $i ) ) {
292  $this->mServers[$i]['slave pos'] = $conn->getSlavePos();
293  }
294  }
295  if ( $this->mReadIndex <= 0 && $this->mLoads[$i] > 0 && $group !== false ) {
296  $this->mReadIndex = $i;
297  }
298  }
299 
300  return $i;
301  }
302 
308  function sleep( $t ) {
309  wfProfileIn( __METHOD__ );
310  wfDebug( __METHOD__ . ": waiting $t us\n" );
311  usleep( $t );
312  wfProfileOut( __METHOD__ );
313 
314  return $t;
315  }
316 
323  public function waitFor( $pos ) {
324  wfProfileIn( __METHOD__ );
325  $this->mWaitForPos = $pos;
326  $i = $this->mReadIndex;
327 
328  if ( $i > 0 ) {
329  if ( !$this->doWait( $i ) ) {
330  $this->mServers[$i]['slave pos'] = $this->getAnyOpenConnection( $i )->getSlavePos();
331  $this->mLaggedSlaveMode = true;
332  }
333  }
334  wfProfileOut( __METHOD__ );
335  }
336 
341  public function waitForAll( $pos ) {
342  wfProfileIn( __METHOD__ );
343  $this->mWaitForPos = $pos;
344  $serverCount = count( $this->mServers );
345  for ( $i = 1; $i < $serverCount; $i++ ) {
346  if ( $this->mLoads[$i] > 0 ) {
347  $this->doWait( $i, true );
348  }
349  }
350  wfProfileOut( __METHOD__ );
351  }
352 
360  function getAnyOpenConnection( $i ) {
361  foreach ( $this->mConns as $conns ) {
362  if ( !empty( $conns[$i] ) ) {
363  return reset( $conns[$i] );
364  }
365  }
366 
367  return false;
368  }
369 
376  protected function doWait( $index, $open = false ) {
377  # Find a connection to wait on
378  $conn = $this->getAnyOpenConnection( $index );
379  if ( !$conn ) {
380  if ( !$open ) {
381  wfDebug( __METHOD__ . ": no connection open\n" );
382 
383  return false;
384  } else {
385  $conn = $this->openConnection( $index, '' );
386  if ( !$conn ) {
387  wfDebug( __METHOD__ . ": failed to open connection\n" );
388 
389  return false;
390  }
391  }
392  }
393 
394  wfDebug( __METHOD__ . ": Waiting for slave #$index to catch up...\n" );
395  $result = $conn->masterPosWait( $this->mWaitForPos, $this->mWaitTimeout );
396 
397  if ( $result == -1 || is_null( $result ) ) {
398  # Timed out waiting for slave, use master instead
399  wfDebug( __METHOD__ . ": Timed out waiting for slave #$index pos {$this->mWaitForPos}\n" );
400 
401  return false;
402  } else {
403  wfDebug( __METHOD__ . ": Done\n" );
404 
405  return true;
406  }
407  }
408 
420  public function &getConnection( $i, $groups = array(), $wiki = false ) {
421  wfProfileIn( __METHOD__ );
422 
423  if ( $i == DB_LAST ) {
424  wfProfileOut( __METHOD__ );
425  throw new MWException( 'Attempt to call ' . __METHOD__ .
426  ' with deprecated server index DB_LAST' );
427  } elseif ( $i === null || $i === false ) {
428  wfProfileOut( __METHOD__ );
429  throw new MWException( 'Attempt to call ' . __METHOD__ .
430  ' with invalid server index' );
431  }
432 
433  if ( $wiki === wfWikiID() ) {
434  $wiki = false;
435  }
436 
437  # Query groups
438  if ( $i == DB_MASTER ) {
439  $i = $this->getWriterIndex();
440  } elseif ( !is_array( $groups ) ) {
441  $groupIndex = $this->getReaderIndex( $groups, $wiki );
442  if ( $groupIndex !== false ) {
443  $serverName = $this->getServerName( $groupIndex );
444  wfDebug( __METHOD__ . ": using server $serverName for group $groups\n" );
445  $i = $groupIndex;
446  }
447  } else {
448  foreach ( $groups as $group ) {
449  $groupIndex = $this->getReaderIndex( $group, $wiki );
450  if ( $groupIndex !== false ) {
451  $serverName = $this->getServerName( $groupIndex );
452  wfDebug( __METHOD__ . ": using server $serverName for group $group\n" );
453  $i = $groupIndex;
454  break;
455  }
456  }
457  }
458 
459  # Operation-based index
460  if ( $i == DB_SLAVE ) {
461  $this->mLastError = 'Unknown error'; // reset error string
462  $i = $this->getReaderIndex( false, $wiki );
463  # Couldn't find a working server in getReaderIndex()?
464  if ( $i === false ) {
465  $this->mLastError = 'No working slave server: ' . $this->mLastError;
466  wfProfileOut( __METHOD__ );
467 
468  return $this->reportConnectionError();
469  }
470  }
471 
472  # Now we have an explicit index into the servers array
473  $conn = $this->openConnection( $i, $wiki );
474  if ( !$conn ) {
475  wfProfileOut( __METHOD__ );
476 
477  return $this->reportConnectionError();
478  }
479 
480  wfProfileOut( __METHOD__ );
481 
482  return $conn;
483  }
484 
493  public function reuseConnection( $conn ) {
494  $serverIndex = $conn->getLBInfo( 'serverIndex' );
495  $refCount = $conn->getLBInfo( 'foreignPoolRefCount' );
496  if ( $serverIndex === null || $refCount === null ) {
497  wfDebug( __METHOD__ . ": this connection was not opened as a foreign connection\n" );
498 
510  return;
511  }
512 
513  $dbName = $conn->getDBname();
514  $prefix = $conn->tablePrefix();
515  if ( strval( $prefix ) !== '' ) {
516  $wiki = "$dbName-$prefix";
517  } else {
518  $wiki = $dbName;
519  }
520  if ( $this->mConns['foreignUsed'][$serverIndex][$wiki] !== $conn ) {
521  throw new MWException( __METHOD__ . ": connection not found, has " .
522  "the connection been freed already?" );
523  }
524  $conn->setLBInfo( 'foreignPoolRefCount', --$refCount );
525  if ( $refCount <= 0 ) {
526  $this->mConns['foreignFree'][$serverIndex][$wiki] = $conn;
527  unset( $this->mConns['foreignUsed'][$serverIndex][$wiki] );
528  wfDebug( __METHOD__ . ": freed connection $serverIndex/$wiki\n" );
529  } else {
530  wfDebug( __METHOD__ . ": reference count for $serverIndex/$wiki reduced to $refCount\n" );
531  }
532  }
533 
546  public function getConnectionRef( $db, $groups = array(), $wiki = false ) {
547  return new DBConnRef( $this, $this->getConnection( $db, $groups, $wiki ) );
548  }
549 
562  public function getLazyConnectionRef( $db, $groups = array(), $wiki = false ) {
563  return new DBConnRef( $this, array( $db, $groups, $wiki ) );
564  }
565 
580  function openConnection( $i, $wiki = false ) {
581  wfProfileIn( __METHOD__ );
582  if ( $wiki !== false ) {
583  $conn = $this->openForeignConnection( $i, $wiki );
584  wfProfileOut( __METHOD__ );
585 
586  return $conn;
587  }
588  if ( isset( $this->mConns['local'][$i][0] ) ) {
589  $conn = $this->mConns['local'][$i][0];
590  } else {
591  $server = $this->mServers[$i];
592  $server['serverIndex'] = $i;
593  $conn = $this->reallyOpenConnection( $server, false );
594  if ( $conn->isOpen() ) {
595  wfDebug( "Connected to database $i at {$this->mServers[$i]['host']}\n" );
596  $this->mConns['local'][$i][0] = $conn;
597  } else {
598  wfDebug( "Failed to connect to database $i at {$this->mServers[$i]['host']}\n" );
599  $this->mErrorConnection = $conn;
600  $conn = false;
601  }
602  }
603  wfProfileOut( __METHOD__ );
604 
605  return $conn;
606  }
607 
626  function openForeignConnection( $i, $wiki ) {
627  wfProfileIn( __METHOD__ );
628  list( $dbName, $prefix ) = wfSplitWikiID( $wiki );
629  if ( isset( $this->mConns['foreignUsed'][$i][$wiki] ) ) {
630  // Reuse an already-used connection
631  $conn = $this->mConns['foreignUsed'][$i][$wiki];
632  wfDebug( __METHOD__ . ": reusing connection $i/$wiki\n" );
633  } elseif ( isset( $this->mConns['foreignFree'][$i][$wiki] ) ) {
634  // Reuse a free connection for the same wiki
635  $conn = $this->mConns['foreignFree'][$i][$wiki];
636  unset( $this->mConns['foreignFree'][$i][$wiki] );
637  $this->mConns['foreignUsed'][$i][$wiki] = $conn;
638  wfDebug( __METHOD__ . ": reusing free connection $i/$wiki\n" );
639  } elseif ( !empty( $this->mConns['foreignFree'][$i] ) ) {
640  // Reuse a connection from another wiki
641  $conn = reset( $this->mConns['foreignFree'][$i] );
642  $oldWiki = key( $this->mConns['foreignFree'][$i] );
643 
644  if ( !$conn->selectDB( $dbName ) ) {
645  $this->mLastError = "Error selecting database $dbName on server " .
646  $conn->getServer() . " from client host " . wfHostname() . "\n";
647  $this->mErrorConnection = $conn;
648  $conn = false;
649  } else {
650  $conn->tablePrefix( $prefix );
651  unset( $this->mConns['foreignFree'][$i][$oldWiki] );
652  $this->mConns['foreignUsed'][$i][$wiki] = $conn;
653  wfDebug( __METHOD__ . ": reusing free connection from $oldWiki for $wiki\n" );
654  }
655  } else {
656  // Open a new connection
657  $server = $this->mServers[$i];
658  $server['serverIndex'] = $i;
659  $server['foreignPoolRefCount'] = 0;
660  $server['foreign'] = true;
661  $conn = $this->reallyOpenConnection( $server, $dbName );
662  if ( !$conn->isOpen() ) {
663  wfDebug( __METHOD__ . ": error opening connection for $i/$wiki\n" );
664  $this->mErrorConnection = $conn;
665  $conn = false;
666  } else {
667  $conn->tablePrefix( $prefix );
668  $this->mConns['foreignUsed'][$i][$wiki] = $conn;
669  wfDebug( __METHOD__ . ": opened new connection for $i/$wiki\n" );
670  }
671  }
672 
673  // Increment reference count
674  if ( $conn ) {
675  $refCount = $conn->getLBInfo( 'foreignPoolRefCount' );
676  $conn->setLBInfo( 'foreignPoolRefCount', $refCount + 1 );
677  }
678  wfProfileOut( __METHOD__ );
679 
680  return $conn;
681  }
682 
690  function isOpen( $index ) {
691  if ( !is_integer( $index ) ) {
692  return false;
693  }
694 
695  return (bool)$this->getAnyOpenConnection( $index );
696  }
697 
708  function reallyOpenConnection( $server, $dbNameOverride = false ) {
709  if ( !is_array( $server ) ) {
710  throw new MWException( 'You must update your load-balancing configuration. ' .
711  'See DefaultSettings.php entry for $wgDBservers.' );
712  }
713 
714  if ( $dbNameOverride !== false ) {
715  $server['dbname'] = $dbNameOverride;
716  }
717 
718  # Create object
719  try {
720  $db = DatabaseBase::factory( $server['type'], $server );
721  } catch ( DBConnectionError $e ) {
722  // FIXME: This is probably the ugliest thing I have ever done to
723  // PHP. I'm half-expecting it to segfault, just out of disgust. -- TS
724  $db = $e->db;
725  }
726 
727  $db->setLBInfo( $server );
728  if ( isset( $server['fakeSlaveLag'] ) ) {
729  $db->setFakeSlaveLag( $server['fakeSlaveLag'] );
730  }
731  if ( isset( $server['fakeMaster'] ) ) {
732  $db->setFakeMaster( true );
733  }
734 
735  return $db;
736  }
737 
742  private function reportConnectionError() {
743  $conn = $this->mErrorConnection; // The connection which caused the error
744 
745  if ( !is_object( $conn ) ) {
746  // No last connection, probably due to all servers being too busy
747  wfLogDBError( "LB failure with no last connection. Connection error: {$this->mLastError}" );
748 
749  // If all servers were busy, mLastError will contain something sensible
750  throw new DBConnectionError( null, $this->mLastError );
751  } else {
752  $server = $conn->getProperty( 'mServer' );
753  wfLogDBError( "Connection error: {$this->mLastError} ({$server})" );
754  $conn->reportConnectionError( "{$this->mLastError} ({$server})" ); // throws DBConnectionError
755  }
756 
757  return false; /* not reached */
758  }
759 
763  function getWriterIndex() {
764  return 0;
765  }
766 
773  function haveIndex( $i ) {
774  return array_key_exists( $i, $this->mServers );
775  }
776 
783  function isNonZeroLoad( $i ) {
784  return array_key_exists( $i, $this->mServers ) && $this->mLoads[$i] != 0;
785  }
786 
792  function getServerCount() {
793  return count( $this->mServers );
794  }
795 
802  function getServerName( $i ) {
803  if ( isset( $this->mServers[$i]['hostName'] ) ) {
804  return $this->mServers[$i]['hostName'];
805  } elseif ( isset( $this->mServers[$i]['host'] ) ) {
806  return $this->mServers[$i]['host'];
807  } else {
808  return '';
809  }
810  }
811 
817  function getServerInfo( $i ) {
818  if ( isset( $this->mServers[$i] ) ) {
819  return $this->mServers[$i];
820  } else {
821  return false;
822  }
823  }
824 
831  function setServerInfo( $i, $serverInfo ) {
832  $this->mServers[$i] = $serverInfo;
833  }
834 
839  function getMasterPos() {
840  # If this entire request was served from a slave without opening a connection to the
841  # master (however unlikely that may be), then we can fetch the position from the slave.
842  $masterConn = $this->getAnyOpenConnection( 0 );
843  if ( !$masterConn ) {
844  $serverCount = count( $this->mServers );
845  for ( $i = 1; $i < $serverCount; $i++ ) {
846  $conn = $this->getAnyOpenConnection( $i );
847  if ( $conn ) {
848  wfDebug( "Master pos fetched from slave\n" );
849 
850  return $conn->getSlavePos();
851  }
852  }
853  } else {
854  wfDebug( "Master pos fetched from master\n" );
855 
856  return $masterConn->getMasterPos();
857  }
858 
859  return false;
860  }
861 
865  function closeAll() {
866  foreach ( $this->mConns as $conns2 ) {
867  foreach ( $conns2 as $conns3 ) {
869  foreach ( $conns3 as $conn ) {
870  $conn->close();
871  }
872  }
873  }
874  $this->mConns = array(
875  'local' => array(),
876  'foreignFree' => array(),
877  'foreignUsed' => array(),
878  );
879  }
880 
887  function closeConnecton( $conn ) {
888  wfDeprecated( __METHOD__, '1.18' );
889  $this->closeConnection( $conn );
890  }
891 
898  function closeConnection( $conn ) {
899  $done = false;
900  foreach ( $this->mConns as $i1 => $conns2 ) {
901  foreach ( $conns2 as $i2 => $conns3 ) {
902  foreach ( $conns3 as $i3 => $candidateConn ) {
903  if ( $conn === $candidateConn ) {
904  $conn->close();
905  unset( $this->mConns[$i1][$i2][$i3] );
906  $done = true;
907  break;
908  }
909  }
910  }
911  }
912  if ( !$done ) {
913  $conn->close();
914  }
915  }
916 
920  function commitAll() {
921  foreach ( $this->mConns as $conns2 ) {
922  foreach ( $conns2 as $conns3 ) {
924  foreach ( $conns3 as $conn ) {
925  if ( $conn->trxLevel() ) {
926  $conn->commit( __METHOD__, 'flush' );
927  }
928  }
929  }
930  }
931  }
932 
936  function commitMasterChanges() {
937  // Always 0, but who knows.. :)
938  $masterIndex = $this->getWriterIndex();
939  foreach ( $this->mConns as $conns2 ) {
940  if ( empty( $conns2[$masterIndex] ) ) {
941  continue;
942  }
944  foreach ( $conns2[$masterIndex] as $conn ) {
945  if ( $conn->trxLevel() && $conn->writesOrCallbacksPending() ) {
946  $conn->commit( __METHOD__, 'flush' );
947  }
948  }
949  }
950  }
951 
956  function rollbackMasterChanges() {
957  // Always 0, but who knows.. :)
958  $masterIndex = $this->getWriterIndex();
959  foreach ( $this->mConns as $conns2 ) {
960  if ( empty( $conns2[$masterIndex] ) ) {
961  continue;
962  }
964  foreach ( $conns2[$masterIndex] as $conn ) {
965  if ( $conn->trxLevel() && $conn->writesOrCallbacksPending() ) {
966  $conn->rollback( __METHOD__, 'flush' );
967  }
968  }
969  }
970  }
971 
978  function hasMasterChanges() {
979  // Always 0, but who knows.. :)
980  $masterIndex = $this->getWriterIndex();
981  foreach ( $this->mConns as $conns2 ) {
982  if ( empty( $conns2[$masterIndex] ) ) {
983  continue;
984  }
986  foreach ( $conns2[$masterIndex] as $conn ) {
987  if ( $conn->trxLevel() && $conn->writesOrCallbacksPending() ) {
988  return true;
989  }
990  }
991  }
992  return false;
993  }
994 
999  function waitTimeout( $value = null ) {
1000  return wfSetVar( $this->mWaitTimeout, $value );
1001  }
1002 
1006  function getLaggedSlaveMode() {
1007  return $this->mLaggedSlaveMode;
1008  }
1009 
1015  function allowLagged( $mode = null ) {
1016  if ( $mode === null ) {
1017  return $this->mAllowLagged;
1018  }
1019  $this->mAllowLagged = $mode;
1020 
1021  return $this->mAllowLagged;
1022  }
1023 
1027  function pingAll() {
1028  $success = true;
1029  foreach ( $this->mConns as $conns2 ) {
1030  foreach ( $conns2 as $conns3 ) {
1032  foreach ( $conns3 as $conn ) {
1033  if ( !$conn->ping() ) {
1034  $success = false;
1035  }
1036  }
1037  }
1038  }
1039 
1040  return $success;
1041  }
1042 
1048  function forEachOpenConnection( $callback, $params = array() ) {
1049  foreach ( $this->mConns as $conns2 ) {
1050  foreach ( $conns2 as $conns3 ) {
1051  foreach ( $conns3 as $conn ) {
1052  $mergedParams = array_merge( array( $conn ), $params );
1053  call_user_func_array( $callback, $mergedParams );
1054  }
1055  }
1056  }
1057  }
1058 
1068  function getMaxLag( $wiki = false ) {
1069  $maxLag = -1;
1070  $host = '';
1071  $maxIndex = 0;
1072  if ( $this->getServerCount() > 1 ) { // no replication = no lag
1073  foreach ( $this->mServers as $i => $conn ) {
1074  $conn = false;
1075  if ( $wiki === false ) {
1076  $conn = $this->getAnyOpenConnection( $i );
1077  }
1078  if ( !$conn ) {
1079  $conn = $this->openConnection( $i, $wiki );
1080  }
1081  if ( !$conn ) {
1082  continue;
1083  }
1084  $lag = $conn->getLag();
1085  if ( $lag > $maxLag ) {
1086  $maxLag = $lag;
1087  $host = $this->mServers[$i]['host'];
1088  $maxIndex = $i;
1089  }
1090  }
1091  }
1092 
1093  return array( $host, $maxLag, $maxIndex );
1094  }
1095 
1103  function getLagTimes( $wiki = false ) {
1104  # Try process cache
1105  if ( isset( $this->mLagTimes ) ) {
1106  return $this->mLagTimes;
1107  }
1108  if ( $this->getServerCount() == 1 ) {
1109  # No replication
1110  $this->mLagTimes = array( 0 => 0 );
1111  } else {
1112  # Send the request to the load monitor
1113  $this->mLagTimes = $this->getLoadMonitor()->getLagTimes(
1114  array_keys( $this->mServers ), $wiki );
1115  }
1116 
1117  return $this->mLagTimes;
1118  }
1119 
1134  function safeGetLag( $conn ) {
1135  if ( $this->getServerCount() == 1 ) {
1136  return 0;
1137  } else {
1138  return $conn->getLag();
1139  }
1140  }
1141 
1145  function clearLagTimeCache() {
1146  $this->mLagTimes = null;
1147  }
1148 }
1149 
1157 class DBConnRef implements IDatabase {
1159  protected $lb;
1162  protected $conn;
1163 
1165  protected $params;
1171  public function __construct( LoadBalancer $lb, $conn ) {
1172  $this->lb = $lb;
1173  if ( $conn instanceof DatabaseBase ) {
1174  $this->conn = $conn;
1175  } else {
1176  $this->params = $conn;
1177  }
1178  }
1179 
1180  public function __call( $name, $arguments ) {
1181  if ( $this->conn === null ) {
1182  list( $db, $groups, $wiki ) = $this->params;
1183  $this->conn = $this->lb->getConnection( $db, $groups, $wiki );
1184  }
1185 
1186  return call_user_func_array( array( $this->conn, $name ), $arguments );
1187  }
1188 
1189  function __destruct() {
1190  if ( $this->conn !== null ) {
1191  $this->lb->reuseConnection( $this->conn );
1192  }
1193  }
1194 }
LoadBalancer\isOpen
isOpen( $index)
Test if the specified index represents an open connection.
Definition: LoadBalancer.php:688
LoadBalancer\haveIndex
haveIndex( $i)
Returns true if the specified index is a valid server index.
Definition: LoadBalancer.php:771
DBConnRef\__call
__call( $name, $arguments)
Definition: LoadBalancer.php:1175
$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. $reader:XMLReader object $logInfo:Array of information Return false to stop further processing of the tag 'ImportHandlePageXMLTag':When parsing a XML tag in a page. $reader:XMLReader object $pageInfo:Array of information Return false to stop further processing of the tag 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision. $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information Return false to stop further processing of the tag 'ImportHandleToplevelXMLTag':When parsing a top level XML tag. $reader:XMLReader object Return false to stop further processing of the tag 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload. $reader:XMLReader object $revisionInfo:Array of information Return false to stop further processing of the tag '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 '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. '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 '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 '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 wfIsTrustedProxy() $ip:IP being check $result:Change this value to override the result of wfIsTrustedProxy() '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 User::isValidEmailAddr(), 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. '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 '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) '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. '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:1528
LoadBalancer\getServerName
getServerName( $i)
Get the host name or IP address of the server with the specified index Prefer a readable name if avai...
Definition: LoadBalancer.php:800
DB_MASTER
const DB_MASTER
Definition: Defines.php:56
php
skin txt MediaWiki includes four core it has been set as the default in MediaWiki since the replacing Monobook it had been been the default skin since before being replaced by Vector largely rewritten in while keeping its appearance Several legacy skins were removed in the as the burden of supporting them became too heavy to bear Those in etc for skin dependent CSS etc for skin dependent JavaScript These can also be customised on a per user by etc This feature has led to a wide variety of user styles becoming that gallery is a good place to ending in php
Definition: skin.txt:62
LoadBalancer\$mWaitForPos
bool DBMasterPos $mWaitForPos
False if not set *.
Definition: LoadBalancer.php:36
LoadBalancer\$mWaitTimeout
$mWaitTimeout
Definition: LoadBalancer.php:38
DB_LAST
const DB_LAST
Definition: Defines.php:62
LoadBalancer\closeConnection
closeConnection( $conn)
Close a connection Using this function makes sure the LoadBalancer knows the connection is closed.
Definition: LoadBalancer.php:896
LoadBalancer\getLoadMonitor
getLoadMonitor()
Get a LoadMonitor instance.
Definition: LoadBalancer.php:103
wfSetVar
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...
Definition: GlobalFunctions.php:2186
LoadBalancer\$mGroupLoads
$mGroupLoads
Definition: LoadBalancer.php:31
LoadBalancer\isNonZeroLoad
isNonZeroLoad( $i)
Returns true if the specified index is valid and has non-zero load.
Definition: LoadBalancer.php:781
DBConnRef\__destruct
__destruct()
Definition: LoadBalancer.php:1184
wfDebugLog
wfDebugLog( $logGroup, $text, $dest='all')
Send a line to a supplementary debug log file, if configured, or main debug log if not.
Definition: GlobalFunctions.php:1087
LoadBalancer\$mLastError
$mLastError
Definition: LoadBalancer.php:39
wfProfileIn
wfProfileIn( $functionname)
Begin profiling of a function.
Definition: Profiler.php:33
LoadBalancer\pingAll
pingAll()
Definition: LoadBalancer.php:1025
$params
$params
Definition: styleTest.css.php:40
wfHostname
wfHostname()
Fetch server name for use in error reporting etc.
Definition: GlobalFunctions.php:1833
LoadBalancer\$mLagTimes
$mLagTimes
Definition: LoadBalancer.php:40
wfSplitWikiID
wfSplitWikiID( $wiki)
Split a wiki ID into DB name and table prefix.
Definition: GlobalFunctions.php:3676
LoadBalancer\safeGetLag
safeGetLag( $conn)
Get the lag in seconds for a given connection, or zero if this load balancer does not have replicatio...
Definition: LoadBalancer.php:1132
LoadBalancer\parentInfo
parentInfo( $x=null)
Get or set arbitrary data used by the parent object, usually an LBFactory.
Definition: LoadBalancer.php:117
LoadBalancer\sleep
sleep( $t)
Wait for a specified number of microseconds, and return the period waited.
Definition: LoadBalancer.php:306
LoadBalancer\$mConns
$mConns
Definition: LoadBalancer.php:31
LoadBalancer\closeAll
closeAll()
Close all open connections.
Definition: LoadBalancer.php:863
DBMasterPos
An object representing a master or slave position in a replicated setup.
Definition: DatabaseUtility.php:323
LoadBalancer\$mErrorConnection
bool DatabaseBase $mErrorConnection
Database connection that caused a problem *.
Definition: LoadBalancer.php:33
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
$success
$success
Definition: Utf8Test.php:91
ProfileSection
Class for handling function-scope profiling.
Definition: Profiler.php:60
LoadBalancer\reallyOpenConnection
reallyOpenConnection( $server, $dbNameOverride=false)
Really opens a connection.
Definition: LoadBalancer.php:706
DBConnRef
Helper class to handle automatically marking connectons as reusable (via RAII pattern) as well handli...
Definition: LoadBalancer.php:1155
MWException
MediaWiki exception.
Definition: MWException.php:26
LoadBalancer\waitFor
waitFor( $pos)
Set the master wait position If a DB_SLAVE connection has been opened already, waits Otherwise sets a...
Definition: LoadBalancer.php:321
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
Definition: GlobalFunctions.php:1174
LoadBalancer\getRandomNonLagged
getRandomNonLagged( $loads, $wiki=false)
Definition: LoadBalancer.php:139
LoadBalancer\getWriterIndex
getWriterIndex()
Definition: LoadBalancer.php:761
LoadBalancer\getLazyConnectionRef
getLazyConnectionRef( $db, $groups=array(), $wiki=false)
Get a database connection handle reference without connecting yet.
Definition: LoadBalancer.php:560
LoadBalancer\commitMasterChanges
commitMasterChanges()
Issue COMMIT only on master, only if queries were done on connection.
Definition: LoadBalancer.php:934
LoadBalancer\reportConnectionError
reportConnectionError()
Definition: LoadBalancer.php:740
LoadBalancer\waitForAll
waitForAll( $pos)
Set the master wait position and wait for ALL slaves to catch up to it.
Definition: LoadBalancer.php:339
LoadBalancer\$mLoadMonitor
$mLoadMonitor
Definition: LoadBalancer.php:41
wfProfileOut
wfProfileOut( $functionname='missing')
Stop profiling of a function.
Definition: Profiler.php:46
LoadBalancer\getMaxLag
getMaxLag( $wiki=false)
Get the hostname and lag time of the most-lagged slave.
Definition: LoadBalancer.php:1066
LoadBalancer\getConnectionRef
getConnectionRef( $db, $groups=array(), $wiki=false)
Get a database connection handle reference.
Definition: LoadBalancer.php:544
LoadBalancer\$mServers
$mServers
Definition: LoadBalancer.php:31
LoadBalancer\$mLoadMonitorClass
$mLoadMonitorClass
Definition: LoadBalancer.php:41
array
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
LoadBalancer\$mAllowLagged
$mAllowLagged
Definition: LoadBalancer.php:34
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
DBConnectionError
Definition: DatabaseError.php:98
DatabaseBase\factory
static factory( $dbType, $p=array())
Given a DB type, construct the name of the appropriate child class of DatabaseBase.
Definition: Database.php:808
LoadBalancer\doWait
doWait( $index, $open=false)
Wait for a given slave to catch up to the master pos stored in $this.
Definition: LoadBalancer.php:374
LoadBalancer\getLaggedSlaveMode
getLaggedSlaveMode()
Definition: LoadBalancer.php:1004
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
LoadBalancer\commitAll
commitAll()
Commit transactions on all open connections.
Definition: LoadBalancer.php:918
LoadBalancer\__construct
__construct( $params)
Definition: LoadBalancer.php:50
LoadBalancer
Database load balancing object.
Definition: LoadBalancer.php:30
LoadBalancer\allowLagged
allowLagged( $mode=null)
Disables/enables lag checks.
Definition: LoadBalancer.php:1013
$section
$section
Definition: Utf8Test.php:88
LoadBalancer\getReaderIndex
getReaderIndex( $group=false, $wiki=false)
Get the index of the reader connection, which may be a slave This takes into account load ratios and ...
Definition: LoadBalancer.php:188
wfDebug
wfDebug( $text, $dest='all')
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:980
wfWikiID
wfWikiID()
Get an ASCII string identifying this wiki This is used as a prefix in memcached keys.
Definition: GlobalFunctions.php:3660
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:336
$value
$value
Definition: styleTest.css.php:45
wfLogDBError
wfLogDBError( $text)
Log for database errors.
Definition: GlobalFunctions.php:1134
LoadBalancer\getMasterPos
getMasterPos()
Get the current master position for chronology control purposes.
Definition: LoadBalancer.php:837
DatabaseBase
Database abstraction object.
Definition: Database.php:219
LoadBalancer\getServerInfo
getServerInfo( $i)
Return the server info structure for a given index, or false if the index is invalid.
Definition: LoadBalancer.php:815
LoadBalancer\pickRandom
pickRandom( $weights)
Given an array of non-normalised probabilities, this function will select an element and return the a...
Definition: LoadBalancer.php:130
IDatabase
Interface for classes that implement or wrap DatabaseBase.
Definition: Database.php:212
LoadBalancer\getAnyOpenConnection
getAnyOpenConnection( $i)
Get any open connection to a given server index, local or foreign Returns false if there is no connec...
Definition: LoadBalancer.php:358
LoadBalancer\$mParentInfo
$mParentInfo
Definition: LoadBalancer.php:40
LoadBalancer\setServerInfo
setServerInfo( $i, $serverInfo)
Sets the server info structure for the given index.
Definition: LoadBalancer.php:829
LoadBalancer\$mLaggedSlaveMode
$mLaggedSlaveMode
Definition: LoadBalancer.php:39
LoadBalancer\waitTimeout
waitTimeout( $value=null)
Definition: LoadBalancer.php:997
DB_SLAVE
const DB_SLAVE
Definition: Defines.php:55
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
LoadBalancer\getLagTimes
getLagTimes( $wiki=false)
Get lag time for each server Results are cached for a short time in memcached, and indefinitely in th...
Definition: LoadBalancer.php:1101
LoadBalancer\openConnection
openConnection( $i, $wiki=false)
Open a connection to the server given by the specified index Index must be an actual index into the a...
Definition: LoadBalancer.php:578
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
LoadBalancer\getConnection
& getConnection( $i, $groups=array(), $wiki=false)
Get a connection by index This is the main entry point for this class.
Definition: LoadBalancer.php:418
LoadBalancer\openForeignConnection
openForeignConnection( $i, $wiki)
Open a connection to a foreign DB, or return one if it is already open.
Definition: LoadBalancer.php:624
DBConnRef\__construct
__construct(LoadBalancer $lb, $conn)
Definition: LoadBalancer.php:1166
DBConnRef\$params
array null $params
Definition: LoadBalancer.php:1160
LoadBalancer\reuseConnection
reuseConnection( $conn)
Mark a foreign connection as being available for reuse under a different DB name or prefix.
Definition: LoadBalancer.php:491
$t
$t
Definition: testCompression.php:65
LoadBalancer\forEachOpenConnection
forEachOpenConnection( $callback, $params=array())
Call a function with each open connection object.
Definition: LoadBalancer.php:1046
LoadBalancer\hasMasterChanges
hasMasterChanges()
Determine if there are any pending changes that need to be rolled back or committed.
Definition: LoadBalancer.php:976
LoadBalancer\closeConnecton
closeConnecton( $conn)
Deprecated function, typo in function name.
Definition: LoadBalancer.php:885
LoadBalancer\getServerCount
getServerCount()
Get the number of defined servers (not the number of open connections)
Definition: LoadBalancer.php:790
LoadBalancer\rollbackMasterChanges
rollbackMasterChanges()
Issue ROLLBACK only on master, only if queries were done on connection.
Definition: LoadBalancer.php:954
DBConnRef\$conn
DatabaseBase null $conn
Definition: LoadBalancer.php:1158
LoadBalancer\$mLoads
$mLoads
Definition: LoadBalancer.php:31
DBConnRef\$lb
LoadBalancer $lb
Definition: LoadBalancer.php:1156
LoadBalancer\clearLagTimeCache
clearLagTimeCache()
Clear the cache for getLagTimes.
Definition: LoadBalancer.php:1143
$e
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging $e
Definition: hooks.txt:1632
LoadBalancer\$mReadIndex
$mReadIndex
Definition: LoadBalancer.php:34