Re-encode in utf-8 and removed trailing whitespaces
[lhc/web/wiklou.git] / includes / LBFactory_Multi.php
1 <?php
2 /**
3 * @file
4 * @ingroup Database
5 */
6
7
8 /**
9 * A multi-wiki, multi-master factory for Wikimedia and similar installations.
10 * Ignores the old configuration globals
11 *
12 * Configuration:
13 * sectionsByDB A map of database names to section names
14 *
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 ) )
17 *
18 * serverTemplate A server info associative array as documented for $wgDBservers. The host,
19 * hostName and load entries will be overridden.
20 *
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 ) ) )
23 *
24 * groupLoadsByDB A 3-d map giving server load ratios by DB name.
25 *
26 * hostsByName A map of hostname to IP address.
27 *
28 * externalLoads A map of external storage cluster name to server load map
29 *
30 * externalTemplateOverrides A set of server info keys overriding serverTemplate for external storage
31 *
32 * templateOverridesByServer A 2-d map overriding serverTemplate and externalTemplateOverrides on a
33 * server-by-server basis. Applies to both core and external storage.
34 *
35 * templateOverridesByCluster A 2-d map overriding the server info by external storage cluster
36 *
37 * masterTemplateOverrides An override array for all master servers.
38 *
39 * @ingroup Database
40 */
41 class LBFactory_Multi extends LBFactory {
42 // Required settings
43 var $sectionsByDB, $sectionLoads, $serverTemplate;
44 // Optional settings
45 var $groupLoadsBySection = array(), $groupLoadsByDB = array(), $hostsByName = array();
46 var $externalLoads = array(), $externalTemplateOverrides, $templateOverridesByServer;
47 var $templateOverridesByCluster, $masterTemplateOverrides;
48 // Other stuff
49 var $conf, $mainLBs = array(), $extLBs = array();
50 var $localSection = null;
51
52 function __construct( $conf ) {
53 $this->chronProt = new ChronologyProtector;
54 $this->conf = $conf;
55 $required = array( 'sectionsByDB', 'sectionLoads', 'serverTemplate' );
56 $optional = array( 'groupLoadsBySection', 'groupLoadsByDB', 'hostsByName',
57 'externalLoads', 'externalTemplateOverrides', 'templateOverridesByServer',
58 'templateOverridesByCluster', 'masterTemplateOverrides' );
59
60 foreach ( $required as $key ) {
61 if ( !isset( $conf[$key] ) ) {
62 throw new MWException( __CLASS__.": $key is required in configuration" );
63 }
64 $this->$key = $conf[$key];
65 }
66
67 foreach ( $optional as $key ) {
68 if ( isset( $conf[$key] ) ) {
69 $this->$key = $conf[$key];
70 }
71 }
72 }
73
74 function getSectionForWiki( $wiki ) {
75 list( $dbName, $prefix ) = $this->getDBNameAndPrefix( $wiki );
76 if ( isset( $this->sectionsByDB[$dbName] ) ) {
77 return $this->sectionsByDB[$dbName];
78 } else {
79 return 'DEFAULT';
80 }
81 }
82
83 function getMainLB( $wiki = false ) {
84 // Determine section
85 if ( $wiki === false ) {
86 if ( $this->localSection === null ) {
87 $this->localSection = $this->getSectionForWiki( $wiki );
88 }
89 $section = $this->localSection;
90 } else {
91 $section = $this->getSectionForWiki( $wiki );
92 }
93
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];
99 }
100 if ( isset( $this->groupLoadsBySection[$section] ) ) {
101 $groupLoads = array_merge_recursive( $groupLoads, $this->groupLoadsBySection[$section] );
102 }
103 $this->mainLBs[$section] = $this->newLoadBalancer( $this->serverTemplate,
104 $this->sectionLoads[$section], $groupLoads, "main-$section" );
105 $this->chronProt->initLB( $this->mainLBs[$section] );
106 }
107 return $this->mainLBs[$section];
108 }
109
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\"" );
114 }
115 $template = $this->serverTemplate;
116 if ( isset( $this->externalTemplateOverrides ) ) {
117 $template = $this->externalTemplateOverrides + $template;
118 }
119 if ( isset( $this->templateOverridesByCluster[$cluster] ) ) {
120 $template = $this->templateOverridesByCluster[$cluster] + $template;
121 }
122 $this->extLBs[$cluster] = $this->newLoadBalancer( $template,
123 $this->externalLoads[$cluster], array(), "ext-$cluster" );
124 }
125 return $this->extLBs[$cluster];
126 }
127
128 /**
129 * Make a new load balancer object based on template and load array
130 */
131 function newLoadBalancer( $template, $loads, $groupLoads, $id ) {
132 global $wgMasterWaitTimeout;
133 $servers = $this->makeServerArray( $template, $loads, $groupLoads );
134 $lb = new LoadBalancer( $servers, false, $wgMasterWaitTimeout );
135 $lb->parentInfo( array( 'id' => $id ) );
136 return $lb;
137 }
138
139 /**
140 * Make a server array as expected by LoadBalancer::__construct, using a template and load array
141 */
142 function makeServerArray( $template, $loads, $groupLoads ) {
143 $servers = array();
144 $master = true;
145 $groupLoadsByServer = $this->reindexGroupLoads( $groupLoads );
146 foreach ( $groupLoadsByServer as $server => $stuff ) {
147 if ( !isset( $loads[$server] ) ) {
148 $loads[$server] = 0;
149 }
150 }
151 foreach ( $loads as $serverName => $load ) {
152 $serverInfo = $template;
153 if ( $master ) {
154 $serverInfo['master'] = true;
155 if ( isset( $this->masterTemplateOverrides ) ) {
156 $serverInfo = $this->masterTemplateOverrides + $serverInfo;
157 }
158 $master = false;
159 }
160 if ( isset( $this->templateOverridesByServer[$serverName] ) ) {
161 $serverInfo = $this->templateOverridesByServer[$serverName] + $serverInfo;
162 }
163 if ( isset( $groupLoadsByServer[$serverName] ) ) {
164 $serverInfo['groupLoads'] = $groupLoadsByServer[$serverName];
165 }
166 if ( isset( $this->hostsByName[$serverName] ) ) {
167 $serverInfo['host'] = $this->hostsByName[$serverName];
168 } else {
169 $serverInfo['host'] = $serverName;
170 }
171 $serverInfo['hostName'] = $serverName;
172 $serverInfo['load'] = $load;
173 $servers[] = $serverInfo;
174 }
175 return $servers;
176 }
177
178 /**
179 * Take a group load array indexed by group then server, and reindex it by server then group
180 */
181 function reindexGroupLoads( $groupLoads ) {
182 $reindexed = array();
183 foreach ( $groupLoads as $group => $loads ) {
184 foreach ( $loads as $server => $load ) {
185 $reindexed[$server][$group] = $load;
186 }
187 }
188 return $reindexed;
189 }
190
191 /**
192 * Get the database name and prefix based on the wiki ID
193 */
194 function getDBNameAndPrefix( $wiki = false ) {
195 if ( $wiki === false ) {
196 global $wgDBname, $wgDBprefix;
197 return array( $wgDBname, $wgDBprefix );
198 } else {
199 return wfSplitWikiID( $wiki );
200 }
201 }
202
203 /**
204 * Execute a function for each tracked load balancer
205 * The callback is called with the load balancer as the first parameter,
206 * and $params passed as the subsequent parameters.
207 */
208 function forEachLB( $callback, $params = array() ) {
209 foreach ( $this->mainLBs as $lb ) {
210 call_user_func_array( $callback, array_merge( array( $lb ), $params ) );
211 }
212 foreach ( $this->extLBs as $lb ) {
213 call_user_func_array( $callback, array_merge( array( $lb ), $params ) );
214 }
215 }
216
217 function shutdown() {
218 foreach ( $this->mainLBs as $lb ) {
219 $this->chronProt->shutdownLB( $lb );
220 }
221 $this->chronProt->shutdown();
222 $this->commitMasterChanges();
223 }
224 }