]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/db/LBFactory.php
MediaWiki 1.14.0
[autoinstallsdev/mediawiki.git] / includes / db / LBFactory.php
1 <?php
2 /**
3  * @file
4  * @ingroup Database
5  */
6
7 /**
8  * An interface for generating database load balancers
9  * @ingroup Database
10  */
11 abstract class LBFactory {
12         static $instance;
13
14         /**
15          * Get an LBFactory instance
16          */
17         static function &singleton() {
18                 if ( is_null( self::$instance ) ) {
19                         global $wgLBFactoryConf;
20                         $class = $wgLBFactoryConf['class'];
21                         self::$instance = new $class( $wgLBFactoryConf );
22                 }
23                 return self::$instance;
24         }
25
26         /**
27          * Shut down, close connections and destroy the cached instance.
28          * 
29          */
30         static function destroyInstance() {
31                 if ( self::$instance ) {
32                         self::$instance->shutdown();
33                         self::$instance->forEachLBCallMethod( 'closeAll' );
34                         self::$instance = null;
35                 }
36         }
37
38         /**
39          * Construct a factory based on a configuration array (typically from $wgLBFactoryConf)
40          */
41         abstract function __construct( $conf );
42
43         /**
44          * Create a new load balancer object. The resulting object will be untracked, 
45          * not chronology-protected, and the caller is responsible for cleaning it up.
46          *
47          * @param string $wiki Wiki ID, or false for the current wiki
48          * @return LoadBalancer
49          */
50         abstract function newMainLB( $wiki = false );
51
52         /**
53          * Get a cached (tracked) load balancer object.
54          *
55          * @param string $wiki Wiki ID, or false for the current wiki
56          * @return LoadBalancer
57          */
58         abstract function getMainLB( $wiki = false );
59
60         /*
61          * Create a new load balancer for external storage. The resulting object will be 
62          * untracked, not chronology-protected, and the caller is responsible for 
63          * cleaning it up.
64          *
65          * @param string $cluster External storage cluster, or false for core
66          * @param string $wiki Wiki ID, or false for the current wiki
67          */
68         abstract function newExternalLB( $cluster, $wiki = false );
69
70         /*
71          * Get a cached (tracked) load balancer for external storage
72          *
73          * @param string $cluster External storage cluster, or false for core
74          * @param string $wiki Wiki ID, or false for the current wiki
75          */
76         abstract function &getExternalLB( $cluster, $wiki = false );
77
78         /**
79          * Execute a function for each tracked load balancer
80          * The callback is called with the load balancer as the first parameter,
81          * and $params passed as the subsequent parameters.
82          */
83         abstract function forEachLB( $callback, $params = array() );
84
85         /**
86          * Prepare all tracked load balancers for shutdown
87          * STUB
88          */
89         function shutdown() {}
90
91         /**
92          * Call a method of each tracked load balancer
93          */
94         function forEachLBCallMethod( $methodName, $args = array() ) {
95                 $this->forEachLB( array( $this, 'callMethod' ), array( $methodName, $args ) );
96         }
97
98         /**
99          * Private helper for forEachLBCallMethod
100          */
101         function callMethod( $loadBalancer, $methodName, $args ) {
102                 call_user_func_array( array( $loadBalancer, $methodName ), $args );
103         }
104
105         /**
106          * Commit changes on all master connections
107          */
108         function commitMasterChanges() {
109                 $this->forEachLBCallMethod( 'commitMasterChanges' );
110         }
111 }
112
113 /**
114  * A simple single-master LBFactory that gets its configuration from the b/c globals
115  */
116 class LBFactory_Simple extends LBFactory {
117         var $mainLB;
118         var $extLBs = array();
119
120         # Chronology protector
121         var $chronProt;
122
123         function __construct( $conf ) {
124                 $this->chronProt = new ChronologyProtector;
125         }
126
127         function newMainLB( $wiki = false ) {
128                 global $wgDBservers, $wgMasterWaitTimeout;
129                 if ( $wgDBservers ) {
130                         $servers = $wgDBservers;
131                 } else {
132                         global $wgDBserver, $wgDBuser, $wgDBpassword, $wgDBname, $wgDBtype, $wgDebugDumpSql;
133                         $servers = array(array(
134                                 'host' => $wgDBserver,
135                                 'user' => $wgDBuser,
136                                 'password' => $wgDBpassword,
137                                 'dbname' => $wgDBname,
138                                 'type' => $wgDBtype,
139                                 'load' => 1,
140                                 'flags' => ($wgDebugDumpSql ? DBO_DEBUG : 0) | DBO_DEFAULT
141                         ));
142                 }
143
144                 return new LoadBalancer( array(
145                         'servers' => $servers, 
146                         'masterWaitTimeout' => $wgMasterWaitTimeout 
147                 ));
148         }
149
150         function getMainLB( $wiki = false ) {
151                 if ( !isset( $this->mainLB ) ) {
152                         $this->mainLB = $this->newMainLB( $wiki );
153                         $this->mainLB->parentInfo( array( 'id' => 'main' ) );
154                         $this->chronProt->initLB( $this->mainLB );
155                 }
156                 return $this->mainLB;
157         }
158
159         function newExternalLB( $cluster, $wiki = false ) {
160                 global $wgExternalServers;
161                 if ( !isset( $wgExternalServers[$cluster] ) ) {
162                         throw new MWException( __METHOD__.": Unknown cluster \"$cluster\"" );
163                 }
164                 return new LoadBalancer( array(
165                         'servers' => $wgExternalServers[$cluster] 
166                 ));
167         }
168
169         function &getExternalLB( $cluster, $wiki = false ) {
170                 if ( !isset( $this->extLBs[$cluster] ) ) {
171                         $this->extLBs[$cluster] = $this->newExternalLB( $cluster, $wiki );
172                         $this->extLBs[$cluster]->parentInfo( array( 'id' => "ext-$cluster" ) );
173                 }
174                 return $this->extLBs[$cluster];
175         }
176
177         /**
178          * Execute a function for each tracked load balancer
179          * The callback is called with the load balancer as the first parameter,
180          * and $params passed as the subsequent parameters.
181          */
182         function forEachLB( $callback, $params = array() ) {
183                 if ( isset( $this->mainLB ) ) {
184                         call_user_func_array( $callback, array_merge( array( $this->mainLB ), $params ) );
185                 }
186                 foreach ( $this->extLBs as $lb ) {
187                         call_user_func_array( $callback, array_merge( array( $lb ), $params ) );
188                 }
189         }
190
191         function shutdown() {
192                 if ( $this->mainLB ) {
193                         $this->chronProt->shutdownLB( $this->mainLB );
194                 }
195                 $this->chronProt->shutdown();
196                 $this->commitMasterChanges();
197         }
198 }
199
200 /**
201  * Class for ensuring a consistent ordering of events as seen by the user, despite replication.
202  * Kind of like Hawking's [[Chronology Protection Agency]].
203  */
204 class ChronologyProtector {
205         var $startupPos;
206         var $shutdownPos = array();
207
208         /**
209          * Initialise a LoadBalancer to give it appropriate chronology protection.
210          *
211          * @param LoadBalancer $lb
212          */
213         function initLB( $lb ) {
214                 if ( $this->startupPos === null ) {
215                         if ( !empty( $_SESSION[__CLASS__] ) ) {
216                                 $this->startupPos = $_SESSION[__CLASS__];
217                         }
218                 }
219                 if ( !$this->startupPos ) {
220                         return;
221                 }
222                 $masterName = $lb->getServerName( 0 );
223
224                 if ( $lb->getServerCount() > 1 && !empty( $this->startupPos[$masterName] ) ) {
225                         $info = $lb->parentInfo();
226                         $pos = $this->startupPos[$masterName];
227                         wfDebug( __METHOD__.": LB " . $info['id'] . " waiting for master pos $pos\n" );
228                         $lb->waitFor( $this->startupPos[$masterName] );
229                 }
230         }
231
232         /**
233          * Notify the ChronologyProtector that the LoadBalancer is about to shut
234          * down. Saves replication positions.
235          *
236          * @param LoadBalancer $lb
237          */
238         function shutdownLB( $lb ) {
239                 if ( session_id() != '' && $lb->getServerCount() > 1 ) {
240                         $masterName = $lb->getServerName( 0 );
241                         if ( !isset( $this->shutdownPos[$masterName] ) ) {
242                                 $pos = $lb->getMasterPos();
243                                 $info = $lb->parentInfo();
244                                 wfDebug( __METHOD__.": LB " . $info['id'] . " has master pos $pos\n" );
245                                 $this->shutdownPos[$masterName] = $pos;
246                         }
247                 }
248         }
249
250         /**
251          * Notify the ChronologyProtector that the LBFactory is done calling shutdownLB() for now.
252          * May commit chronology data to persistent storage.
253          */
254         function shutdown() {
255                 if ( session_id() != '' && count( $this->shutdownPos ) ) {
256                         wfDebug( __METHOD__.": saving master pos for " .
257                                 count( $this->shutdownPos ) . " master(s)\n" );
258                         $_SESSION[__CLASS__] = $this->shutdownPos;
259                 }
260         }
261 }