9 * A multi-wiki, multi-master factory for Wikimedia and similar installations.
10 * Ignores the old configuration globals
13 * sectionsByDB A map of database names to section names
15 * sectionLoads A 2-d map. For each section, gives a map of server names to load ratios.
16 * For example: array( 'section1' => array( 'db1' => 100, 'db2' => 100 ) )
18 * serverTemplate A server info associative array as documented for $wgDBservers. The host,
19 * hostName and load entries will be overridden.
21 * groupLoadsBySection A 3-d map giving server load ratios for each section and group. For example:
22 * array( 'section1' => array( 'group1' => array( 'db1' => 100, 'db2' => 100 ) ) )
24 * groupLoadsByDB A 3-d map giving server load ratios by DB name.
26 * hostsByName A map of hostname to IP address.
28 * externalLoads A map of external storage cluster name to server load map
30 * externalTemplateOverrides A set of server info keys overriding serverTemplate for external storage
32 * templateOverridesByServer A 2-d map overriding serverTemplate and externalTemplateOverrides on a
33 * server-by-server basis. Applies to both core and external storage.
35 * templateOverridesByCluster A 2-d map overriding the server info by external storage cluster
37 * masterTemplateOverrides An override array for all master servers.
41 class LBFactory_Multi
extends LBFactory
{
43 var $sectionsByDB, $sectionLoads, $serverTemplate;
45 var $groupLoadsBySection = array(), $groupLoadsByDB = array(), $hostsByName = array();
46 var $externalLoads = array(), $externalTemplateOverrides, $templateOverridesByServer;
47 var $templateOverridesByCluster, $masterTemplateOverrides;
49 var $conf, $mainLBs = array(), $extLBs = array();
50 var $lastWiki, $lastSection;
52 function __construct( $conf ) {
53 $this->chronProt
= new ChronologyProtector
;
55 $required = array( 'sectionsByDB', 'sectionLoads', 'serverTemplate' );
56 $optional = array( 'groupLoadsBySection', 'groupLoadsByDB', 'hostsByName',
57 'externalLoads', 'externalTemplateOverrides', 'templateOverridesByServer',
58 'templateOverridesByCluster', 'masterTemplateOverrides' );
60 foreach ( $required as $key ) {
61 if ( !isset( $conf[$key] ) ) {
62 throw new MWException( __CLASS__
.": $key is required in configuration" );
64 $this->$key = $conf[$key];
67 foreach ( $optional as $key ) {
68 if ( isset( $conf[$key] ) ) {
69 $this->$key = $conf[$key];
74 function getSectionForWiki( $wiki = false ) {
75 if ( $this->lastWiki
=== $wiki ) {
76 return $this->lastSection
;
78 list( $dbName, $prefix ) = $this->getDBNameAndPrefix( $wiki );
79 if ( isset( $this->sectionsByDB
[$dbName] ) ) {
80 $section = $this->sectionsByDB
[$dbName];
84 $this->lastSection
= $section;
85 $this->lastWiki
= $wiki;
89 function newMainLB( $wiki = false ) {
90 list( $dbName, $prefix ) = $this->getDBNameAndPrefix( $wiki );
91 $section = $this->getSectionForWiki( $wiki );
92 $groupLoads = array();
93 if ( isset( $this->groupLoadsByDB
[$dbName] ) ) {
94 $groupLoads = $this->groupLoadsByDB
[$dbName];
96 if ( isset( $this->groupLoadsBySection
[$section] ) ) {
97 $groupLoads = array_merge_recursive( $groupLoads, $this->groupLoadsBySection
[$section] );
99 return $this->newLoadBalancer( $this->serverTemplate
, $this->sectionLoads
[$section], $groupLoads );
102 function getMainLB( $wiki = false ) {
103 $section = $this->getSectionForWiki( $wiki );
104 if ( !isset( $this->mainLBs
[$section] ) ) {
105 $lb = $this->newMainLB( $wiki, $section );
106 $this->chronProt
->initLB( $lb );
107 $lb->parentInfo( array( 'id' => "main-$section" ) );
108 $this->mainLBs
[$section] = $lb;
110 return $this->mainLBs
[$section];
113 function newExternalLB( $cluster, $wiki = false ) {
114 if ( !isset( $this->externalLoads
[$cluster] ) ) {
115 throw new MWException( __METHOD__
.": Unknown cluster \"$cluster\"" );
117 $template = $this->serverTemplate
;
118 if ( isset( $this->externalTemplateOverrides
) ) {
119 $template = $this->externalTemplateOverrides +
$template;
121 if ( isset( $this->templateOverridesByCluster
[$cluster] ) ) {
122 $template = $this->templateOverridesByCluster
[$cluster] +
$template;
124 return $this->newLoadBalancer( $template, $this->externalLoads
[$cluster], array() );
127 function &getExternalLB( $cluster, $wiki = false ) {
128 if ( !isset( $this->extLBs
[$cluster] ) ) {
129 $this->extLBs
[$cluster] = $this->newExternalLB( $cluster, $wiki );
130 $this->extLBs
[$cluster]->parentInfo( array( 'id' => "ext-$cluster" ) );
132 return $this->extLBs
[$cluster];
136 * Make a new load balancer object based on template and load array
138 function newLoadBalancer( $template, $loads, $groupLoads ) {
139 global $wgMasterWaitTimeout;
140 $servers = $this->makeServerArray( $template, $loads, $groupLoads );
141 $lb = new LoadBalancer( array(
142 'servers' => $servers,
143 'masterWaitTimeout' => $wgMasterWaitTimeout
149 * Make a server array as expected by LoadBalancer::__construct, using a template and load array
151 function makeServerArray( $template, $loads, $groupLoads ) {
154 $groupLoadsByServer = $this->reindexGroupLoads( $groupLoads );
155 foreach ( $groupLoadsByServer as $server => $stuff ) {
156 if ( !isset( $loads[$server] ) ) {
160 foreach ( $loads as $serverName => $load ) {
161 $serverInfo = $template;
163 $serverInfo['master'] = true;
164 if ( isset( $this->masterTemplateOverrides
) ) {
165 $serverInfo = $this->masterTemplateOverrides +
$serverInfo;
169 if ( isset( $this->templateOverridesByServer
[$serverName] ) ) {
170 $serverInfo = $this->templateOverridesByServer
[$serverName] +
$serverInfo;
172 if ( isset( $groupLoadsByServer[$serverName] ) ) {
173 $serverInfo['groupLoads'] = $groupLoadsByServer[$serverName];
175 if ( isset( $this->hostsByName
[$serverName] ) ) {
176 $serverInfo['host'] = $this->hostsByName
[$serverName];
178 $serverInfo['host'] = $serverName;
180 $serverInfo['hostName'] = $serverName;
181 $serverInfo['load'] = $load;
182 $servers[] = $serverInfo;
188 * Take a group load array indexed by group then server, and reindex it by server then group
190 function reindexGroupLoads( $groupLoads ) {
191 $reindexed = array();
192 foreach ( $groupLoads as $group => $loads ) {
193 foreach ( $loads as $server => $load ) {
194 $reindexed[$server][$group] = $load;
201 * Get the database name and prefix based on the wiki ID
203 function getDBNameAndPrefix( $wiki = false ) {
204 if ( $wiki === false ) {
205 global $wgDBname, $wgDBprefix;
206 return array( $wgDBname, $wgDBprefix );
208 return wfSplitWikiID( $wiki );
213 * Execute a function for each tracked load balancer
214 * The callback is called with the load balancer as the first parameter,
215 * and $params passed as the subsequent parameters.
217 function forEachLB( $callback, $params = array() ) {
218 foreach ( $this->mainLBs
as $lb ) {
219 call_user_func_array( $callback, array_merge( array( $lb ), $params ) );
221 foreach ( $this->extLBs
as $lb ) {
222 call_user_func_array( $callback, array_merge( array( $lb ), $params ) );
226 function shutdown() {
227 foreach ( $this->mainLBs
as $lb ) {
228 $this->chronProt
->shutdownLB( $lb );
230 $this->chronProt
->shutdown();
231 $this->commitMasterChanges();