* Allow $wgDiff3=false
[lhc/web/wiklou.git] / includes / db / 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 $lastWiki, $lastSection;
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 = false ) {
75 if ( $this->lastWiki === $wiki ) {
76 return $this->lastSection;
77 }
78 list( $dbName, $prefix ) = $this->getDBNameAndPrefix( $wiki );
79 if ( isset( $this->sectionsByDB[$dbName] ) ) {
80 $section = $this->sectionsByDB[$dbName];
81 } else {
82 $section = 'DEFAULT';
83 }
84 $this->lastSection = $section;
85 $this->lastWiki = $wiki;
86 return $section;
87 }
88
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];
95 }
96 if ( isset( $this->groupLoadsBySection[$section] ) ) {
97 $groupLoads = array_merge_recursive( $groupLoads, $this->groupLoadsBySection[$section] );
98 }
99 return $this->newLoadBalancer( $this->serverTemplate, $this->sectionLoads[$section], $groupLoads );
100 }
101
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;
109 }
110 return $this->mainLBs[$section];
111 }
112
113 function newExternalLB( $cluster, $wiki = false ) {
114 if ( !isset( $this->externalLoads[$cluster] ) ) {
115 throw new MWException( __METHOD__.": Unknown cluster \"$cluster\"" );
116 }
117 $template = $this->serverTemplate;
118 if ( isset( $this->externalTemplateOverrides ) ) {
119 $template = $this->externalTemplateOverrides + $template;
120 }
121 if ( isset( $this->templateOverridesByCluster[$cluster] ) ) {
122 $template = $this->templateOverridesByCluster[$cluster] + $template;
123 }
124 return $this->newLoadBalancer( $template, $this->externalLoads[$cluster], array() );
125 }
126
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" ) );
131 }
132 return $this->extLBs[$cluster];
133 }
134
135 /**
136 * Make a new load balancer object based on template and load array
137 */
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
144 ));
145 return $lb;
146 }
147
148 /**
149 * Make a server array as expected by LoadBalancer::__construct, using a template and load array
150 */
151 function makeServerArray( $template, $loads, $groupLoads ) {
152 $servers = array();
153 $master = true;
154 $groupLoadsByServer = $this->reindexGroupLoads( $groupLoads );
155 foreach ( $groupLoadsByServer as $server => $stuff ) {
156 if ( !isset( $loads[$server] ) ) {
157 $loads[$server] = 0;
158 }
159 }
160 foreach ( $loads as $serverName => $load ) {
161 $serverInfo = $template;
162 if ( $master ) {
163 $serverInfo['master'] = true;
164 if ( isset( $this->masterTemplateOverrides ) ) {
165 $serverInfo = $this->masterTemplateOverrides + $serverInfo;
166 }
167 $master = false;
168 }
169 if ( isset( $this->templateOverridesByServer[$serverName] ) ) {
170 $serverInfo = $this->templateOverridesByServer[$serverName] + $serverInfo;
171 }
172 if ( isset( $groupLoadsByServer[$serverName] ) ) {
173 $serverInfo['groupLoads'] = $groupLoadsByServer[$serverName];
174 }
175 if ( isset( $this->hostsByName[$serverName] ) ) {
176 $serverInfo['host'] = $this->hostsByName[$serverName];
177 } else {
178 $serverInfo['host'] = $serverName;
179 }
180 $serverInfo['hostName'] = $serverName;
181 $serverInfo['load'] = $load;
182 $servers[] = $serverInfo;
183 }
184 return $servers;
185 }
186
187 /**
188 * Take a group load array indexed by group then server, and reindex it by server then group
189 */
190 function reindexGroupLoads( $groupLoads ) {
191 $reindexed = array();
192 foreach ( $groupLoads as $group => $loads ) {
193 foreach ( $loads as $server => $load ) {
194 $reindexed[$server][$group] = $load;
195 }
196 }
197 return $reindexed;
198 }
199
200 /**
201 * Get the database name and prefix based on the wiki ID
202 */
203 function getDBNameAndPrefix( $wiki = false ) {
204 if ( $wiki === false ) {
205 global $wgDBname, $wgDBprefix;
206 return array( $wgDBname, $wgDBprefix );
207 } else {
208 return wfSplitWikiID( $wiki );
209 }
210 }
211
212 /**
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.
216 */
217 function forEachLB( $callback, $params = array() ) {
218 foreach ( $this->mainLBs as $lb ) {
219 call_user_func_array( $callback, array_merge( array( $lb ), $params ) );
220 }
221 foreach ( $this->extLBs as $lb ) {
222 call_user_func_array( $callback, array_merge( array( $lb ), $params ) );
223 }
224 }
225
226 function shutdown() {
227 foreach ( $this->mainLBs as $lb ) {
228 $this->chronProt->shutdownLB( $lb );
229 }
230 $this->chronProt->shutdown();
231 $this->commitMasterChanges();
232 }
233 }