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 $localSection = null;
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 ) {
75 list( $dbName, $prefix ) = $this->getDBNameAndPrefix( $wiki );
76 if ( isset( $this->sectionsByDB
[$dbName] ) ) {
77 return $this->sectionsByDB
[$dbName];
83 function getMainLB( $wiki = false ) {
85 if ( $wiki === false ) {
86 if ( $this->localSection
=== null ) {
87 $this->localSection
= $this->getSectionForWiki( $wiki );
89 $section = $this->localSection
;
91 $section = $this->getSectionForWiki( $wiki );
94 if ( !isset( $this->mainLBs
[$section] ) ) {
95 list( $dbName, $prefix ) = $this->getDBNameAndPrefix( $wiki );
96 $groupLoads = array();
97 if ( isset( $this->groupLoadsByDB
[$dbName] ) ) {
98 $groupLoads = $this->groupLoadsByDB
[$dbName];
100 if ( isset( $this->groupLoadsBySection
[$section] ) ) {
101 $groupLoads = array_merge_recursive( $groupLoads, $this->groupLoadsBySection
[$section] );
103 $this->mainLBs
[$section] = $this->newLoadBalancer( $this->serverTemplate
,
104 $this->sectionLoads
[$section], $groupLoads, "main-$section" );
105 $this->chronProt
->initLB( $this->mainLBs
[$section] );
107 return $this->mainLBs
[$section];
110 function &getExternalLB( $cluster, $wiki = false ) {
111 if ( !isset( $this->extLBs
[$cluster] ) ) {
112 if ( !isset( $this->externalLoads
[$cluster] ) ) {
113 throw new MWException( __METHOD__
.": Unknown cluster \"$cluster\"" );
115 $template = $this->serverTemplate
;
116 if ( isset( $this->externalTemplateOverrides
) ) {
117 $template = $this->externalTemplateOverrides +
$template;
119 if ( isset( $this->templateOverridesByCluster
[$cluster] ) ) {
120 $template = $this->templateOverridesByCluster
[$cluster] +
$template;
122 $this->extLBs
[$cluster] = $this->newLoadBalancer( $template,
123 $this->externalLoads
[$cluster], array(), "ext-$cluster" );
125 return $this->extLBs
[$cluster];
129 * Make a new load balancer object based on template and load array
131 function newLoadBalancer( $template, $loads, $groupLoads, $id ) {
132 global $wgMasterWaitTimeout;
133 $servers = $this->makeServerArray( $template, $loads, $groupLoads );
134 $lb = new LoadBalancer( array(
135 'servers' => $servers,
136 'masterWaitTimeout' => $wgMasterWaitTimeout
138 $lb->parentInfo( array( 'id' => $id ) );
143 * Make a server array as expected by LoadBalancer::__construct, using a template and load array
145 function makeServerArray( $template, $loads, $groupLoads ) {
148 $groupLoadsByServer = $this->reindexGroupLoads( $groupLoads );
149 foreach ( $groupLoadsByServer as $server => $stuff ) {
150 if ( !isset( $loads[$server] ) ) {
154 foreach ( $loads as $serverName => $load ) {
155 $serverInfo = $template;
157 $serverInfo['master'] = true;
158 if ( isset( $this->masterTemplateOverrides
) ) {
159 $serverInfo = $this->masterTemplateOverrides +
$serverInfo;
163 if ( isset( $this->templateOverridesByServer
[$serverName] ) ) {
164 $serverInfo = $this->templateOverridesByServer
[$serverName] +
$serverInfo;
166 if ( isset( $groupLoadsByServer[$serverName] ) ) {
167 $serverInfo['groupLoads'] = $groupLoadsByServer[$serverName];
169 if ( isset( $this->hostsByName
[$serverName] ) ) {
170 $serverInfo['host'] = $this->hostsByName
[$serverName];
172 $serverInfo['host'] = $serverName;
174 $serverInfo['hostName'] = $serverName;
175 $serverInfo['load'] = $load;
176 $servers[] = $serverInfo;
182 * Take a group load array indexed by group then server, and reindex it by server then group
184 function reindexGroupLoads( $groupLoads ) {
185 $reindexed = array();
186 foreach ( $groupLoads as $group => $loads ) {
187 foreach ( $loads as $server => $load ) {
188 $reindexed[$server][$group] = $load;
195 * Get the database name and prefix based on the wiki ID
197 function getDBNameAndPrefix( $wiki = false ) {
198 if ( $wiki === false ) {
199 global $wgDBname, $wgDBprefix;
200 return array( $wgDBname, $wgDBprefix );
202 return wfSplitWikiID( $wiki );
207 * Execute a function for each tracked load balancer
208 * The callback is called with the load balancer as the first parameter,
209 * and $params passed as the subsequent parameters.
211 function forEachLB( $callback, $params = array() ) {
212 foreach ( $this->mainLBs
as $lb ) {
213 call_user_func_array( $callback, array_merge( array( $lb ), $params ) );
215 foreach ( $this->extLBs
as $lb ) {
216 call_user_func_array( $callback, array_merge( array( $lb ), $params ) );
220 function shutdown() {
221 foreach ( $this->mainLBs
as $lb ) {
222 $this->chronProt
->shutdownLB( $lb );
224 $this->chronProt
->shutdown();
225 $this->commitMasterChanges();