]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/db/LBFactory_Multi.php
MediaWiki 1.14.0
[autoinstallsdev/mediawiki.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  *     readOnlyBySection           A map of section name to read-only message. Missing or false for read/write.
40  *
41  * @ingroup Database
42  */
43 class LBFactory_Multi extends LBFactory {
44         // Required settings
45         var $sectionsByDB, $sectionLoads, $serverTemplate;
46         // Optional settings
47         var $groupLoadsBySection = array(), $groupLoadsByDB = array(), $hostsByName = array();
48         var $externalLoads = array(), $externalTemplateOverrides, $templateOverridesByServer;
49         var $templateOverridesByCluster, $masterTemplateOverrides, $readOnlyBySection = array();
50         // Other stuff
51         var $conf, $mainLBs = array(), $extLBs = array();
52         var $lastWiki, $lastSection;
53
54         function __construct( $conf ) {
55                 $this->chronProt = new ChronologyProtector;
56                 $this->conf = $conf;
57                 $required = array( 'sectionsByDB', 'sectionLoads', 'serverTemplate' );
58                 $optional = array( 'groupLoadsBySection', 'groupLoadsByDB', 'hostsByName',
59                         'externalLoads', 'externalTemplateOverrides', 'templateOverridesByServer',
60                         'templateOverridesByCluster', 'masterTemplateOverrides', 
61                         'readOnlyBySection' );
62
63                 foreach ( $required as $key ) {
64                         if ( !isset( $conf[$key] ) ) {
65                                 throw new MWException( __CLASS__.": $key is required in configuration" );
66                         }
67                         $this->$key = $conf[$key];
68                 }
69
70                 foreach ( $optional as $key ) {
71                         if ( isset( $conf[$key] ) ) {
72                                 $this->$key = $conf[$key];
73                         }
74                 }
75
76                 // Check for read-only mode
77                 $section = $this->getSectionForWiki();
78                 if ( !empty( $this->readOnlyBySection[$section] ) ) {
79                         global $wgReadOnly;
80                         $wgReadOnly = $this->readOnlyBySection[$section];
81                 }
82         }
83
84         function getSectionForWiki( $wiki = false ) {
85                 if ( $this->lastWiki === $wiki ) {
86                         return $this->lastSection;
87                 }
88                 list( $dbName, $prefix ) = $this->getDBNameAndPrefix( $wiki );
89                 if ( isset( $this->sectionsByDB[$dbName] ) ) {
90                         $section = $this->sectionsByDB[$dbName];
91                 } else {
92                         $section = 'DEFAULT';
93                 }
94                 $this->lastSection = $section;
95                 $this->lastWiki = $wiki;
96                 return $section;
97         }
98
99         function newMainLB( $wiki = false ) {
100                 list( $dbName, $prefix ) = $this->getDBNameAndPrefix( $wiki );
101                 $section = $this->getSectionForWiki( $wiki );
102                 $groupLoads = array();
103                 if ( isset( $this->groupLoadsByDB[$dbName] ) ) {
104                         $groupLoads = $this->groupLoadsByDB[$dbName];
105                 }
106                 if ( isset( $this->groupLoadsBySection[$section] ) ) {
107                         $groupLoads = array_merge_recursive( $groupLoads, $this->groupLoadsBySection[$section] );
108                 }
109                 return $this->newLoadBalancer( $this->serverTemplate, $this->sectionLoads[$section], $groupLoads );
110         }
111
112         function getMainLB( $wiki = false ) {
113                 $section = $this->getSectionForWiki( $wiki );
114                 if ( !isset( $this->mainLBs[$section] ) ) {
115                         $lb = $this->newMainLB( $wiki, $section );
116                         $this->chronProt->initLB( $lb );
117                         $lb->parentInfo( array( 'id' => "main-$section" ) );
118                         $this->mainLBs[$section] = $lb;
119                 }
120                 return $this->mainLBs[$section];
121         }
122
123         function newExternalLB( $cluster, $wiki = false ) {
124                 if ( !isset( $this->externalLoads[$cluster] ) ) {
125                         throw new MWException( __METHOD__.": Unknown cluster \"$cluster\"" );
126                 }
127                 $template = $this->serverTemplate;
128                 if ( isset( $this->externalTemplateOverrides ) ) {
129                         $template = $this->externalTemplateOverrides + $template;
130                 }
131                 if ( isset( $this->templateOverridesByCluster[$cluster] ) ) {
132                         $template = $this->templateOverridesByCluster[$cluster] + $template;
133                 }
134                 return $this->newLoadBalancer( $template, $this->externalLoads[$cluster], array() );
135         }
136
137         function &getExternalLB( $cluster, $wiki = false ) {
138                 if ( !isset( $this->extLBs[$cluster] ) ) {
139                         $this->extLBs[$cluster] = $this->newExternalLB( $cluster, $wiki );
140                         $this->extLBs[$cluster]->parentInfo( array( 'id' => "ext-$cluster" ) );
141                 }
142                 return $this->extLBs[$cluster];
143         }
144
145         /**
146          * Make a new load balancer object based on template and load array
147          */
148         function newLoadBalancer( $template, $loads, $groupLoads ) {
149                 global $wgMasterWaitTimeout;
150                 $servers = $this->makeServerArray( $template, $loads, $groupLoads );
151                 $lb = new LoadBalancer( array(
152                         'servers' => $servers,
153                         'masterWaitTimeout' => $wgMasterWaitTimeout 
154                 ));
155                 return $lb;
156         }
157
158         /**
159          * Make a server array as expected by LoadBalancer::__construct, using a template and load array
160          */
161         function makeServerArray( $template, $loads, $groupLoads ) {
162                 $servers = array();
163                 $master = true;
164                 $groupLoadsByServer = $this->reindexGroupLoads( $groupLoads );
165                 foreach ( $groupLoadsByServer as $server => $stuff ) {
166                         if ( !isset( $loads[$server] ) ) {
167                                 $loads[$server] = 0;
168                         }
169                 }
170                 foreach ( $loads as $serverName => $load ) {
171                         $serverInfo = $template;
172                         if ( $master ) {
173                                 $serverInfo['master'] = true;
174                                 if ( isset( $this->masterTemplateOverrides ) ) {
175                                         $serverInfo = $this->masterTemplateOverrides + $serverInfo;
176                                 }
177                                 $master = false;
178                         }
179                         if ( isset( $this->templateOverridesByServer[$serverName] ) ) {
180                                 $serverInfo = $this->templateOverridesByServer[$serverName] + $serverInfo;
181                         }
182                         if ( isset( $groupLoadsByServer[$serverName] ) ) {
183                                 $serverInfo['groupLoads'] = $groupLoadsByServer[$serverName];
184                         }
185                         if ( isset( $this->hostsByName[$serverName] ) ) {
186                                 $serverInfo['host'] = $this->hostsByName[$serverName];
187                         } else {
188                                 $serverInfo['host'] = $serverName;
189                         }
190                         $serverInfo['hostName'] = $serverName;
191                         $serverInfo['load'] = $load;
192                         $servers[] = $serverInfo;
193                 }
194                 return $servers;
195         }
196
197         /**
198          * Take a group load array indexed by group then server, and reindex it by server then group
199          */
200         function reindexGroupLoads( $groupLoads ) {
201                 $reindexed = array();
202                 foreach ( $groupLoads as $group => $loads ) {
203                         foreach ( $loads as $server => $load ) {
204                                 $reindexed[$server][$group] = $load;
205                         }
206                 }
207                 return $reindexed;
208         }
209
210         /**
211          * Get the database name and prefix based on the wiki ID
212          */
213         function getDBNameAndPrefix( $wiki = false ) {
214                 if ( $wiki === false ) {
215                         global $wgDBname, $wgDBprefix;
216                         return array( $wgDBname, $wgDBprefix );
217                 } else {
218                         return wfSplitWikiID( $wiki );
219                 }
220         }
221
222         /**
223          * Execute a function for each tracked load balancer
224          * The callback is called with the load balancer as the first parameter,
225          * and $params passed as the subsequent parameters.
226          */
227         function forEachLB( $callback, $params = array() ) {
228                 foreach ( $this->mainLBs as $lb ) {
229                         call_user_func_array( $callback, array_merge( array( $lb ), $params ) );
230                 }
231                 foreach ( $this->extLBs as $lb ) {
232                         call_user_func_array( $callback, array_merge( array( $lb ), $params ) );
233                 }
234         }
235
236         function shutdown() {
237                 foreach ( $this->mainLBs as $lb ) {
238                         $this->chronProt->shutdownLB( $lb );
239                 }
240                 $this->chronProt->shutdown();
241                 $this->commitMasterChanges();
242         }
243 }