Index: trunk/phase3/maintenance/getSlaveServer.php |
— | — | @@ -4,10 +4,11 @@ |
5 | 5 | |
6 | 6 | if( isset( $options['group'] ) ) { |
7 | 7 | $db = wfGetDB( DB_SLAVE, $options['group'] ); |
8 | | - $host = $db->getProperty( 'mServer' ); |
| 8 | + $host = $db->getServer(); |
9 | 9 | } else { |
10 | | - $i = $wgLoadBalancer->getReaderIndex(); |
11 | | - $host = $wgDBservers[$i]['host']; |
| 10 | + $lb = wfGetLB(); |
| 11 | + $i = $lb->getReaderIndex(); |
| 12 | + $host = $lb->getServerName( $i ); |
12 | 13 | } |
13 | 14 | |
14 | 15 | print "$host\n"; |
Index: trunk/phase3/maintenance/getLagTimes.php |
— | — | @@ -2,13 +2,15 @@ |
3 | 3 | |
4 | 4 | require 'commandLine.inc'; |
5 | 5 | |
6 | | -if( empty( $wgDBservers ) ) { |
| 6 | +$lb = wfGetLB(); |
| 7 | + |
| 8 | +if( $lb->getServerCount() == 1 ) { |
7 | 9 | echo "This script dumps replication lag times, but you don't seem to have\n"; |
8 | 10 | echo "a multi-host db server configuration.\n"; |
9 | 11 | } else { |
10 | | - $lags = $wgLoadBalancer->getLagTimes(); |
| 12 | + $lags = $lb->getLagTimes(); |
11 | 13 | foreach( $lags as $n => $lag ) { |
12 | | - $host = $wgDBservers[$n]["host"]; |
| 14 | + $host = $lb->getServerName( $n ); |
13 | 15 | if( IP::isValid( $host ) ) { |
14 | 16 | $ip = $host; |
15 | 17 | $host = gethostbyaddr( $host ); |
Index: trunk/phase3/maintenance/updateSpecialPages.php |
— | — | @@ -73,12 +73,12 @@ |
74 | 74 | } |
75 | 75 | |
76 | 76 | # Reopen any connections that have closed |
77 | | - if ( !$wgLoadBalancer->pingAll()) { |
| 77 | + if ( !wfGetLB()->pingAll()) { |
78 | 78 | print "\n"; |
79 | 79 | do { |
80 | 80 | print "Connection failed, reconnecting in 10 seconds...\n"; |
81 | 81 | sleep(10); |
82 | | - } while ( !$wgLoadBalancer->pingAll() ); |
| 82 | + } while ( !wfGetLB()->pingAll() ); |
83 | 83 | print "Reconnected\n\n"; |
84 | 84 | } else { |
85 | 85 | # Commit the results |
Index: trunk/phase3/maintenance/nextJobDB.php |
— | — | @@ -21,19 +21,13 @@ |
22 | 22 | $pendingDBs = array(); |
23 | 23 | # Cross-reference DBs by master DB server |
24 | 24 | $dbsByMaster = array(); |
25 | | - $defaultMaster = isset( $wgAlternateMaster['DEFAULT'] ) |
26 | | - ? $wgAlternateMaster['DEFAULT'] |
27 | | - : $wgDBserver; |
28 | 25 | foreach ( $wgLocalDatabases as $db ) { |
29 | | - if ( isset( $wgAlternateMaster[$db] ) ) { |
30 | | - $dbsByMaster[$wgAlternateMaster[$db]][] = $db; |
31 | | - } else { |
32 | | - $dbsByMaster[$defaultMaster][] = $db; |
33 | | - } |
| 26 | + $lb = wfGetLB( $db ); |
| 27 | + $dbsByMaster[$lb->getServerName(0)][] = $db; |
34 | 28 | } |
35 | 29 | |
36 | 30 | foreach ( $dbsByMaster as $master => $dbs ) { |
37 | | - $dbConn = new Database( $master, $wgDBuser, $wgDBpassword, $dbs[0] ); |
| 31 | + $dbConn = wfGetDB( DB_MASTER, array(), $dbs[0] ); |
38 | 32 | $stype = $dbConn->addQuotes($type); |
39 | 33 | |
40 | 34 | # Padding row for MySQL bug |
Index: trunk/phase3/maintenance/waitForSlave.php |
— | — | @@ -1,12 +1,5 @@ |
2 | 2 | <?php |
3 | 3 | require_once( "commandLine.inc" ); |
4 | | - |
5 | | -# Don't wait for benet |
6 | | -foreach ( $wgLoadBalancer->mServers as $i => $server ) { |
7 | | - if ( $server['host'] == '10.0.0.29' ) { |
8 | | - unset($wgLoadBalancer->mServers[$i]); |
9 | | - } |
10 | | -} |
11 | 4 | if ( isset( $args[0] ) ) { |
12 | 5 | wfWaitForSlaves($args[0]); |
13 | 6 | } else { |
Index: trunk/phase3/maintenance/eval.php |
— | — | @@ -33,8 +33,9 @@ |
34 | 34 | $wgDebugLogFile = '/dev/stdout'; |
35 | 35 | } |
36 | 36 | if ( $d > 1 ) { |
37 | | - foreach ( $wgLoadBalancer->mServers as $i => $server ) { |
38 | | - $wgLoadBalancer->mServers[$i]['flags'] |= DBO_DEBUG; |
| 37 | + $lb = wfGetLB(); |
| 38 | + foreach ( $lb->mServers as $i => $server ) { |
| 39 | + $lb->mServers[$i]['flags'] |= DBO_DEBUG; |
39 | 40 | } |
40 | 41 | } |
41 | 42 | if ( $d > 2 ) { |
Index: trunk/phase3/maintenance/fixSlaveDesync.php |
— | — | @@ -7,13 +7,13 @@ |
8 | 8 | |
9 | 9 | $slaveIndexes = array(); |
10 | 10 | for ( $i = 1; $i < count( $wgDBservers ); $i++ ) { |
11 | | - if ( $wgLoadBalancer->isNonZeroLoad( $i ) ) { |
| 11 | + if ( wfGetLB()->isNonZeroLoad( $i ) ) { |
12 | 12 | $slaveIndexes[] = $i; |
13 | 13 | } |
14 | 14 | } |
15 | 15 | /* |
16 | | -foreach ( $wgLoadBalancer->mServers as $i => $server ) { |
17 | | - $wgLoadBalancer->mServers[$i]['flags'] |= DBO_DEBUG; |
| 16 | +foreach ( wfGetLB()->mServers as $i => $server ) { |
| 17 | + wfGetLB()->mServers[$i]['flags'] |= DBO_DEBUG; |
18 | 18 | }*/ |
19 | 19 | $reportingInterval = 1000; |
20 | 20 | |
Index: trunk/phase3/includes/DatabaseOracle.php |
— | — | @@ -692,6 +692,17 @@ |
693 | 693 | return 0; |
694 | 694 | } |
695 | 695 | |
| 696 | + function setFakeSlaveLag() {} |
| 697 | + function setFakeMaster() {} |
| 698 | + |
| 699 | + function getDBname() { |
| 700 | + return $this->mDBname; |
| 701 | + } |
| 702 | + |
| 703 | + function getServer() { |
| 704 | + return $this->mServer; |
| 705 | + } |
| 706 | + |
696 | 707 | } // end DatabaseOracle class |
697 | 708 | |
698 | 709 | |
Index: trunk/phase3/includes/BagOStuff.php |
— | — | @@ -645,6 +645,7 @@ |
646 | 646 | } |
647 | 647 | $this->mFile = "$dir/mw-cache-" . wfWikiID(); |
648 | 648 | $this->mFile .= '.db'; |
| 649 | + wfDebug( __CLASS__.": using cache file {$this->mFile}\n" ); |
649 | 650 | $this->mHandler = $handler; |
650 | 651 | } |
651 | 652 | |
Index: trunk/phase3/includes/GlobalFunctions.php |
— | — | @@ -613,7 +613,6 @@ |
614 | 614 | * @deprecated Please return control to the caller or throw an exception |
615 | 615 | */ |
616 | 616 | function wfAbruptExit( $error = false ){ |
617 | | - global $wgLoadBalancer; |
618 | 617 | static $called = false; |
619 | 618 | if ( $called ){ |
620 | 619 | exit( -1 ); |
— | — | @@ -634,7 +633,7 @@ |
635 | 634 | wfLogProfilingData(); |
636 | 635 | |
637 | 636 | if ( !$error ) { |
638 | | - $wgLoadBalancer->closeAll(); |
| 637 | + wfGetLB()->closeAll(); |
639 | 638 | } |
640 | 639 | exit( -1 ); |
641 | 640 | } |
— | — | @@ -2234,7 +2233,9 @@ |
2235 | 2234 | } |
2236 | 2235 | session_set_cookie_params( 0, $wgCookiePath, $wgCookieDomain, $wgCookieSecure); |
2237 | 2236 | session_cache_limiter( 'private, must-revalidate' ); |
2238 | | - @session_start(); |
| 2237 | + wfSuppressWarnings(); |
| 2238 | + session_start(); |
| 2239 | + wfRestoreWarnings(); |
2239 | 2240 | } |
2240 | 2241 | |
2241 | 2242 | /** |
— | — | @@ -2317,6 +2318,17 @@ |
2318 | 2319 | } |
2319 | 2320 | } |
2320 | 2321 | |
| 2322 | +/** |
| 2323 | + * Split a wiki ID into DB name and table prefix |
| 2324 | + */ |
| 2325 | +function wfSplitWikiID( $wiki ) { |
| 2326 | + $bits = explode( '-', $wiki, 2 ); |
| 2327 | + if ( count( $bits ) < 2 ) { |
| 2328 | + $bits[] = ''; |
| 2329 | + } |
| 2330 | + return $bits; |
| 2331 | +} |
| 2332 | + |
2321 | 2333 | /* |
2322 | 2334 | * Get a Database object |
2323 | 2335 | * @param integer $db Index of the connection to get. May be DB_MASTER for the |
— | — | @@ -2326,14 +2338,32 @@ |
2327 | 2339 | * @param mixed $groups Query groups. An array of group names that this query |
2328 | 2340 | * belongs to. May contain a single string if the query is only |
2329 | 2341 | * in one group. |
| 2342 | + * |
| 2343 | + * @param string $wiki The wiki ID, or false for the current wiki |
2330 | 2344 | */ |
2331 | | -function &wfGetDB( $db = DB_LAST, $groups = array() ) { |
2332 | | - global $wgLoadBalancer; |
2333 | | - $ret = $wgLoadBalancer->getConnection( $db, true, $groups ); |
2334 | | - return $ret; |
| 2345 | +function &wfGetDB( $db = DB_LAST, $groups = array(), $wiki = false ) { |
| 2346 | + return wfGetLB( $wiki )->getConnection( $db, $groups, $wiki ); |
2335 | 2347 | } |
2336 | 2348 | |
2337 | 2349 | /** |
| 2350 | + * Get a load balancer object. |
| 2351 | + * |
| 2352 | + * @param array $groups List of query groups |
| 2353 | + * @param string $wiki Wiki ID, or false for the current wiki |
| 2354 | + * @return LoadBalancer |
| 2355 | + */ |
| 2356 | +function wfGetLB( $wiki = false ) { |
| 2357 | + return wfGetLBFactory()->getMainLB( $wiki ); |
| 2358 | +} |
| 2359 | + |
| 2360 | +/** |
| 2361 | + * Get the load balancer factory object |
| 2362 | + */ |
| 2363 | +function &wfGetLBFactory() { |
| 2364 | + return LBFactory::singleton(); |
| 2365 | +} |
| 2366 | + |
| 2367 | +/** |
2338 | 2368 | * Find a file. |
2339 | 2369 | * Shortcut for RepoGroup::singleton()->findFile() |
2340 | 2370 | * @param mixed $title Title object or string. May be interwiki. |
— | — | @@ -2457,9 +2487,9 @@ |
2458 | 2488 | * @return null |
2459 | 2489 | */ |
2460 | 2490 | function wfWaitForSlaves( $maxLag ) { |
2461 | | - global $wgLoadBalancer; |
2462 | 2491 | if( $maxLag ) { |
2463 | | - list( $host, $lag ) = $wgLoadBalancer->getMaxLag(); |
| 2492 | + $lb = wfGetLB(); |
| 2493 | + list( $host, $lag ) = $lb->getMaxLag(); |
2464 | 2494 | while( $lag > $maxLag ) { |
2465 | 2495 | $name = @gethostbyaddr( $host ); |
2466 | 2496 | if( $name !== false ) { |
— | — | @@ -2467,7 +2497,7 @@ |
2468 | 2498 | } |
2469 | 2499 | print "Waiting for $host (lagged $lag seconds)...\n"; |
2470 | 2500 | sleep($maxLag); |
2471 | | - list( $host, $lag ) = $wgLoadBalancer->getMaxLag(); |
| 2501 | + list( $host, $lag ) = $lb->getMaxLag(); |
2472 | 2502 | } |
2473 | 2503 | } |
2474 | 2504 | } |
Index: trunk/phase3/includes/LBFactory_Multi.php |
— | — | @@ -0,0 +1,221 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * A multi-wiki, multi-master factory for Wikimedia and similar installations. |
| 6 | + * Ignores the old configuration globals |
| 7 | + * |
| 8 | + * Configuration: |
| 9 | + * sectionsByDB A map of database names to section names |
| 10 | + * |
| 11 | + * sectionLoads A 2-d map. For each section, gives a map of server names to load ratios. |
| 12 | + * For example: array( 'section1' => array( 'db1' => 100, 'db2' => 100 ) ) |
| 13 | + * |
| 14 | + * mainTemplate A server info associative array as documented for $wgDBservers. The host, |
| 15 | + * hostName and load entries will be overridden. |
| 16 | + * |
| 17 | + * groupLoadsBySection A 3-d map giving server load ratios for each section and group. For example: |
| 18 | + * array( 'section1' => array( 'group1' => array( 'db1' => 100, 'db2' => 100 ) ) ) |
| 19 | + * |
| 20 | + * groupLoadsByDB A 3-d map giving server load ratios by DB name. |
| 21 | + * |
| 22 | + * hostsByName A map of hostname to IP address. |
| 23 | + * |
| 24 | + * externalLoads A map of external storage cluster name to server load map |
| 25 | + * |
| 26 | + * externalTemplate A server info structure used for external storage servers |
| 27 | + * |
| 28 | + * templateOverridesByServer A 2-d map overriding mainTemplate or externalTemplate on a |
| 29 | + * server-by-server basis. |
| 30 | + * |
| 31 | + * templateOverridesByCluster A 2-d map overriding externalTemplate by cluster |
| 32 | + * |
| 33 | + * masterTemplateOverrides An override array for mainTemplate and externalTemplate for all |
| 34 | + * master servers. |
| 35 | + * |
| 36 | + */ |
| 37 | +class LBFactory_Multi extends LBFactory { |
| 38 | + // Required settings |
| 39 | + var $sectionsByDB, $sectionLoads, $mainTemplate; |
| 40 | + // Optional settings |
| 41 | + var $groupLoadsBySection = array(), $groupLoadsByDB = array(), $hostsByName = array(); |
| 42 | + var $externalLoads = array(), $externalTemplate, $templateOverridesByServer; |
| 43 | + var $templateOverridesByCluster, $masterTemplateOverrides; |
| 44 | + // Other stuff |
| 45 | + var $conf, $mainLBs = array(), $extLBs = array(); |
| 46 | + var $localSection = null; |
| 47 | + |
| 48 | + function __construct( $conf ) { |
| 49 | + $this->chronProt = new ChronologyProtector; |
| 50 | + $this->conf = $conf; |
| 51 | + $required = array( 'sectionsByDB', 'sectionLoads', 'mainTemplate' ); |
| 52 | + $optional = array( 'groupLoadsBySection', 'groupLoadsByDB', 'hostsByName', |
| 53 | + 'externalLoads', 'externalTemplate', 'templateOverridesByServer', |
| 54 | + 'templateOverridesByCluster', 'masterTemplateOverrides' ); |
| 55 | + |
| 56 | + foreach ( $required as $key ) { |
| 57 | + if ( !isset( $conf[$key] ) ) { |
| 58 | + throw new MWException( __CLASS__.": $key is required in configuration" ); |
| 59 | + } |
| 60 | + $this->$key = $conf[$key]; |
| 61 | + } |
| 62 | + |
| 63 | + foreach ( $optional as $key ) { |
| 64 | + if ( isset( $conf[$key] ) ) { |
| 65 | + $this->$key = $conf[$key]; |
| 66 | + } |
| 67 | + } |
| 68 | + } |
| 69 | + |
| 70 | + function getSectionForWiki( $wiki ) { |
| 71 | + list( $dbName, $prefix ) = $this->getDBNameAndPrefix( $wiki ); |
| 72 | + if ( isset( $this->sectionsByDB[$dbName] ) ) { |
| 73 | + return $this->sectionsByDB[$dbName]; |
| 74 | + } else { |
| 75 | + return 'DEFAULT'; |
| 76 | + } |
| 77 | + } |
| 78 | + |
| 79 | + function getMainLB( $wiki = false ) { |
| 80 | + // Determine section |
| 81 | + if ( $wiki === false ) { |
| 82 | + if ( $this->localSection === null ) { |
| 83 | + $this->localSection = $this->getSectionForWiki( $wiki ); |
| 84 | + } |
| 85 | + $section = $this->localSection; |
| 86 | + } else { |
| 87 | + $section = $this->getSectionForWiki( $wiki ); |
| 88 | + } |
| 89 | + |
| 90 | + if ( !isset( $this->mainLBs[$section] ) ) { |
| 91 | + list( $dbName, $prefix ) = $this->getDBNameAndPrefix( $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 | + $this->mainLBs[$section] = $this->newLoadBalancer( $this->mainTemplate, |
| 100 | + $this->sectionLoads[$section], $groupLoads, "main-$section" ); |
| 101 | + $this->chronProt->initLB( $this->mainLBs[$section] ); |
| 102 | + } |
| 103 | + return $this->mainLBs[$section]; |
| 104 | + } |
| 105 | + |
| 106 | + function getExternalLB( $cluster, $wiki = false ) { |
| 107 | + global $wgExternalServers; |
| 108 | + if ( !isset( $this->extLBs[$cluster] ) ) { |
| 109 | + if ( !isset( $this->externalLoads[$cluster] ) ) { |
| 110 | + throw new MWException( __METHOD__.": Unknown cluster \"$cluster\"" ); |
| 111 | + } |
| 112 | + if ( isset( $this->templateOverridesByCluster[$cluster] ) ) { |
| 113 | + $template = $this->templateOverridesByCluster[$cluster]; |
| 114 | + } elseif ( isset( $this->externalTemplate ) ) { |
| 115 | + $template = $this->externalTemplate; |
| 116 | + } else { |
| 117 | + $template = $this->mainTemplate; |
| 118 | + } |
| 119 | + $this->extLBs[$cluster] = $this->newLoadBalancer( $template, |
| 120 | + $this->externalLoads[$cluster], array(), "ext-$cluster" ); |
| 121 | + } |
| 122 | + return $this->extLBs[$cluster]; |
| 123 | + } |
| 124 | + |
| 125 | + /** |
| 126 | + * Make a new load balancer object based on template and load array |
| 127 | + */ |
| 128 | + function newLoadBalancer( $template, $loads, $groupLoads, $id ) { |
| 129 | + global $wgMasterWaitTimeout; |
| 130 | + $servers = $this->makeServerArray( $template, $loads, $groupLoads ); |
| 131 | + $lb = new LoadBalancer( $servers, false, $wgMasterWaitTimeout ); |
| 132 | + $lb->parentInfo( array( 'id' => $id ) ); |
| 133 | + return $lb; |
| 134 | + } |
| 135 | + |
| 136 | + /** |
| 137 | + * Make a server array as expected by LoadBalancer::__construct, using a template and load array |
| 138 | + */ |
| 139 | + function makeServerArray( $template, $loads, $groupLoads ) { |
| 140 | + $servers = array(); |
| 141 | + $master = true; |
| 142 | + $groupLoadsByServer = $this->reindexGroupLoads( $groupLoads ); |
| 143 | + foreach ( $groupLoadsByServer as $server => $stuff ) { |
| 144 | + if ( !isset( $loads[$server] ) ) { |
| 145 | + $loads[$server] = 0; |
| 146 | + } |
| 147 | + } |
| 148 | + foreach ( $loads as $serverName => $load ) { |
| 149 | + $serverInfo = $template; |
| 150 | + if ( $master ) { |
| 151 | + $serverInfo['master'] = true; |
| 152 | + if ( isset( $this->masterTemplateOverrides ) ) { |
| 153 | + $serverInfo = $this->masterTemplateOverrides + $serverInfo; |
| 154 | + } |
| 155 | + $master = false; |
| 156 | + } |
| 157 | + if ( isset( $this->templateOverridesByServer[$serverName] ) ) { |
| 158 | + $serverInfo = $this->templateOverridesByServer[$serverName] + $serverInfo; |
| 159 | + } |
| 160 | + if ( isset( $groupLoadsByServer[$serverName] ) ) { |
| 161 | + $serverInfo['groupLoads'] = $groupLoadsByServer[$serverName]; |
| 162 | + } |
| 163 | + if ( isset( $this->hostsByName[$serverName] ) ) { |
| 164 | + $serverInfo['host'] = $this->hostsByName[$serverName]; |
| 165 | + } else { |
| 166 | + $serverInfo['host'] = $serverName; |
| 167 | + } |
| 168 | + $serverInfo['hostName'] = $serverName; |
| 169 | + $serverInfo['load'] = $load; |
| 170 | + $servers[] = $serverInfo; |
| 171 | + } |
| 172 | + return $servers; |
| 173 | + } |
| 174 | + |
| 175 | + /** |
| 176 | + * Take a group load array indexed by group then server, and reindex it by server then group |
| 177 | + */ |
| 178 | + function reindexGroupLoads( $groupLoads ) { |
| 179 | + $reindexed = array(); |
| 180 | + foreach ( $groupLoads as $group => $loads ) { |
| 181 | + foreach ( $loads as $server => $load ) { |
| 182 | + $reindexed[$server][$group] = $load; |
| 183 | + } |
| 184 | + } |
| 185 | + return $reindexed; |
| 186 | + } |
| 187 | + |
| 188 | + /** |
| 189 | + * Get the database name and prefix based on the wiki ID |
| 190 | + */ |
| 191 | + function getDBNameAndPrefix( $wiki = false ) { |
| 192 | + if ( $wiki === false ) { |
| 193 | + global $wgDBname, $wgDBprefix; |
| 194 | + return array( $wgDBname, $wgDBprefix ); |
| 195 | + } else { |
| 196 | + return wfSplitWikiID( $wiki ); |
| 197 | + } |
| 198 | + } |
| 199 | + |
| 200 | + /** |
| 201 | + * Execute a function for each tracked load balancer |
| 202 | + * The callback is called with the load balancer as the first parameter, |
| 203 | + * and $params passed as the subsequent parameters. |
| 204 | + */ |
| 205 | + function forEachLB( $callback, $params = array() ) { |
| 206 | + foreach ( $this->mainLBs as $lb ) { |
| 207 | + call_user_func_array( $callback, array_merge( array( $lb ), $params ) ); |
| 208 | + } |
| 209 | + foreach ( $this->extLBs as $lb ) { |
| 210 | + call_user_func_array( $callback, array_merge( array( $lb ), $params ) ); |
| 211 | + } |
| 212 | + } |
| 213 | + |
| 214 | + function shutdown() { |
| 215 | + foreach ( $this->mainLBs as $lb ) { |
| 216 | + $this->chronProt->shutdownLB( $lb ); |
| 217 | + } |
| 218 | + $this->chronProt->shutdown(); |
| 219 | + $this->commitMasterChanges(); |
| 220 | + } |
| 221 | +} |
| 222 | + |
Property changes on: trunk/phase3/includes/LBFactory_Multi.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 223 | + native |
Index: trunk/phase3/includes/Setup.php |
— | — | @@ -213,20 +213,6 @@ |
214 | 214 | wfProfileOut( $fname.'-SetupSession' ); |
215 | 215 | wfProfileIn( $fname.'-globals' ); |
216 | 216 | |
217 | | -if ( !$wgDBservers ) { |
218 | | - $wgDBservers = array(array( |
219 | | - 'host' => $wgDBserver, |
220 | | - 'user' => $wgDBuser, |
221 | | - 'password' => $wgDBpassword, |
222 | | - 'dbname' => $wgDBname, |
223 | | - 'type' => $wgDBtype, |
224 | | - 'load' => 1, |
225 | | - 'flags' => ($wgDebugDumpSql ? DBO_DEBUG : 0) | DBO_DEFAULT |
226 | | - )); |
227 | | -} |
228 | | - |
229 | | -$wgLoadBalancer = new StubObject( 'wgLoadBalancer', 'LoadBalancer', |
230 | | - array( $wgDBservers, false, $wgMasterWaitTimeout, true ) ); |
231 | 217 | $wgContLang = new StubContLang; |
232 | 218 | |
233 | 219 | // Now that variant lists may be available... |
Index: trunk/phase3/includes/filerepo/ForeignDBViaLBRepo.php |
— | — | @@ -0,0 +1,37 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * A foreign repository with a MediaWiki database accessible via the configured LBFactory |
| 6 | + */ |
| 7 | +class ForeignDBViaLBRepo extends LocalRepo { |
| 8 | + var $wiki, $dbName, $tablePrefix; |
| 9 | + var $fileFactory = array( 'ForeignDBFile', 'newFromTitle' ); |
| 10 | + |
| 11 | + function __construct( $info ) { |
| 12 | + parent::__construct( $info ); |
| 13 | + $this->wiki = $info['wiki']; |
| 14 | + list( $this->dbName, $this->tablePrefix ) = wfSplitWikiID( $this->wiki ); |
| 15 | + $this->hasSharedCache = $info['hasSharedCache']; |
| 16 | + } |
| 17 | + |
| 18 | + function getMasterDB() { |
| 19 | + return wfGetDB( DB_MASTER, array(), $this->wiki ); |
| 20 | + } |
| 21 | + |
| 22 | + function getSlaveDB() { |
| 23 | + return wfGetDB( DB_SLAVE, array(), $this->wiki ); |
| 24 | + } |
| 25 | + function hasSharedCache() { |
| 26 | + return $this->hasSharedCache; |
| 27 | + } |
| 28 | + |
| 29 | + function store( $srcPath, $dstZone, $dstRel, $flags = 0 ) { |
| 30 | + throw new MWException( get_class($this) . ': write operations are not supported' ); |
| 31 | + } |
| 32 | + function publish( $srcPath, $dstRel, $archiveRel, $flags = 0 ) { |
| 33 | + throw new MWException( get_class($this) . ': write operations are not supported' ); |
| 34 | + } |
| 35 | + function deleteBatch( $fileMap ) { |
| 36 | + throw new MWException( get_class($this) . ': write operations are not supported' ); |
| 37 | + } |
| 38 | +} |
Property changes on: trunk/phase3/includes/filerepo/ForeignDBViaLBRepo.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 39 | + native |
Index: trunk/phase3/includes/api/ApiQuerySiteinfo.php |
— | — | @@ -185,7 +185,7 @@ |
186 | 186 | } |
187 | 187 | |
188 | 188 | protected function appendDbReplLagInfo($property, $includeAll) { |
189 | | - global $wgLoadBalancer, $wgShowHostnames; |
| 189 | + global $wgShowHostnames; |
190 | 190 | |
191 | 191 | $data = array(); |
192 | 192 | |
— | — | @@ -194,14 +194,14 @@ |
195 | 195 | $this->dieUsage('Cannot view all servers info unless $wgShowHostnames is true', 'includeAllDenied'); |
196 | 196 | |
197 | 197 | global $wgDBservers; |
198 | | - $lags = $wgLoadBalancer->getLagTimes(); |
| 198 | + $lags = wfGetLB()->getLagTimes(); |
199 | 199 | foreach( $lags as $i => $lag ) { |
200 | 200 | $data[] = array ( |
201 | 201 | 'host' => $wgDBservers[$i]['host'], |
202 | 202 | 'lag' => $lag); |
203 | 203 | } |
204 | 204 | } else { |
205 | | - list( $host, $lag ) = $wgLoadBalancer->getMaxLag(); |
| 205 | + list( $host, $lag ) = wfGetLB()->getMaxLag(); |
206 | 206 | $data[] = array ( |
207 | 207 | 'host' => $wgShowHostnames ? $host : '', |
208 | 208 | 'lag' => $lag); |
Index: trunk/phase3/includes/api/ApiMain.php |
— | — | @@ -320,9 +320,9 @@ |
321 | 321 | |
322 | 322 | if( $module->shouldCheckMaxlag() && isset( $params['maxlag'] ) ) { |
323 | 323 | // Check for maxlag |
324 | | - global $wgLoadBalancer, $wgShowHostnames; |
| 324 | + global $wgShowHostnames; |
325 | 325 | $maxLag = $params['maxlag']; |
326 | | - list( $host, $lag ) = $wgLoadBalancer->getMaxLag(); |
| 326 | + list( $host, $lag ) = wfGetLB()->getMaxLag(); |
327 | 327 | if ( $lag > $maxLag ) { |
328 | 328 | if( $wgShowHostnames ) { |
329 | 329 | ApiBase :: dieUsage( "Waiting for $host: $lag seconds lagged", 'maxlag' ); |
Index: trunk/phase3/includes/AutoLoader.php |
— | — | @@ -32,6 +32,7 @@ |
33 | 33 | 'CategoryViewer' => 'includes/CategoryPage.php', |
34 | 34 | 'ChangesList' => 'includes/ChangesList.php', |
35 | 35 | 'ChannelFeed' => 'includes/Feed.php', |
| 36 | + 'ChronologyProtector' => 'includes/LBFactory.php', |
36 | 37 | 'ConcatenatedGzipHistoryBlob' => 'includes/HistoryBlob.php', |
37 | 38 | 'ContributionsPage' => 'includes/SpecialContributions.php', |
38 | 39 | 'CoreParserFunctions' => 'includes/CoreParserFunctions.php', |
— | — | @@ -120,6 +121,9 @@ |
121 | 122 | 'IP' => 'includes/IP.php', |
122 | 123 | 'IPUnblockForm' => 'includes/SpecialIpblocklist.php', |
123 | 124 | 'Job' => 'includes/JobQueue.php', |
| 125 | + 'LBFactory' => 'includes/LBFactory.php', |
| 126 | + 'LBFactory_Multi' => 'includes/LBFactory_Multi.php', |
| 127 | + 'LBFactory_Simple' => 'includes/LBFactory.php', |
124 | 128 | 'License' => 'includes/Licenses.php', |
125 | 129 | 'Licenses' => 'includes/Licenses.php', |
126 | 130 | 'LinkBatch' => 'includes/LinkBatch.php', |
— | — | @@ -159,6 +163,7 @@ |
160 | 164 | 'MWException' => 'includes/Exception.php', |
161 | 165 | 'MWNamespace' => 'includes/Namespace.php', |
162 | 166 | 'MySQLSearchResultSet' => 'includes/SearchMySQL.php', |
| 167 | + 'MySQLMasterPos' => 'includes/Database.php', |
163 | 168 | 'Namespace' => 'includes/NamespaceCompat.php', // Compat |
164 | 169 | 'NewbieContributionsPage' => 'includes/SpecialNewbieContributions.php', |
165 | 170 | 'NewPagesPage' => 'includes/SpecialNewpages.php', |
— | — | @@ -289,6 +294,7 @@ |
290 | 295 | 'FileRepoStatus' => 'includes/filerepo/FileRepoStatus.php', |
291 | 296 | 'ForeignDBFile' => 'includes/filerepo/ForeignDBFile.php', |
292 | 297 | 'ForeignDBRepo' => 'includes/filerepo/ForeignDBRepo.php', |
| 298 | + 'ForeignDBViaLBRepo' => 'includes/filerepo/ForeignDBViaLBRepo.php', |
293 | 299 | 'FSRepo' => 'includes/filerepo/FSRepo.php', |
294 | 300 | 'Image' => 'includes/filerepo/LocalFile.php', |
295 | 301 | 'LocalFileDeleteBatch' => 'includes/filerepo/LocalFile.php', |
Index: trunk/phase3/includes/Wiki.php |
— | — | @@ -70,13 +70,12 @@ |
71 | 71 | * Check if the maximum lag of database slaves is higher that $maxLag, and |
72 | 72 | * if it's the case, output an error message |
73 | 73 | * |
74 | | - * @param LoadBalancer $loadBalancer |
75 | 74 | * @param int $maxLag maximum lag allowed for the request, as supplied by |
76 | 75 | * the client |
77 | | - * @return bool true if the requet can continue |
| 76 | + * @return bool true if the request can continue |
78 | 77 | */ |
79 | | - function checkMaxLag( $loadBalancer, $maxLag ) { |
80 | | - list( $host, $lag ) = $loadBalancer->getMaxLag(); |
| 78 | + function checkMaxLag( $maxLag ) { |
| 79 | + list( $host, $lag ) = wfGetLB()->getMaxLag(); |
81 | 80 | if ( $lag > $maxLag ) { |
82 | 81 | wfMaxlagError( $host, $lag, $maxLag ); |
83 | 82 | return false; |
— | — | @@ -316,20 +315,18 @@ |
317 | 316 | } |
318 | 317 | |
319 | 318 | /** |
320 | | - * Cleaning up by doing deferred updates, calling loadbalancer and doing the |
321 | | - * output |
| 319 | + * Cleaning up by doing deferred updates, calling LBFactory and doing the output |
322 | 320 | * |
323 | 321 | * @param Array $deferredUpdates array of updates to do |
324 | | - * @param LoadBalancer $loadBalancer |
325 | 322 | * @param OutputPage $output |
326 | 323 | */ |
327 | | - function finalCleanup( &$deferredUpdates, &$loadBalancer, &$output ) { |
| 324 | + function finalCleanup ( &$deferredUpdates, &$output ) { |
328 | 325 | wfProfileIn( __METHOD__ ); |
329 | 326 | $this->doUpdates( $deferredUpdates ); |
330 | 327 | $this->doJobs(); |
331 | | - $loadBalancer->saveMasterPos(); |
332 | 328 | # Now commit any transactions, so that unreported errors after output() don't roll back the whole thing |
333 | | - $loadBalancer->commitMasterChanges(); |
| 329 | + $factory = wfGetLBFactory(); |
| 330 | + $factory->shutdown(); |
334 | 331 | $output->output(); |
335 | 332 | wfProfileOut( __METHOD__ ); |
336 | 333 | } |
Index: trunk/phase3/includes/DatabasePostgres.php |
— | — | @@ -1304,6 +1304,17 @@ |
1305 | 1305 | return false; |
1306 | 1306 | } |
1307 | 1307 | |
| 1308 | + function setFakeSlaveLag() {} |
| 1309 | + function setFakeMaster() {} |
| 1310 | + |
| 1311 | + function getDBname() { |
| 1312 | + return $this->mDBname; |
| 1313 | + } |
| 1314 | + |
| 1315 | + function getServer() { |
| 1316 | + return $this->mServer; |
| 1317 | + } |
| 1318 | + |
1308 | 1319 | function buildConcat( $stringList ) { |
1309 | 1320 | return implode( ' || ', $stringList ); |
1310 | 1321 | } |
Index: trunk/phase3/includes/DefaultSettings.php |
— | — | @@ -580,48 +580,61 @@ |
581 | 581 | */ |
582 | 582 | $wgSharedDB = null; |
583 | 583 | |
584 | | -# Database load balancer |
585 | | -# This is a two-dimensional array, an array of server info structures |
586 | | -# Fields are: |
587 | | -# host: Host name |
588 | | -# dbname: Default database name |
589 | | -# user: DB user |
590 | | -# password: DB password |
591 | | -# type: "mysql" or "postgres" |
592 | | -# load: ratio of DB_SLAVE load, must be >=0, the sum of all loads must be >0 |
593 | | -# groupLoads: array of load ratios, the key is the query group name. A query may belong |
594 | | -# to several groups, the most specific group defined here is used. |
595 | | -# |
596 | | -# flags: bit field |
597 | | -# DBO_DEFAULT -- turns on DBO_TRX only if !$wgCommandLineMode (recommended) |
598 | | -# DBO_DEBUG -- equivalent of $wgDebugDumpSql |
599 | | -# DBO_TRX -- wrap entire request in a transaction |
600 | | -# DBO_IGNORE -- ignore errors (not useful in LocalSettings.php) |
601 | | -# DBO_NOBUFFER -- turn off buffering (not useful in LocalSettings.php) |
602 | | -# |
603 | | -# max lag: (optional) Maximum replication lag before a slave will taken out of rotation |
604 | | -# max threads: (optional) Maximum number of running threads |
605 | | -# |
606 | | -# These and any other user-defined properties will be assigned to the mLBInfo member |
607 | | -# variable of the Database object. |
608 | | -# |
609 | | -# Leave at false to use the single-server variables above. If you set this |
610 | | -# variable, the single-server variables will generally be ignored (except |
611 | | -# perhaps in some command-line scripts). |
612 | | -# |
613 | | -# The first server listed in this array (with key 0) will be the master. The |
614 | | -# rest of the servers will be slaves. To prevent writes to your slaves due to |
615 | | -# accidental misconfiguration or MediaWiki bugs, set read_only=1 on all your |
616 | | -# slaves in my.cnf. You can set read_only mode at runtime using: |
617 | | -# |
618 | | -# SET @@read_only=1; |
619 | | -# |
620 | | -# Since the effect of writing to a slave is so damaging and difficult to clean |
621 | | -# up, we at Wikimedia set read_only=1 in my.cnf on all our DB servers, even |
622 | | -# our masters, and then set read_only=0 on masters at runtime. |
623 | | -# |
| 584 | +/** |
| 585 | + * Database load balancer |
| 586 | + * This is a two-dimensional array, an array of server info structures |
| 587 | + * Fields are: |
| 588 | + * host: Host name |
| 589 | + * dbname: Default database name |
| 590 | + * user: DB user |
| 591 | + * password: DB password |
| 592 | + * type: "mysql" or "postgres" |
| 593 | + * load: ratio of DB_SLAVE load, must be >=0, the sum of all loads must be >0 |
| 594 | + * groupLoads: array of load ratios, the key is the query group name. A query may belong |
| 595 | + * to several groups, the most specific group defined here is used. |
| 596 | + * |
| 597 | + * flags: bit field |
| 598 | + * DBO_DEFAULT -- turns on DBO_TRX only if !$wgCommandLineMode (recommended) |
| 599 | + * DBO_DEBUG -- equivalent of $wgDebugDumpSql |
| 600 | + * DBO_TRX -- wrap entire request in a transaction |
| 601 | + * DBO_IGNORE -- ignore errors (not useful in LocalSettings.php) |
| 602 | + * DBO_NOBUFFER -- turn off buffering (not useful in LocalSettings.php) |
| 603 | + * |
| 604 | + * max lag: (optional) Maximum replication lag before a slave will taken out of rotation |
| 605 | + * max threads: (optional) Maximum number of running threads |
| 606 | + * |
| 607 | + * These and any other user-defined properties will be assigned to the mLBInfo member |
| 608 | + * variable of the Database object. |
| 609 | + * |
| 610 | + * Leave at false to use the single-server variables above. If you set this |
| 611 | + * variable, the single-server variables will generally be ignored (except |
| 612 | + * perhaps in some command-line scripts). |
| 613 | + * |
| 614 | + * The first server listed in this array (with key 0) will be the master. The |
| 615 | + * rest of the servers will be slaves. To prevent writes to your slaves due to |
| 616 | + * accidental misconfiguration or MediaWiki bugs, set read_only=1 on all your |
| 617 | + * slaves in my.cnf. You can set read_only mode at runtime using: |
| 618 | + * |
| 619 | + * SET @@read_only=1; |
| 620 | + * |
| 621 | + * Since the effect of writing to a slave is so damaging and difficult to clean |
| 622 | + * up, we at Wikimedia set read_only=1 in my.cnf on all our DB servers, even |
| 623 | + * our masters, and then set read_only=0 on masters at runtime. |
| 624 | + */ |
624 | 625 | $wgDBservers = false; |
625 | 626 | |
| 627 | +/** |
| 628 | + * Load balancer factory configuration |
| 629 | + * To set up a multi-master wiki farm, set the class here to something that |
| 630 | + * can return a LoadBalancer with an appropriate master on a call to getMainLB(). |
| 631 | + * The class identified here is responsible for reading $wgDBservers, |
| 632 | + * $wgDBserver, etc., so overriding it may cause those globals to be ignored. |
| 633 | + * |
| 634 | + * The LBFactory_Multi class is provided for this purpose, please see |
| 635 | + * includes/LBFactory_Multi.php for configuration information. |
| 636 | + */ |
| 637 | +$wgLBFactoryConf = array( 'class' => 'LBFactory_Simple' ); |
| 638 | + |
626 | 639 | /** How long to wait for a slave to catch up to the master */ |
627 | 640 | $wgMasterWaitTimeout = 10; |
628 | 641 | |
— | — | @@ -677,19 +690,6 @@ |
678 | 691 | $wgLocalDatabases = array(); |
679 | 692 | |
680 | 693 | /** |
681 | | - * For multi-wiki clusters with multiple master servers; if an alternate |
682 | | - * is listed for the requested database, a connection to it will be opened |
683 | | - * instead of to the current wiki's regular master server when cross-wiki |
684 | | - * data operations are done from here. |
685 | | - * |
686 | | - * Requires that the other server be accessible by network, with the same |
687 | | - * username/password as the primary. |
688 | | - * |
689 | | - * eg $wgAlternateMaster['enwiki'] = 'ariel'; |
690 | | - */ |
691 | | -$wgAlternateMaster = array(); |
692 | | - |
693 | | -/** |
694 | 694 | * Object cache settings |
695 | 695 | * See Defines.php for types |
696 | 696 | */ |
Index: trunk/phase3/includes/ExternalStoreDB.php |
— | — | @@ -32,12 +32,7 @@ |
33 | 33 | |
34 | 34 | /** @todo Document.*/ |
35 | 35 | function &getLoadBalancer( $cluster ) { |
36 | | - global $wgExternalServers, $wgExternalLoadBalancers; |
37 | | - if ( !array_key_exists( $cluster, $wgExternalLoadBalancers ) ) { |
38 | | - $wgExternalLoadBalancers[$cluster] = LoadBalancer::newFromParams( $wgExternalServers[$cluster] ); |
39 | | - } |
40 | | - $wgExternalLoadBalancers[$cluster]->allowLagged(true); |
41 | | - return $wgExternalLoadBalancers[$cluster]; |
| 36 | + return wfGetLBFactory()->getExternalLB( $cluster ); |
42 | 37 | } |
43 | 38 | |
44 | 39 | /** @todo Document.*/ |
Index: trunk/phase3/includes/SiteConfiguration.php |
— | — | @@ -126,7 +126,11 @@ |
127 | 127 | $site = NULL; |
128 | 128 | $lang = NULL; |
129 | 129 | foreach ( $this->suffixes as $suffix ) { |
130 | | - if ( substr( $db, -strlen( $suffix ) ) == $suffix ) { |
| 130 | + if ( $suffix === '' ) { |
| 131 | + $site = ''; |
| 132 | + $lang = $db; |
| 133 | + break; |
| 134 | + } elseif ( substr( $db, -strlen( $suffix ) ) == $suffix ) { |
131 | 135 | $site = $suffix == 'wiki' ? 'wikipedia' : $suffix; |
132 | 136 | $lang = substr( $db, 0, strlen( $db ) - strlen( $suffix ) ); |
133 | 137 | break; |
Index: trunk/phase3/includes/UserRightsProxy.php |
— | — | @@ -72,25 +72,12 @@ |
73 | 73 | // Hmm... this shouldn't happen though. :) |
74 | 74 | return wfGetDB( DB_MASTER ); |
75 | 75 | } else { |
76 | | - global $wgDBuser, $wgDBpassword; |
77 | | - $server = self::getMaster( $database ); |
78 | | - return new Database( $server, $wgDBuser, $wgDBpassword, $database ); |
| 76 | + return wfGetDB( DB_MASTER, array(), $database ); |
79 | 77 | } |
80 | 78 | } |
81 | 79 | return null; |
82 | 80 | } |
83 | 81 | |
84 | | - /** |
85 | | - * Return the master server to connect to for the requested database. |
86 | | - */ |
87 | | - private static function getMaster( $database ) { |
88 | | - global $wgDBserver, $wgAlternateMaster; |
89 | | - if( isset( $wgAlternateMaster[$database] ) ) { |
90 | | - return $wgAlternateMaster[$database]; |
91 | | - } |
92 | | - return $wgDBserver; |
93 | | - } |
94 | | - |
95 | 82 | public function getId() { |
96 | 83 | return $this->id; |
97 | 84 | } |
— | — | @@ -158,4 +145,4 @@ |
159 | 146 | } |
160 | 147 | } |
161 | 148 | |
162 | | -?> |
\ No newline at end of file |
| 149 | +?> |
Index: trunk/phase3/includes/Skin.php |
— | — | @@ -1184,7 +1184,7 @@ |
1185 | 1185 | } |
1186 | 1186 | |
1187 | 1187 | function lastModified() { |
1188 | | - global $wgLang, $wgArticle, $wgLoadBalancer; |
| 1188 | + global $wgLang, $wgArticle; |
1189 | 1189 | |
1190 | 1190 | $timestamp = $wgArticle->getTimestamp(); |
1191 | 1191 | if ( $timestamp ) { |
— | — | @@ -1194,7 +1194,7 @@ |
1195 | 1195 | } else { |
1196 | 1196 | $s = ''; |
1197 | 1197 | } |
1198 | | - if ( $wgLoadBalancer->getLaggedSlaveMode() ) { |
| 1198 | + if ( wfGetLB()->getLaggedSlaveMode() ) { |
1199 | 1199 | $s .= ' <strong>' . wfMsg( 'laggedslavemode' ) . '</strong>'; |
1200 | 1200 | } |
1201 | 1201 | return $s; |
Index: trunk/phase3/includes/LoadBalancer.php |
— | — | @@ -1,32 +1,29 @@ |
2 | 2 | <?php |
3 | 3 | /** |
4 | | - * |
5 | | - */ |
6 | | - |
7 | | - |
8 | | -/** |
9 | 4 | * Database load balancing object |
10 | 5 | * |
11 | 6 | * @todo document |
12 | 7 | */ |
13 | 8 | class LoadBalancer { |
14 | | - /* private */ var $mServers, $mConnections, $mLoads, $mGroupLoads; |
| 9 | + /* private */ var $mServers, $mConns, $mLoads, $mGroupLoads; |
15 | 10 | /* private */ var $mFailFunction, $mErrorConnection; |
16 | | - /* private */ var $mForce, $mReadIndex, $mLastIndex, $mAllowLagged; |
17 | | - /* private */ var $mWaitForFile, $mWaitForPos, $mWaitTimeout; |
| 11 | + /* private */ var $mReadIndex, $mLastIndex, $mAllowLagged; |
| 12 | + /* private */ var $mWaitForPos, $mWaitTimeout; |
18 | 13 | /* private */ var $mLaggedSlaveMode, $mLastError = 'Unknown error'; |
| 14 | + /* private */ var $mParentInfo, $mLagTimes; |
19 | 15 | |
20 | | - function __construct( $servers, $failFunction = false, $waitTimeout = 10, $waitForMasterNow = false ) |
| 16 | + function __construct( $servers, $failFunction = false, $waitTimeout = 10, $unused = false ) |
21 | 17 | { |
22 | 18 | $this->mServers = $servers; |
23 | 19 | $this->mFailFunction = $failFunction; |
24 | 20 | $this->mReadIndex = -1; |
25 | 21 | $this->mWriteIndex = -1; |
26 | | - $this->mForce = -1; |
27 | | - $this->mConnections = array(); |
| 22 | + $this->mConns = array( |
| 23 | + 'local' => array(), |
| 24 | + 'foreignUsed' => array(), |
| 25 | + 'foreignFree' => array() ); |
28 | 26 | $this->mLastIndex = -1; |
29 | 27 | $this->mLoads = array(); |
30 | | - $this->mWaitForFile = false; |
31 | 28 | $this->mWaitForPos = false; |
32 | 29 | $this->mWaitTimeout = $waitTimeout; |
33 | 30 | $this->mLaggedSlaveMode = false; |
— | — | @@ -44,9 +41,6 @@ |
45 | 42 | } |
46 | 43 | } |
47 | 44 | } |
48 | | - if ( $waitForMasterNow ) { |
49 | | - $this->loadMasterPos(); |
50 | | - } |
51 | 45 | } |
52 | 46 | |
53 | 47 | static function newFromParams( $servers, $failFunction = false, $waitTimeout = 10 ) |
— | — | @@ -55,6 +49,13 @@ |
56 | 50 | } |
57 | 51 | |
58 | 52 | /** |
| 53 | + * Get or set arbitrary data used by the parent object, usually an LBFactory |
| 54 | + */ |
| 55 | + function parentInfo( $x = null ) { |
| 56 | + return wfSetVar( $this->mParentInfo, $x ); |
| 57 | + } |
| 58 | + |
| 59 | + /** |
59 | 60 | * Given an array of non-normalised probabilities, this function will select |
60 | 61 | * an element and return the appropriate key |
61 | 62 | */ |
— | — | @@ -89,10 +90,14 @@ |
90 | 91 | # Unset excessively lagged servers |
91 | 92 | $lags = $this->getLagTimes(); |
92 | 93 | foreach ( $lags as $i => $lag ) { |
93 | | - if ( $i != 0 && isset( $this->mServers[$i]['max lag'] ) && |
94 | | - ( $lag === false || $lag > $this->mServers[$i]['max lag'] ) ) |
95 | | - { |
96 | | - unset( $loads[$i] ); |
| 94 | + if ( $i != 0 && isset( $this->mServers[$i]['max lag'] ) ) { |
| 95 | + if ( $lag === false ) { |
| 96 | + wfDebug( "Server #$i is not replicating\n" ); |
| 97 | + unset( $loads[$i] ); |
| 98 | + } elseif ( $lag > $this->mServers[$i]['max lag'] ) { |
| 99 | + wfDebug( "Server #$i is excessively lagged ($lag seconds)\n" ); |
| 100 | + unset( $loads[$i] ); |
| 101 | + } |
97 | 102 | } |
98 | 103 | } |
99 | 104 | |
— | — | @@ -126,114 +131,168 @@ |
127 | 132 | * |
128 | 133 | * Side effect: opens connections to databases |
129 | 134 | */ |
130 | | - function getReaderIndex() { |
131 | | - global $wgReadOnly, $wgDBClusterTimeout, $wgDBAvgStatusPoll; |
| 135 | + function getReaderIndex( $group = false, $wiki = false ) { |
| 136 | + global $wgReadOnly, $wgDBClusterTimeout, $wgDBAvgStatusPoll, $wgDBtype; |
| 137 | + |
| 138 | + # FIXME: For now, only go through all this for mysql databases |
| 139 | + if ($wgDBtype != 'mysql') { |
| 140 | + return $this->getWriterIndex(); |
| 141 | + } |
132 | 142 | |
133 | | - $fname = 'LoadBalancer::getReaderIndex'; |
134 | | - wfProfileIn( $fname ); |
| 143 | + if ( count( $this->mServers ) == 1 ) { |
| 144 | + # Skip the load balancing if there's only one server |
| 145 | + return 0; |
| 146 | + } elseif ( $this->mReadIndex >= 0 ) { |
| 147 | + return $this->mReadIndex; |
| 148 | + } |
135 | 149 | |
136 | | - $i = false; |
137 | | - if ( $this->mForce >= 0 ) { |
138 | | - $i = $this->mForce; |
139 | | - } elseif ( count( $this->mServers ) == 1 ) { |
140 | | - # Skip the load balancing if there's only one server |
141 | | - $i = 0; |
| 150 | + wfProfileIn( __METHOD__ ); |
| 151 | + |
| 152 | + $totalElapsed = 0; |
| 153 | + |
| 154 | + # convert from seconds to microseconds |
| 155 | + $timeout = $wgDBClusterTimeout * 1e6; |
| 156 | + |
| 157 | + # Find the relevant load array |
| 158 | + if ( $group !== false ) { |
| 159 | + if ( isset( $this->mGroupLoads[$group] ) ) { |
| 160 | + $nonErrorLoads = $this->mGroupLoads[$group]; |
| 161 | + } else { |
| 162 | + # No loads for this group, return false and the caller can use some other group |
| 163 | + wfDebug( __METHOD__.": no loads for group $group\n" ); |
| 164 | + wfProfileOut( __METHOD__ ); |
| 165 | + return false; |
| 166 | + } |
142 | 167 | } else { |
143 | | - if ( $this->mReadIndex >= 0 ) { |
144 | | - $i = $this->mReadIndex; |
145 | | - } else { |
146 | | - # $loads is $this->mLoads except with elements knocked out if they |
147 | | - # don't work |
148 | | - $loads = $this->mLoads; |
149 | | - $done = false; |
150 | | - $totalElapsed = 0; |
151 | | - do { |
152 | | - if ( $wgReadOnly or $this->mAllowLagged ) { |
153 | | - $i = $this->pickRandom( $loads ); |
154 | | - } else { |
155 | | - $i = $this->getRandomNonLagged( $loads ); |
156 | | - if ( $i === false && count( $loads ) != 0 ) { |
157 | | - # All slaves lagged. Switch to read-only mode |
158 | | - $wgReadOnly = wfMsgNoDBForContent( 'readonly_lag' ); |
159 | | - $i = $this->pickRandom( $loads ); |
160 | | - } |
161 | | - } |
162 | | - $serverIndex = $i; |
163 | | - if ( $i !== false ) { |
164 | | - wfDebugLog( 'connect', "$fname: Using reader #$i: {$this->mServers[$i]['host']}...\n" ); |
165 | | - $this->openConnection( $i ); |
| 168 | + $nonErrorLoads = $this->mLoads; |
| 169 | + } |
166 | 170 | |
167 | | - if ( !$this->isOpen( $i ) ) { |
168 | | - wfDebug( "$fname: Failed\n" ); |
169 | | - unset( $loads[$i] ); |
170 | | - $sleepTime = 0; |
171 | | - } else { |
172 | | - if ( isset( $this->mServers[$i]['max threads'] ) ) { |
173 | | - $status = $this->mConnections[$i]->getStatus("Thread%"); |
174 | | - if ( $status['Threads_running'] > $this->mServers[$i]['max threads'] ) { |
175 | | - # Too much load, back off and wait for a while. |
176 | | - # The sleep time is scaled by the number of threads connected, |
177 | | - # to produce a roughly constant global poll rate. |
178 | | - $sleepTime = $wgDBAvgStatusPoll * $status['Threads_connected']; |
| 171 | + if ( !$nonErrorLoads ) { |
| 172 | + throw new MWException( "Empty server array given to LoadBalancer" ); |
| 173 | + } |
179 | 174 | |
180 | | - # If we reach the timeout and exit the loop, don't use it |
181 | | - $i = false; |
182 | | - } else { |
183 | | - $done = true; |
184 | | - $sleepTime = 0; |
185 | | - } |
186 | | - } else { |
187 | | - $done = true; |
188 | | - $sleepTime = 0; |
189 | | - } |
190 | | - } |
191 | | - } else { |
192 | | - $sleepTime = 500000; |
| 175 | + $i = false; |
| 176 | + $found = false; |
| 177 | + $laggedSlaveMode = false; |
| 178 | + |
| 179 | + # First try quickly looking through the available servers for a server that |
| 180 | + # meets our criteria |
| 181 | + do { |
| 182 | + $totalThreadsConnected = 0; |
| 183 | + $overloadedServers = 0; |
| 184 | + $currentLoads = $nonErrorLoads; |
| 185 | + while ( count( $currentLoads ) ) { |
| 186 | + if ( $wgReadOnly || $this->mAllowLagged || $laggedSlaveMode ) { |
| 187 | + $i = $this->pickRandom( $currentLoads ); |
| 188 | + } else { |
| 189 | + $i = $this->getRandomNonLagged( $currentLoads ); |
| 190 | + if ( $i === false && count( $currentLoads ) != 0 ) { |
| 191 | + # All slaves lagged. Switch to read-only mode |
| 192 | + $wgReadOnly = wfMsgNoDBForContent( 'readonly_lag' ); |
| 193 | + $i = $this->pickRandom( $currentLoads ); |
| 194 | + $laggedSlaveMode = true; |
193 | 195 | } |
194 | | - if ( $sleepTime ) { |
195 | | - $totalElapsed += $sleepTime; |
196 | | - $x = "{$this->mServers[$serverIndex]['host']} [$serverIndex]"; |
197 | | - wfProfileIn( "$fname-sleep $x" ); |
198 | | - usleep( $sleepTime ); |
199 | | - wfProfileOut( "$fname-sleep $x" ); |
200 | | - } |
201 | | - } while ( count( $loads ) && !$done && $totalElapsed / 1e6 < $wgDBClusterTimeout ); |
| 196 | + } |
202 | 197 | |
203 | | - if ( $totalElapsed / 1e6 >= $wgDBClusterTimeout ) { |
204 | | - $this->mErrorConnection = false; |
205 | | - $this->mLastError = 'All servers busy'; |
| 198 | + if ( $i === false ) { |
| 199 | + # pickRandom() returned false |
| 200 | + # This is permanent and means the configuration wants us to return false |
| 201 | + wfDebugLog( 'connect', __METHOD__.": pickRandom() returned false\n" ); |
| 202 | + wfProfileOut( __METHOD__ ); |
| 203 | + return false; |
206 | 204 | } |
207 | 205 | |
208 | | - if ( $i !== false && $this->isOpen( $i ) ) { |
209 | | - # Wait for the session master pos for a short time |
210 | | - if ( $this->mWaitForFile ) { |
211 | | - if ( !$this->doWait( $i ) ) { |
212 | | - $this->mServers[$i]['slave pos'] = $this->mConnections[$i]->getSlavePos(); |
213 | | - } |
| 206 | + wfDebugLog( 'connect', __METHOD__.": Using reader #$i: {$this->mServers[$i]['host']}...\n" ); |
| 207 | + $conn = $this->openConnection( $i, $wiki ); |
| 208 | + |
| 209 | + if ( !$conn ) { |
| 210 | + wfDebugLog( 'connect', __METHOD__.": Failed connecting to $i/$wiki\n" ); |
| 211 | + unset( $nonErrorLoads[$i] ); |
| 212 | + unset( $currentLoads[$i] ); |
| 213 | + continue; |
| 214 | + } |
| 215 | + |
| 216 | + if ( isset( $this->mServers[$i]['max threads'] ) ) { |
| 217 | + $status = $conn->getStatus("Thread%"); |
| 218 | + if ( $wiki !== false ) { |
| 219 | + $this->reuseConnection( $conn ); |
214 | 220 | } |
215 | | - if ( $i !== false ) { |
216 | | - $this->mReadIndex = $i; |
| 221 | + if ( $status['Threads_running'] > $this->mServers[$i]['max threads'] ) { |
| 222 | + $totalThreadsConnected += $status['Threads_connected']; |
| 223 | + $overloadedServers++; |
| 224 | + unset( $currentLoads[$i] ); |
| 225 | + } else { |
| 226 | + # Max threads satisfied, return this server |
| 227 | + break 2; |
217 | 228 | } |
218 | 229 | } else { |
219 | | - $i = false; |
| 230 | + # No maximum, return this server |
| 231 | + if ( $wiki !== false ) { |
| 232 | + $this->reuseConnection( $conn ); |
| 233 | + } |
| 234 | + $found = true; |
| 235 | + break 2; |
220 | 236 | } |
221 | 237 | } |
| 238 | + |
| 239 | + # No server found yet |
| 240 | + $i = false; |
| 241 | + |
| 242 | + # If all servers were down, quit now |
| 243 | + if ( !count( $nonErrorLoads ) ) { |
| 244 | + wfDebugLog( 'connect', "All servers down\n" ); |
| 245 | + break; |
| 246 | + } |
| 247 | + |
| 248 | + # Some servers must have been overloaded |
| 249 | + if ( $overloadedServers == 0 ) { |
| 250 | + throw new MWException( __METHOD__.": unexpectedly found no overloaded servers" ); |
| 251 | + } |
| 252 | + # Back off for a while |
| 253 | + # Scale the sleep time by the number of connected threads, to produce a |
| 254 | + # roughly constant global poll rate |
| 255 | + $avgThreads = $totalThreadsConnected / $overloadedServers; |
| 256 | + $totalElapsed += $this->sleep( $wgDBAvgStatusPoll * $avgThreads ); |
| 257 | + } while ( $totalElapsed < $timeout ); |
| 258 | + |
| 259 | + if ( $totalElapsed >= $timeout ) { |
| 260 | + wfDebugLog( 'connect', "All servers busy\n" ); |
| 261 | + $this->mErrorConnection = false; |
| 262 | + $this->mLastError = 'All servers busy'; |
222 | 263 | } |
223 | | - wfProfileOut( $fname ); |
| 264 | + |
| 265 | + if ( $i !== false ) { |
| 266 | + # Wait for the session master pos for a short time |
| 267 | + if ( $this->mWaitForPos && $i > 0 ) { |
| 268 | + if ( !$this->doWait( $i ) ) { |
| 269 | + $this->mServers[$i]['slave pos'] = $conn->getSlavePos(); |
| 270 | + } |
| 271 | + } |
| 272 | + if ( $i !== false ) { |
| 273 | + $this->mReadIndex = $i; |
| 274 | + } |
| 275 | + } |
| 276 | + wfProfileOut( __METHOD__ ); |
224 | 277 | return $i; |
225 | 278 | } |
226 | 279 | |
227 | 280 | /** |
| 281 | + * Wait for a specified number of microseconds, and return the period waited |
| 282 | + */ |
| 283 | + function sleep( $t ) { |
| 284 | + wfProfileIn( __METHOD__ ); |
| 285 | + wfDebug( __METHOD__.": waiting $t us\n" ); |
| 286 | + usleep( $t ); |
| 287 | + wfProfileOut( __METHOD__ ); |
| 288 | + return $t; |
| 289 | + } |
| 290 | + |
| 291 | + /** |
228 | 292 | * Get a random server to use in a query group |
| 293 | + * @deprecated use getReaderIndex |
229 | 294 | */ |
230 | 295 | function getGroupIndex( $group ) { |
231 | | - if ( isset( $this->mGroupLoads[$group] ) ) { |
232 | | - $i = $this->pickRandom( $this->mGroupLoads[$group] ); |
233 | | - } else { |
234 | | - $i = false; |
235 | | - } |
236 | | - wfDebug( "Query group $group => $i\n" ); |
237 | | - return $i; |
| 296 | + return $this->getReaderIndex( $group ); |
238 | 297 | } |
239 | 298 | |
240 | 299 | /** |
— | — | @@ -241,104 +300,92 @@ |
242 | 301 | * If a DB_SLAVE connection has been opened already, waits |
243 | 302 | * Otherwise sets a variable telling it to wait if such a connection is opened |
244 | 303 | */ |
245 | | - function waitFor( $file, $pos ) { |
246 | | - $fname = 'LoadBalancer::waitFor'; |
247 | | - wfProfileIn( $fname ); |
| 304 | + public function waitFor( $pos ) { |
| 305 | + wfProfileIn( __METHOD__ ); |
| 306 | + $this->mWaitForPos = $pos; |
| 307 | + $i = $this->mReadIndex; |
248 | 308 | |
249 | | - wfDebug( "User master pos: $file $pos\n" ); |
250 | | - $this->mWaitForFile = false; |
251 | | - $this->mWaitForPos = false; |
| 309 | + if ( $i > 0 ) { |
| 310 | + if ( !$this->doWait( $i ) ) { |
| 311 | + $this->mServers[$i]['slave pos'] = $this->getAnyOpenConnection( $i )->getSlavePos(); |
| 312 | + $this->mLaggedSlaveMode = true; |
| 313 | + } |
| 314 | + } |
| 315 | + wfProfileOut( __METHOD__ ); |
| 316 | + } |
252 | 317 | |
253 | | - if ( count( $this->mServers ) > 1 ) { |
254 | | - $this->mWaitForFile = $file; |
255 | | - $this->mWaitForPos = $pos; |
256 | | - $i = $this->mReadIndex; |
257 | | - |
258 | | - if ( $i > 0 ) { |
259 | | - if ( !$this->doWait( $i ) ) { |
260 | | - $this->mServers[$i]['slave pos'] = $this->mConnections[$i]->getSlavePos(); |
261 | | - $this->mLaggedSlaveMode = true; |
262 | | - } |
| 318 | + /** |
| 319 | + * Get any open connection to a given server index, local or foreign |
| 320 | + * Returns false if there is no connection open |
| 321 | + */ |
| 322 | + function getAnyOpenConnection( $i ) { |
| 323 | + foreach ( $this->mConns as $type => $conns ) { |
| 324 | + if ( !empty( $conns[$i] ) ) { |
| 325 | + return reset( $conns[$i] ); |
263 | 326 | } |
264 | 327 | } |
265 | | - wfProfileOut( $fname ); |
| 328 | + return false; |
266 | 329 | } |
267 | 330 | |
268 | 331 | /** |
269 | 332 | * Wait for a given slave to catch up to the master pos stored in $this |
270 | 333 | */ |
271 | 334 | function doWait( $index ) { |
272 | | - global $wgMemc; |
| 335 | + # Find a connection to wait on |
| 336 | + $conn = $this->getAnyOpenConnection( $index ); |
| 337 | + if ( !$conn ) { |
| 338 | + wfDebug( __METHOD__ . ": no connection open\n" ); |
| 339 | + return false; |
| 340 | + } |
273 | 341 | |
274 | | - $retVal = false; |
| 342 | + wfDebug( __METHOD__.": Waiting for slave #$index to catch up...\n" ); |
| 343 | + $result = $conn->masterPosWait( $this->mWaitForPos, $this->mWaitTimeout ); |
275 | 344 | |
276 | | - # Debugging hacks |
277 | | - if ( isset( $this->mServers[$index]['lagged slave'] ) ) { |
| 345 | + if ( $result == -1 || is_null( $result ) ) { |
| 346 | + # Timed out waiting for slave, use master instead |
| 347 | + wfDebug( __METHOD__.": Timed out waiting for slave #$index pos {$this->mWaitForPos}\n" ); |
278 | 348 | return false; |
279 | | - } elseif ( isset( $this->mServers[$index]['fake slave'] ) ) { |
| 349 | + } else { |
| 350 | + wfDebug( __METHOD__.": Done\n" ); |
280 | 351 | return true; |
281 | 352 | } |
282 | | - |
283 | | - $key = 'masterpos:' . $index; |
284 | | - $memcPos = $wgMemc->get( $key ); |
285 | | - if ( $memcPos ) { |
286 | | - list( $file, $pos ) = explode( ' ', $memcPos ); |
287 | | - # If the saved position is later than the requested position, return now |
288 | | - if ( $file == $this->mWaitForFile && $this->mWaitForPos <= $pos ) { |
289 | | - $retVal = true; |
290 | | - } |
291 | | - } |
292 | | - |
293 | | - if ( !$retVal && $this->isOpen( $index ) ) { |
294 | | - $conn =& $this->mConnections[$index]; |
295 | | - wfDebug( "Waiting for slave #$index to catch up...\n" ); |
296 | | - $result = $conn->masterPosWait( $this->mWaitForFile, $this->mWaitForPos, $this->mWaitTimeout ); |
297 | | - |
298 | | - if ( $result == -1 || is_null( $result ) ) { |
299 | | - # Timed out waiting for slave, use master instead |
300 | | - wfDebug( "Timed out waiting for slave #$index pos {$this->mWaitForFile} {$this->mWaitForPos}\n" ); |
301 | | - $retVal = false; |
302 | | - } else { |
303 | | - $retVal = true; |
304 | | - wfDebug( "Done\n" ); |
305 | | - } |
306 | | - } |
307 | | - return $retVal; |
308 | 353 | } |
309 | 354 | |
310 | 355 | /** |
311 | 356 | * Get a connection by index |
| 357 | + * This is the main entry point for this class. |
312 | 358 | */ |
313 | | - function &getConnection( $i, $fail = true, $groups = array() ) |
314 | | - { |
| 359 | + public function &getConnection( $i, $groups = array(), $wiki = false ) { |
315 | 360 | global $wgDBtype; |
316 | | - $fname = 'LoadBalancer::getConnection'; |
317 | | - wfProfileIn( $fname ); |
| 361 | + wfProfileIn( __METHOD__ ); |
318 | 362 | |
| 363 | + if ( $wiki === wfWikiID() ) { |
| 364 | + $wiki = false; |
| 365 | + } |
319 | 366 | |
320 | 367 | # Query groups |
321 | 368 | if ( !is_array( $groups ) ) { |
322 | | - $groupIndex = $this->getGroupIndex( $groups ); |
| 369 | + $groupIndex = $this->getReaderIndex( $groups, $wiki ); |
323 | 370 | if ( $groupIndex !== false ) { |
| 371 | + $serverName = $this->getServerName( $groupIndex ); |
| 372 | + wfDebug( __METHOD__.": using server $serverName for group $groups\n" ); |
324 | 373 | $i = $groupIndex; |
325 | 374 | } |
326 | 375 | } else { |
327 | 376 | foreach ( $groups as $group ) { |
328 | | - $groupIndex = $this->getGroupIndex( $group ); |
| 377 | + $groupIndex = $this->getReaderIndex( $group, $wiki ); |
329 | 378 | if ( $groupIndex !== false ) { |
| 379 | + $serverName = $this->getServerName( $groupIndex ); |
| 380 | + wfDebug( __METHOD__.": using server $serverName for group $group\n" ); |
330 | 381 | $i = $groupIndex; |
331 | 382 | break; |
332 | 383 | } |
333 | 384 | } |
334 | 385 | } |
335 | 386 | |
336 | | - # For now, only go through all this for mysql databases |
337 | | - if ($wgDBtype != 'mysql') { |
338 | | - $i = $this->getWriterIndex(); |
339 | | - } |
340 | 387 | # Operation-based index |
341 | | - elseif ( $i == DB_SLAVE ) { |
342 | | - $i = $this->getReaderIndex(); |
| 388 | + if ( $i == DB_SLAVE ) { |
| 389 | + $i = $this->getReaderIndex( false, $wiki ); |
343 | 390 | } elseif ( $i == DB_MASTER ) { |
344 | 391 | $i = $this->getWriterIndex(); |
345 | 392 | } elseif ( $i == DB_LAST ) { |
— | — | @@ -354,43 +401,171 @@ |
355 | 402 | if ( $i === false ) { |
356 | 403 | $this->reportConnectionError( $this->mErrorConnection ); |
357 | 404 | } |
| 405 | + |
358 | 406 | # Now we have an explicit index into the servers array |
359 | | - $this->openConnection( $i, $fail ); |
| 407 | + $conn = $this->openConnection( $i, $wiki ); |
| 408 | + if ( !$conn ) { |
| 409 | + $this->reportConnectionError( $this->mErrorConnection ); |
| 410 | + } |
360 | 411 | |
361 | | - wfProfileOut( $fname ); |
362 | | - return $this->mConnections[$i]; |
| 412 | + wfProfileOut( __METHOD__ ); |
| 413 | + return $conn; |
363 | 414 | } |
364 | 415 | |
365 | 416 | /** |
| 417 | + * Mark a foreign connection as being available for reuse under a different |
| 418 | + * DB name or prefix. This mechanism is reference-counted, and must be called |
| 419 | + * the same number of times as getConnection() to work. |
| 420 | + */ |
| 421 | + public function reuseConnection( $conn ) { |
| 422 | + $serverIndex = $conn->getLBInfo('serverIndex'); |
| 423 | + $refCount = $conn->getLBInfo('foreignPoolRefCount'); |
| 424 | + $dbName = $conn->getDBname(); |
| 425 | + $prefix = $conn->tablePrefix(); |
| 426 | + if ( strval( $prefix ) !== '' ) { |
| 427 | + $wiki = "$dbName-$prefix"; |
| 428 | + } else { |
| 429 | + $wiki = $dbName; |
| 430 | + } |
| 431 | + if ( $serverIndex === null || $refCount === null ) { |
| 432 | + wfDebug( __METHOD__.": this connection was not opened as a foreign connection\n" ); |
| 433 | + /** |
| 434 | + * This can happen in code like: |
| 435 | + * foreach ( $dbs as $db ) { |
| 436 | + * $conn = $lb->getConnection( DB_SLAVE, array(), $db ); |
| 437 | + * ... |
| 438 | + * $lb->reuseConnection( $conn ); |
| 439 | + * } |
| 440 | + * When a connection to the local DB is opened in this way, reuseConnection() |
| 441 | + * should be ignored |
| 442 | + */ |
| 443 | + return; |
| 444 | + } |
| 445 | + if ( $this->mConns['foreignUsed'][$serverIndex][$wiki] !== $conn ) { |
| 446 | + throw new MWException( __METHOD__.": connection not found, has the connection been freed already?" ); |
| 447 | + } |
| 448 | + $conn->setLBInfo( 'foreignPoolRefCount', --$refCount ); |
| 449 | + if ( $refCount <= 0 ) { |
| 450 | + $this->mConns['foreignFree'][$serverIndex][$wiki] = $conn; |
| 451 | + unset( $this->mConns['foreignUsed'][$serverIndex][$wiki] ); |
| 452 | + wfDebug( __METHOD__.": freed connection $serverIndex/$wiki\n" ); |
| 453 | + } else { |
| 454 | + wfDebug( __METHOD__.": reference count for $serverIndex/$wiki reduced to $refCount\n" ); |
| 455 | + } |
| 456 | + } |
| 457 | + |
| 458 | + /** |
366 | 459 | * Open a connection to the server given by the specified index |
367 | | - * Index must be an actual index into the array |
368 | | - * Returns success |
| 460 | + * Index must be an actual index into the array. |
| 461 | + * If the server is already open, returns it. |
| 462 | + * |
| 463 | + * On error, returns false, and the connection which caused the |
| 464 | + * error will be available via $this->mErrorConnection. |
| 465 | + * |
| 466 | + * @param integer $i Server index |
| 467 | + * @param string $wiki Wiki ID to open |
| 468 | + * @return Database |
| 469 | + * |
369 | 470 | * @access private |
370 | 471 | */ |
371 | | - function openConnection( $i, $fail = false ) { |
372 | | - $fname = 'LoadBalancer::openConnection'; |
373 | | - wfProfileIn( $fname ); |
374 | | - $success = true; |
| 472 | + function openConnection( $i, $wiki = false ) { |
| 473 | + wfProfileIn( __METHOD__ ); |
375 | 474 | |
376 | | - if ( !$this->isOpen( $i ) ) { |
377 | | - $this->mConnections[$i] = $this->reallyOpenConnection( $this->mServers[$i] ); |
| 475 | + if ( $wiki !== false ) { |
| 476 | + return $this->openForeignConnection( $i, $wiki ); |
378 | 477 | } |
379 | | - |
380 | | - if ( !$this->isOpen( $i ) ) { |
381 | | - wfDebug( "Failed to connect to database $i at {$this->mServers[$i]['host']}\n" ); |
382 | | - if ( $fail ) { |
383 | | - $this->reportConnectionError( $this->mConnections[$i] ); |
| 478 | + if ( isset( $this->mConns['local'][$i][0] ) ) { |
| 479 | + $conn = $this->mConns['local'][$i][0]; |
| 480 | + } else { |
| 481 | + $server = $this->mServers[$i]; |
| 482 | + $server['serverIndex'] = $i; |
| 483 | + $conn = $this->reallyOpenConnection( $server ); |
| 484 | + if ( $conn->isOpen() ) { |
| 485 | + $this->mConns['local'][$i][0] = $conn; |
| 486 | + } else { |
| 487 | + wfDebug( "Failed to connect to database $i at {$this->mServers[$i]['host']}\n" ); |
| 488 | + $this->mErrorConnection = $conn; |
| 489 | + $conn = false; |
384 | 490 | } |
385 | | - $this->mErrorConnection = $this->mConnections[$i]; |
386 | | - $this->mConnections[$i] = false; |
387 | | - $success = false; |
388 | 491 | } |
389 | 492 | $this->mLastIndex = $i; |
390 | | - wfProfileOut( $fname ); |
391 | | - return $success; |
| 493 | + wfProfileOut( __METHOD__ ); |
| 494 | + return $conn; |
392 | 495 | } |
393 | 496 | |
394 | 497 | /** |
| 498 | + * Open a connection to a foreign DB, or return one if it is already open. |
| 499 | + * |
| 500 | + * Increments a reference count on the returned connection which locks the |
| 501 | + * connection to the requested wiki. This reference count can be |
| 502 | + * decremented by calling reuseConnection(). |
| 503 | + * |
| 504 | + * If a connection is open to the appropriate server already, but with the wrong |
| 505 | + * database, it will be switched to the right database and returned, as long as |
| 506 | + * it has been freed first with reuseConnection(). |
| 507 | + * |
| 508 | + * On error, returns false, and the connection which caused the |
| 509 | + * error will be available via $this->mErrorConnection. |
| 510 | + * |
| 511 | + * @param integer $i Server index |
| 512 | + * @param string $wiki Wiki ID to open |
| 513 | + * @return Database |
| 514 | + */ |
| 515 | + function openForeignConnection( $i, $wiki ) { |
| 516 | + list( $dbName, $prefix ) = wfSplitWikiID( $wiki ); |
| 517 | + |
| 518 | + if ( isset( $this->mConns['foreignUsed'][$i][$wiki] ) ) { |
| 519 | + // Reuse an already-used connection |
| 520 | + $conn = $this->mConns['foreignUsed'][$i][$wiki]; |
| 521 | + wfDebug( __METHOD__.": reusing connection $i/$wiki\n" ); |
| 522 | + } elseif ( isset( $this->mConns['foreignFree'][$i][$wiki] ) ) { |
| 523 | + // Reuse a free connection for the same wiki |
| 524 | + $conn = $this->mConns['foreignFree'][$i][$wiki]; |
| 525 | + unset( $this->mConns['foreignFree'][$i][$wiki] ); |
| 526 | + $this->mConns['foreignUsed'][$i][$wiki] = $conn; |
| 527 | + wfDebug( __METHOD__.": reusing free connection $i/$wiki\n" ); |
| 528 | + } elseif ( !empty( $this->mConns['foreignFree'][$i] ) ) { |
| 529 | + // Reuse a connection from another wiki |
| 530 | + $conn = reset( $this->mConns['foreignFree'][$i] ); |
| 531 | + $oldWiki = key( $this->mConns['foreignFree'][$i] ); |
| 532 | + |
| 533 | + if ( !$conn->selectDB( $dbName ) ) { |
| 534 | + global $wguname; |
| 535 | + $this->mLastError = "Error selecting database $dbName on server " . |
| 536 | + $conn->getServer() . " from client host {$wguname['nodename']}\n"; |
| 537 | + $this->mErrorConnection = $conn; |
| 538 | + $conn = false; |
| 539 | + } else { |
| 540 | + $conn->tablePrefix( $prefix ); |
| 541 | + unset( $this->mConns['foreignFree'][$i][$oldWiki] ); |
| 542 | + $this->mConns['foreignUsed'][$i][$wiki] = $conn; |
| 543 | + wfDebug( __METHOD__.": reusing free connection from $oldWiki for $wiki\n" ); |
| 544 | + } |
| 545 | + } else { |
| 546 | + // Open a new connection |
| 547 | + $server = $this->mServers[$i]; |
| 548 | + $server['serverIndex'] = $i; |
| 549 | + $server['foreignPoolRefCount'] = 0; |
| 550 | + $conn = $this->reallyOpenConnection( $server, $dbName ); |
| 551 | + if ( !$conn->isOpen() ) { |
| 552 | + wfDebug( __METHOD__.": error opening connection for $i/$wiki\n" ); |
| 553 | + $this->mErrorConnection = $conn; |
| 554 | + $conn = false; |
| 555 | + } else { |
| 556 | + $this->mConns['foreignUsed'][$i][$wiki] = $conn; |
| 557 | + wfDebug( __METHOD__.": opened new connection for $i/$wiki\n" ); |
| 558 | + } |
| 559 | + } |
| 560 | + |
| 561 | + // Increment reference count |
| 562 | + if ( $conn ) { |
| 563 | + $refCount = $conn->getLBInfo( 'foreignPoolRefCount' ); |
| 564 | + $conn->setLBInfo( 'foreignPoolRefCount', $refCount + 1 ); |
| 565 | + } |
| 566 | + return $conn; |
| 567 | + } |
| 568 | + |
| 569 | + /** |
395 | 570 | * Test if the specified index represents an open connection |
396 | 571 | * @access private |
397 | 572 | */ |
— | — | @@ -398,37 +573,47 @@ |
399 | 574 | if( !is_integer( $index ) ) { |
400 | 575 | return false; |
401 | 576 | } |
402 | | - if ( array_key_exists( $index, $this->mConnections ) && is_object( $this->mConnections[$index] ) && |
403 | | - $this->mConnections[$index]->isOpen() ) |
404 | | - { |
405 | | - return true; |
406 | | - } else { |
407 | | - return false; |
408 | | - } |
| 577 | + return (bool)$this->getAnyOpenConnection( $index ); |
409 | 578 | } |
410 | 579 | |
411 | 580 | /** |
412 | | - * Really opens a connection |
| 581 | + * Really opens a connection. Uncached. |
| 582 | + * Returns a Database object whether or not the connection was successful. |
413 | 583 | * @access private |
414 | 584 | */ |
415 | | - function reallyOpenConnection( &$server ) { |
| 585 | + function reallyOpenConnection( $server, $dbNameOverride = false ) { |
416 | 586 | if( !is_array( $server ) ) { |
417 | 587 | throw new MWException( 'You must update your load-balancing configuration. See DefaultSettings.php entry for $wgDBservers.' ); |
418 | 588 | } |
419 | 589 | |
420 | 590 | extract( $server ); |
| 591 | + if ( $dbNameOverride !== false ) { |
| 592 | + $dbname = $dbNameOverride; |
| 593 | + } |
| 594 | + |
421 | 595 | # Get class for this database type |
422 | 596 | $class = 'Database' . ucfirst( $type ); |
423 | 597 | |
424 | 598 | # Create object |
| 599 | + wfDebug( "Connecting to $host...\n" ); |
425 | 600 | $db = new $class( $host, $user, $password, $dbname, 1, $flags ); |
| 601 | + if ( $db->isOpen() ) { |
| 602 | + wfDebug( "Connected\n" ); |
| 603 | + } else { |
| 604 | + wfDebug( "Failed\n" ); |
| 605 | + } |
426 | 606 | $db->setLBInfo( $server ); |
| 607 | + if ( isset( $server['fakeSlaveLag'] ) ) { |
| 608 | + $db->setFakeSlaveLag( $server['fakeSlaveLag'] ); |
| 609 | + } |
| 610 | + if ( isset( $server['fakeMaster'] ) ) { |
| 611 | + $db->setFakeMaster( true ); |
| 612 | + } |
427 | 613 | return $db; |
428 | 614 | } |
429 | 615 | |
430 | 616 | function reportConnectionError( &$conn ) { |
431 | | - $fname = 'LoadBalancer::reportConnectionError'; |
432 | | - wfProfileIn( $fname ); |
| 617 | + wfProfileIn( __METHOD__ ); |
433 | 618 | # Prevent infinite recursion |
434 | 619 | |
435 | 620 | static $reporting = false; |
— | — | @@ -455,7 +640,7 @@ |
456 | 641 | } |
457 | 642 | $reporting = false; |
458 | 643 | } |
459 | | - wfProfileOut( $fname ); |
| 644 | + wfProfileOut( __METHOD__ ); |
460 | 645 | } |
461 | 646 | |
462 | 647 | function getWriterIndex() { |
— | — | @@ -463,16 +648,6 @@ |
464 | 649 | } |
465 | 650 | |
466 | 651 | /** |
467 | | - * Force subsequent calls to getConnection(DB_SLAVE) to return the |
468 | | - * given index. Set to -1 to restore the original load balancing |
469 | | - * behaviour. I thought this was a good idea when I originally |
470 | | - * wrote this class, but it has never been used. |
471 | | - */ |
472 | | - function force( $i ) { |
473 | | - $this->mForce = $i; |
474 | | - } |
475 | | - |
476 | | - /** |
477 | 652 | * Returns true if the specified index is a valid server index |
478 | 653 | */ |
479 | 654 | function haveIndex( $i ) { |
— | — | @@ -494,54 +669,88 @@ |
495 | 670 | } |
496 | 671 | |
497 | 672 | /** |
498 | | - * Save master pos to the session and to memcached, if the session exists |
| 673 | + * Get the host name or IP address of the server with the specified index |
499 | 674 | */ |
500 | | - function saveMasterPos() { |
501 | | - if ( session_id() != '' && count( $this->mServers ) > 1 ) { |
502 | | - # If this entire request was served from a slave without opening a connection to the |
503 | | - # master (however unlikely that may be), then we can fetch the position from the slave. |
504 | | - if ( empty( $this->mConnections[0] ) ) { |
505 | | - $conn =& $this->getConnection( DB_SLAVE ); |
506 | | - list( $file, $pos ) = $conn->getSlavePos(); |
507 | | - wfDebug( "Saving master pos fetched from slave: $file $pos\n" ); |
508 | | - } else { |
509 | | - $conn =& $this->getConnection( 0 ); |
510 | | - list( $file, $pos ) = $conn->getMasterPos(); |
511 | | - wfDebug( "Saving master pos: $file $pos\n" ); |
512 | | - } |
513 | | - if ( $file !== false ) { |
514 | | - $_SESSION['master_log_file'] = $file; |
515 | | - $_SESSION['master_pos'] = $pos; |
516 | | - } |
| 675 | + function getServerName( $i ) { |
| 676 | + if ( isset( $this->mServers[$i]['hostName'] ) ) { |
| 677 | + return $this->mServers[$i]['hostName']; |
| 678 | + } elseif ( isset( $this->mServers[$i]['host'] ) ) { |
| 679 | + return $this->mServers[$i]['host']; |
| 680 | + } else { |
| 681 | + return ''; |
517 | 682 | } |
518 | 683 | } |
519 | 684 | |
520 | 685 | /** |
521 | | - * Loads the master pos from the session, waits for it if necessary |
| 686 | + * Get the current master position for chronology control purposes |
| 687 | + * @return mixed |
522 | 688 | */ |
523 | | - function loadMasterPos() { |
524 | | - if ( isset( $_SESSION['master_log_file'] ) && isset( $_SESSION['master_pos'] ) ) { |
525 | | - $this->waitFor( $_SESSION['master_log_file'], $_SESSION['master_pos'] ); |
| 689 | + function getMasterPos() { |
| 690 | + # If this entire request was served from a slave without opening a connection to the |
| 691 | + # master (however unlikely that may be), then we can fetch the position from the slave. |
| 692 | + $masterConn = $this->getAnyOpenConnection( 0 ); |
| 693 | + if ( !$masterConn ) { |
| 694 | + $conn = $this->getConnection( DB_SLAVE ); |
| 695 | + $pos = $conn->getSlavePos(); |
| 696 | + wfDebug( "Master pos fetched from slave\n" ); |
| 697 | + } else { |
| 698 | + $pos = $masterConn->getMasterPos(); |
| 699 | + wfDebug( "Master pos fetched from master\n" ); |
526 | 700 | } |
| 701 | + return $pos; |
527 | 702 | } |
528 | 703 | |
529 | 704 | /** |
530 | 705 | * Close all open connections |
531 | 706 | */ |
532 | 707 | function closeAll() { |
533 | | - foreach( $this->mConnections as $i => $conn ) { |
534 | | - if ( $this->isOpen( $i ) ) { |
535 | | - // Need to use this syntax because $conn is a copy not a reference |
536 | | - $this->mConnections[$i]->close(); |
| 708 | + foreach ( $this->mConns as $conns2 ) { |
| 709 | + foreach ( $conns2 as $conns3 ) { |
| 710 | + foreach ( $conns3 as $conn ) { |
| 711 | + $conn->close(); |
| 712 | + } |
537 | 713 | } |
538 | 714 | } |
| 715 | + $this->mConns = array( |
| 716 | + 'local' => array(), |
| 717 | + 'foreignFree' => array(), |
| 718 | + 'foreignUsed' => array(), |
| 719 | + ); |
539 | 720 | } |
540 | 721 | |
| 722 | + /** |
| 723 | + * Close a connection |
| 724 | + * Using this function makes sure the LoadBalancer knows the connection is closed. |
| 725 | + * If you use $conn->close() directly, the load balancer won't update its state. |
| 726 | + */ |
| 727 | + function closeConnecton( $conn ) { |
| 728 | + $done = false; |
| 729 | + foreach ( $this->mConns as $i1 => $conns2 ) { |
| 730 | + foreach ( $conns2 as $i2 => $conns3 ) { |
| 731 | + foreach ( $conns3 as $i3 => $candidateConn ) { |
| 732 | + if ( $conn === $candidateConn ) { |
| 733 | + $conn->close(); |
| 734 | + unset( $this->mConns[$i1][$i2][$i3] ); |
| 735 | + $done = true; |
| 736 | + break; |
| 737 | + } |
| 738 | + } |
| 739 | + } |
| 740 | + } |
| 741 | + if ( !$done ) { |
| 742 | + $conn->close(); |
| 743 | + } |
| 744 | + } |
| 745 | + |
| 746 | + /** |
| 747 | + * Commit transactions on all open connections |
| 748 | + */ |
541 | 749 | function commitAll() { |
542 | | - foreach( $this->mConnections as $i => $conn ) { |
543 | | - if ( $this->isOpen( $i ) ) { |
544 | | - // Need to use this syntax because $conn is a copy not a reference |
545 | | - $this->mConnections[$i]->immediateCommit(); |
| 750 | + foreach ( $this->mConns as $conns2 ) { |
| 751 | + foreach ( $conns2 as $conns3 ) { |
| 752 | + foreach ( $conns3 as $conn ) { |
| 753 | + $conn->immediateCommit(); |
| 754 | + } |
546 | 755 | } |
547 | 756 | } |
548 | 757 | } |
— | — | @@ -549,11 +758,16 @@ |
550 | 759 | /* Issue COMMIT only on master, only if queries were done on connection */ |
551 | 760 | function commitMasterChanges() { |
552 | 761 | // Always 0, but who knows.. :) |
553 | | - $i = $this->getWriterIndex(); |
554 | | - if (array_key_exists($i,$this->mConnections)) { |
555 | | - if ($this->mConnections[$i]->lastQuery() != '') { |
556 | | - $this->mConnections[$i]->immediateCommit(); |
| 762 | + $masterIndex = $this->getWriterIndex(); |
| 763 | + foreach ( $this->mConns as $type => $conns2 ) { |
| 764 | + if ( empty( $conns2[$masterIndex] ) ) { |
| 765 | + continue; |
557 | 766 | } |
| 767 | + foreach ( $conns2[$masterIndex] as $conn ) { |
| 768 | + if ( $conn->lastQuery() != '' ) { |
| 769 | + $conn->commit(); |
| 770 | + } |
| 771 | + } |
558 | 772 | } |
559 | 773 | } |
560 | 774 | |
— | — | @@ -574,10 +788,12 @@ |
575 | 789 | |
576 | 790 | function pingAll() { |
577 | 791 | $success = true; |
578 | | - foreach ( $this->mConnections as $i => $conn ) { |
579 | | - if ( $this->isOpen( $i ) ) { |
580 | | - if ( !$this->mConnections[$i]->ping() ) { |
581 | | - $success = false; |
| 792 | + foreach ( $this->mConns as $conns2 ) { |
| 793 | + foreach ( $conns2 as $conns3 ) { |
| 794 | + foreach ( $conns3 as $conn ) { |
| 795 | + if ( !$conn->ping() ) { |
| 796 | + $success = false; |
| 797 | + } |
582 | 798 | } |
583 | 799 | } |
584 | 800 | } |
— | — | @@ -592,61 +808,74 @@ |
593 | 809 | $maxLag = -1; |
594 | 810 | $host = ''; |
595 | 811 | foreach ( $this->mServers as $i => $conn ) { |
596 | | - if ( $this->openConnection( $i ) ) { |
597 | | - $lag = $this->mConnections[$i]->getLag(); |
598 | | - if ( $lag > $maxLag ) { |
599 | | - $maxLag = $lag; |
600 | | - $host = $this->mServers[$i]['host']; |
601 | | - } |
| 812 | + $conn = $this->getAnyOpenConnection( $i ); |
| 813 | + if ( !$conn ) { |
| 814 | + $conn = $this->openConnection( $i ); |
602 | 815 | } |
| 816 | + if ( !$conn ) { |
| 817 | + continue; |
| 818 | + } |
| 819 | + $lag = $conn->getLag(); |
| 820 | + if ( $lag > $maxLag ) { |
| 821 | + $maxLag = $lag; |
| 822 | + $host = $this->mServers[$i]['host']; |
| 823 | + } |
603 | 824 | } |
604 | 825 | return array( $host, $maxLag ); |
605 | 826 | } |
606 | 827 | |
607 | 828 | /** |
608 | | - * Get lag time for each DB |
609 | | - * Results are cached for a short time in memcached |
| 829 | + * Get lag time for each server |
| 830 | + * Results are cached for a short time in memcached, and indefinitely in the process cache |
610 | 831 | */ |
611 | 832 | function getLagTimes() { |
612 | 833 | wfProfileIn( __METHOD__ ); |
613 | | - $expiry = 5; |
614 | | - $requestRate = 10; |
615 | 834 | |
616 | | - global $wgMemc; |
617 | | - $times = $wgMemc->get( wfMemcKey( 'lag_times' ) ); |
618 | | - if ( $times ) { |
619 | | - # Randomly recache with probability rising over $expiry |
620 | | - $elapsed = time() - $times['timestamp']; |
621 | | - $chance = max( 0, ( $expiry - $elapsed ) * $requestRate ); |
622 | | - if ( mt_rand( 0, $chance ) != 0 ) { |
623 | | - unset( $times['timestamp'] ); |
624 | | - wfProfileOut( __METHOD__ ); |
625 | | - return $times; |
| 835 | + if ( !isset( $this->mLagTimes ) ) { |
| 836 | + $expiry = 5; |
| 837 | + $requestRate = 10; |
| 838 | + |
| 839 | + global $wgMemc; |
| 840 | + $masterName = $this->getServerName( 0 ); |
| 841 | + $memcKey = wfMemcKey( 'lag_times', $masterName ); |
| 842 | + $times = $wgMemc->get( $memcKey ); |
| 843 | + if ( $times ) { |
| 844 | + # Randomly recache with probability rising over $expiry |
| 845 | + $elapsed = time() - $times['timestamp']; |
| 846 | + $chance = max( 0, ( $expiry - $elapsed ) * $requestRate ); |
| 847 | + if ( mt_rand( 0, $chance ) != 0 ) { |
| 848 | + unset( $times['timestamp'] ); |
| 849 | + wfProfileOut( __METHOD__ ); |
| 850 | + return $times; |
| 851 | + } |
| 852 | + wfIncrStats( 'lag_cache_miss_expired' ); |
| 853 | + } else { |
| 854 | + wfIncrStats( 'lag_cache_miss_absent' ); |
626 | 855 | } |
627 | | - wfIncrStats( 'lag_cache_miss_expired' ); |
628 | | - } else { |
629 | | - wfIncrStats( 'lag_cache_miss_absent' ); |
630 | | - } |
631 | 856 | |
632 | | - # Cache key missing or expired |
| 857 | + # Cache key missing or expired |
633 | 858 | |
634 | | - $times = array(); |
635 | | - foreach ( $this->mServers as $i => $conn ) { |
636 | | - if ($i==0) { # Master |
637 | | - $times[$i] = 0; |
638 | | - } elseif ( $this->openConnection( $i ) ) { |
639 | | - $times[$i] = $this->mConnections[$i]->getLag(); |
| 859 | + $times = array(); |
| 860 | + foreach ( $this->mServers as $i => $conn ) { |
| 861 | + if ($i == 0) { # Master |
| 862 | + $times[$i] = 0; |
| 863 | + } elseif ( false !== ( $conn = $this->getAnyOpenConnection( $i ) ) ) { |
| 864 | + $times[$i] = $conn->getLag(); |
| 865 | + } elseif ( false !== ( $conn = $this->openConnection( $i ) ) ) { |
| 866 | + $times[$i] = $conn->getLag(); |
| 867 | + } |
640 | 868 | } |
641 | | - } |
642 | 869 | |
643 | | - # Add a timestamp key so we know when it was cached |
644 | | - $times['timestamp'] = time(); |
645 | | - $wgMemc->set( wfMemcKey( 'lag_times' ), $times, $expiry ); |
| 870 | + # Add a timestamp key so we know when it was cached |
| 871 | + $times['timestamp'] = time(); |
| 872 | + $wgMemc->set( $memcKey, $times, $expiry ); |
646 | 873 | |
647 | | - # But don't give the timestamp to the caller |
648 | | - unset($times['timestamp']); |
| 874 | + # But don't give the timestamp to the caller |
| 875 | + unset($times['timestamp']); |
| 876 | + $this->mLagTimes = $times; |
| 877 | + } |
649 | 878 | wfProfileOut( __METHOD__ ); |
650 | | - return $times; |
| 879 | + return $this->mLagTimes; |
651 | 880 | } |
652 | 881 | } |
653 | 882 | |
Index: trunk/phase3/includes/Database.php |
— | — | @@ -11,305 +11,7 @@ |
12 | 12 | /** Maximum time to wait before retry */ |
13 | 13 | define( 'DEADLOCK_DELAY_MAX', 1500000 ); |
14 | 14 | |
15 | | -/****************************************************************************** |
16 | | - * Utility classes |
17 | | - *****************************************************************************/ |
18 | | - |
19 | 15 | /** |
20 | | - * Utility class. |
21 | | - * @addtogroup Database |
22 | | - */ |
23 | | -class DBObject { |
24 | | - public $mData; |
25 | | - |
26 | | - function DBObject($data) { |
27 | | - $this->mData = $data; |
28 | | - } |
29 | | - |
30 | | - function isLOB() { |
31 | | - return false; |
32 | | - } |
33 | | - |
34 | | - function data() { |
35 | | - return $this->mData; |
36 | | - } |
37 | | -}; |
38 | | - |
39 | | -/** |
40 | | - * Utility class |
41 | | - * @addtogroup Database |
42 | | - * |
43 | | - * This allows us to distinguish a blob from a normal string and an array of strings |
44 | | - */ |
45 | | -class Blob { |
46 | | - private $mData; |
47 | | - function __construct($data) { |
48 | | - $this->mData = $data; |
49 | | - } |
50 | | - function fetch() { |
51 | | - return $this->mData; |
52 | | - } |
53 | | -}; |
54 | | - |
55 | | -/** |
56 | | - * Utility class. |
57 | | - * @addtogroup Database |
58 | | - */ |
59 | | -class MySQLField { |
60 | | - private $name, $tablename, $default, $max_length, $nullable, |
61 | | - $is_pk, $is_unique, $is_key, $type; |
62 | | - function __construct ($info) { |
63 | | - $this->name = $info->name; |
64 | | - $this->tablename = $info->table; |
65 | | - $this->default = $info->def; |
66 | | - $this->max_length = $info->max_length; |
67 | | - $this->nullable = !$info->not_null; |
68 | | - $this->is_pk = $info->primary_key; |
69 | | - $this->is_unique = $info->unique_key; |
70 | | - $this->is_multiple = $info->multiple_key; |
71 | | - $this->is_key = ($this->is_pk || $this->is_unique || $this->is_multiple); |
72 | | - $this->type = $info->type; |
73 | | - } |
74 | | - |
75 | | - function name() { |
76 | | - return $this->name; |
77 | | - } |
78 | | - |
79 | | - function tableName() { |
80 | | - return $this->tableName; |
81 | | - } |
82 | | - |
83 | | - function defaultValue() { |
84 | | - return $this->default; |
85 | | - } |
86 | | - |
87 | | - function maxLength() { |
88 | | - return $this->max_length; |
89 | | - } |
90 | | - |
91 | | - function nullable() { |
92 | | - return $this->nullable; |
93 | | - } |
94 | | - |
95 | | - function isKey() { |
96 | | - return $this->is_key; |
97 | | - } |
98 | | - |
99 | | - function isMultipleKey() { |
100 | | - return $this->is_multiple; |
101 | | - } |
102 | | - |
103 | | - function type() { |
104 | | - return $this->type; |
105 | | - } |
106 | | -} |
107 | | - |
108 | | -/****************************************************************************** |
109 | | - * Error classes |
110 | | - *****************************************************************************/ |
111 | | - |
112 | | -/** |
113 | | - * Database error base class |
114 | | - * @addtogroup Database |
115 | | - */ |
116 | | -class DBError extends MWException { |
117 | | - public $db; |
118 | | - |
119 | | - /** |
120 | | - * Construct a database error |
121 | | - * @param Database $db The database object which threw the error |
122 | | - * @param string $error A simple error message to be used for debugging |
123 | | - */ |
124 | | - function __construct( Database &$db, $error ) { |
125 | | - $this->db =& $db; |
126 | | - parent::__construct( $error ); |
127 | | - } |
128 | | -} |
129 | | - |
130 | | -/** |
131 | | - * @addtogroup Database |
132 | | - */ |
133 | | -class DBConnectionError extends DBError { |
134 | | - public $error; |
135 | | - |
136 | | - function __construct( Database &$db, $error = 'unknown error' ) { |
137 | | - $msg = 'DB connection error'; |
138 | | - if ( trim( $error ) != '' ) { |
139 | | - $msg .= ": $error"; |
140 | | - } |
141 | | - $this->error = $error; |
142 | | - parent::__construct( $db, $msg ); |
143 | | - } |
144 | | - |
145 | | - function useOutputPage() { |
146 | | - // Not likely to work |
147 | | - return false; |
148 | | - } |
149 | | - |
150 | | - function useMessageCache() { |
151 | | - // Not likely to work |
152 | | - return false; |
153 | | - } |
154 | | - |
155 | | - function getText() { |
156 | | - return $this->getMessage() . "\n"; |
157 | | - } |
158 | | - |
159 | | - function getLogMessage() { |
160 | | - # Don't send to the exception log |
161 | | - return false; |
162 | | - } |
163 | | - |
164 | | - function getPageTitle() { |
165 | | - global $wgSitename; |
166 | | - return "$wgSitename has a problem"; |
167 | | - } |
168 | | - |
169 | | - function getHTML() { |
170 | | - global $wgTitle, $wgUseFileCache, $title, $wgInputEncoding; |
171 | | - global $wgSitename, $wgServer, $wgMessageCache; |
172 | | - |
173 | | - # I give up, Brion is right. Getting the message cache to work when there is no DB is tricky. |
174 | | - # Hard coding strings instead. |
175 | | - |
176 | | - $noconnect = "<p><strong>Sorry! This site is experiencing technical difficulties.</strong></p><p>Try waiting a few minutes and reloading.</p><p><small>(Can't contact the database server: $1)</small></p>"; |
177 | | - $mainpage = 'Main Page'; |
178 | | - $searchdisabled = <<<EOT |
179 | | -<p style="margin: 1.5em 2em 1em">$wgSitename search is disabled for performance reasons. You can search via Google in the meantime. |
180 | | -<span style="font-size: 89%; display: block; margin-left: .2em">Note that their indexes of $wgSitename content may be out of date.</span></p>', |
181 | | -EOT; |
182 | | - |
183 | | - $googlesearch = " |
184 | | -<!-- SiteSearch Google --> |
185 | | -<FORM method=GET action=\"http://www.google.com/search\"> |
186 | | -<TABLE bgcolor=\"#FFFFFF\"><tr><td> |
187 | | -<A HREF=\"http://www.google.com/\"> |
188 | | -<IMG SRC=\"http://www.google.com/logos/Logo_40wht.gif\" |
189 | | -border=\"0\" ALT=\"Google\"></A> |
190 | | -</td> |
191 | | -<td> |
192 | | -<INPUT TYPE=text name=q size=31 maxlength=255 value=\"$1\"> |
193 | | -<INPUT type=submit name=btnG VALUE=\"Google Search\"> |
194 | | -<font size=-1> |
195 | | -<input type=hidden name=domains value=\"$wgServer\"><br /><input type=radio name=sitesearch value=\"\"> WWW <input type=radio name=sitesearch value=\"$wgServer\" checked> $wgServer <br /> |
196 | | -<input type='hidden' name='ie' value='$2'> |
197 | | -<input type='hidden' name='oe' value='$2'> |
198 | | -</font> |
199 | | -</td></tr></TABLE> |
200 | | -</FORM> |
201 | | -<!-- SiteSearch Google -->"; |
202 | | - $cachederror = "The following is a cached copy of the requested page, and may not be up to date. "; |
203 | | - |
204 | | - # No database access |
205 | | - if ( is_object( $wgMessageCache ) ) { |
206 | | - $wgMessageCache->disable(); |
207 | | - } |
208 | | - |
209 | | - if ( trim( $this->error ) == '' ) { |
210 | | - $this->error = $this->db->getProperty('mServer'); |
211 | | - } |
212 | | - |
213 | | - $text = str_replace( '$1', $this->error, $noconnect ); |
214 | | - $text .= wfGetSiteNotice(); |
215 | | - |
216 | | - if($wgUseFileCache) { |
217 | | - if($wgTitle) { |
218 | | - $t =& $wgTitle; |
219 | | - } else { |
220 | | - if($title) { |
221 | | - $t = Title::newFromURL( $title ); |
222 | | - } elseif (@/**/$_REQUEST['search']) { |
223 | | - $search = $_REQUEST['search']; |
224 | | - return $searchdisabled . |
225 | | - str_replace( array( '$1', '$2' ), array( htmlspecialchars( $search ), |
226 | | - $wgInputEncoding ), $googlesearch ); |
227 | | - } else { |
228 | | - $t = Title::newFromText( $mainpage ); |
229 | | - } |
230 | | - } |
231 | | - |
232 | | - $cache = new HTMLFileCache( $t ); |
233 | | - if( $cache->isFileCached() ) { |
234 | | - // @todo, FIXME: $msg is not defined on the next line. |
235 | | - $msg = '<p style="color: red"><b>'.$msg."<br />\n" . |
236 | | - $cachederror . "</b></p>\n"; |
237 | | - |
238 | | - $tag = '<div id="article">'; |
239 | | - $text = str_replace( |
240 | | - $tag, |
241 | | - $tag . $msg, |
242 | | - $cache->fetchPageText() ); |
243 | | - } |
244 | | - } |
245 | | - |
246 | | - return $text; |
247 | | - } |
248 | | -} |
249 | | - |
250 | | -/** |
251 | | - * @addtogroup Database |
252 | | - */ |
253 | | -class DBQueryError extends DBError { |
254 | | - public $error, $errno, $sql, $fname; |
255 | | - |
256 | | - function __construct( Database &$db, $error, $errno, $sql, $fname ) { |
257 | | - $message = "A database error has occurred\n" . |
258 | | - "Query: $sql\n" . |
259 | | - "Function: $fname\n" . |
260 | | - "Error: $errno $error\n"; |
261 | | - |
262 | | - parent::__construct( $db, $message ); |
263 | | - $this->error = $error; |
264 | | - $this->errno = $errno; |
265 | | - $this->sql = $sql; |
266 | | - $this->fname = $fname; |
267 | | - } |
268 | | - |
269 | | - function getText() { |
270 | | - if ( $this->useMessageCache() ) { |
271 | | - return wfMsg( 'dberrortextcl', htmlspecialchars( $this->getSQL() ), |
272 | | - htmlspecialchars( $this->fname ), $this->errno, htmlspecialchars( $this->error ) ) . "\n"; |
273 | | - } else { |
274 | | - return $this->getMessage(); |
275 | | - } |
276 | | - } |
277 | | - |
278 | | - function getSQL() { |
279 | | - global $wgShowSQLErrors; |
280 | | - if( !$wgShowSQLErrors ) { |
281 | | - return $this->msg( 'sqlhidden', 'SQL hidden' ); |
282 | | - } else { |
283 | | - return $this->sql; |
284 | | - } |
285 | | - } |
286 | | - |
287 | | - function getLogMessage() { |
288 | | - # Don't send to the exception log |
289 | | - return false; |
290 | | - } |
291 | | - |
292 | | - function getPageTitle() { |
293 | | - return $this->msg( 'databaseerror', 'Database error' ); |
294 | | - } |
295 | | - |
296 | | - function getHTML() { |
297 | | - if ( $this->useMessageCache() ) { |
298 | | - return wfMsgNoDB( 'dberrortext', htmlspecialchars( $this->getSQL() ), |
299 | | - htmlspecialchars( $this->fname ), $this->errno, htmlspecialchars( $this->error ) ); |
300 | | - } else { |
301 | | - return nl2br( htmlspecialchars( $this->getMessage() ) ); |
302 | | - } |
303 | | - } |
304 | | -} |
305 | | - |
306 | | -/** |
307 | | - * @addtogroup Database |
308 | | - */ |
309 | | -class DBUnexpectedError extends DBError {} |
310 | | - |
311 | | -/******************************************************************************/ |
312 | | - |
313 | | -/** |
314 | 16 | * Database abstraction object |
315 | 17 | * @addtogroup Database |
316 | 18 | */ |
— | — | @@ -330,6 +32,7 @@ |
331 | 33 | protected $mTrxLevel = 0; |
332 | 34 | protected $mErrorCount = 0; |
333 | 35 | protected $mLBInfo = array(); |
| 36 | + protected $mFakeSlaveLag = null, $mFakeMaster = false; |
334 | 37 | |
335 | 38 | #------------------------------------------------------------------------------ |
336 | 39 | # Accessors |
— | — | @@ -397,6 +100,10 @@ |
398 | 101 | return wfSetVar( $this->mErrorCount, $count ); |
399 | 102 | } |
400 | 103 | |
| 104 | + function tablePrefix( $prefix = null ) { |
| 105 | + return wfSetVar( $this->mTablePrefix, $prefix ); |
| 106 | + } |
| 107 | + |
401 | 108 | /** |
402 | 109 | * Properties passed down from the server info array of the load balancer |
403 | 110 | */ |
— | — | @@ -421,6 +128,20 @@ |
422 | 129 | } |
423 | 130 | |
424 | 131 | /** |
| 132 | + * Set lag time in seconds for a fake slave |
| 133 | + */ |
| 134 | + function setFakeSlaveLag( $lag ) { |
| 135 | + $this->mFakeSlaveLag = $lag; |
| 136 | + } |
| 137 | + |
| 138 | + /** |
| 139 | + * Make this connection a fake master |
| 140 | + */ |
| 141 | + function setFakeMaster( $enabled = true ) { |
| 142 | + $this->mFakeMaster = $enabled; |
| 143 | + } |
| 144 | + |
| 145 | + /** |
425 | 146 | * Returns true if this database supports (and uses) cascading deletes |
426 | 147 | */ |
427 | 148 | function cascadingDeletes() { |
— | — | @@ -577,6 +298,8 @@ |
578 | 299 | global $wguname; |
579 | 300 | wfProfileIn( __METHOD__ ); |
580 | 301 | |
| 302 | + $server = 'localhost'; debugging_code_left_in(); |
| 303 | + |
581 | 304 | # Test for missing mysql.so |
582 | 305 | # First try to load it |
583 | 306 | if (!@extension_loaded('mysql')) { |
— | — | @@ -598,8 +321,10 @@ |
599 | 322 | $success = false; |
600 | 323 | |
601 | 324 | wfProfileIn("dbconnect-$server"); |
602 | | - |
603 | | - # LIVE PATCH by Tim, ask Domas for why: retry loop |
| 325 | + |
| 326 | + # Try to connect up to three times |
| 327 | + # The kernel's default SYN retransmission period is far too slow for us, |
| 328 | + # so we use a short timeout plus a manual retry. |
604 | 329 | $this->mConn = false; |
605 | 330 | $max = 3; |
606 | 331 | for ( $i = 0; $i < $max && !$this->mConn; $i++ ) { |
— | — | @@ -720,6 +445,7 @@ |
721 | 446 | public function query( $sql, $fname = '', $tempIgnore = false ) { |
722 | 447 | global $wgProfiling; |
723 | 448 | |
| 449 | + $isMaster = !is_null( $this->getLBInfo( 'master' ) ); |
724 | 450 | if ( $wgProfiling ) { |
725 | 451 | # generalizeSQL will probably cut down the query to reasonable |
726 | 452 | # logging size most of the time. The substr is really just a sanity check. |
— | — | @@ -727,12 +453,12 @@ |
728 | 454 | # Who's been wasting my precious column space? -- TS |
729 | 455 | #$profName = 'query: ' . $fname . ' ' . substr( Database::generalizeSQL( $sql ), 0, 255 ); |
730 | 456 | |
731 | | - if ( is_null( $this->getLBInfo( 'master' ) ) ) { |
| 457 | + if ( $isMaster ) { |
| 458 | + $queryProf = 'query-m: ' . substr( Database::generalizeSQL( $sql ), 0, 255 ); |
| 459 | + $totalProf = 'Database::query-master'; |
| 460 | + } else { |
732 | 461 | $queryProf = 'query: ' . substr( Database::generalizeSQL( $sql ), 0, 255 ); |
733 | 462 | $totalProf = 'Database::query'; |
734 | | - } else { |
735 | | - $queryProf = 'query-m: ' . substr( Database::generalizeSQL( $sql ), 0, 255 ); |
736 | | - $totalProf = 'Database::query-master'; |
737 | 463 | } |
738 | 464 | wfProfileIn( $totalProf ); |
739 | 465 | wfProfileIn( $queryProf ); |
— | — | @@ -771,7 +497,11 @@ |
772 | 498 | if ( $this->debug() ) { |
773 | 499 | $sqlx = substr( $commentedSql, 0, 500 ); |
774 | 500 | $sqlx = strtr( $sqlx, "\t\n", ' ' ); |
775 | | - wfDebug( "SQL: $sqlx\n" ); |
| 501 | + if ( $isMaster ) { |
| 502 | + wfDebug( "SQL-master: $sqlx\n" ); |
| 503 | + } else { |
| 504 | + wfDebug( "SQL: $sqlx\n" ); |
| 505 | + } |
776 | 506 | } |
777 | 507 | |
778 | 508 | # Do the query and handle errors |
— | — | @@ -1605,6 +1335,20 @@ |
1606 | 1336 | } |
1607 | 1337 | |
1608 | 1338 | /** |
| 1339 | + * Get the current DB name |
| 1340 | + */ |
| 1341 | + function getDBname() { |
| 1342 | + return $this->mDBname; |
| 1343 | + } |
| 1344 | + |
| 1345 | + /** |
| 1346 | + * Get the server hostname or IP address |
| 1347 | + */ |
| 1348 | + function getServer() { |
| 1349 | + return $this->mServer; |
| 1350 | + } |
| 1351 | + |
| 1352 | + /** |
1609 | 1353 | * Format a table name ready for use in constructing an SQL query |
1610 | 1354 | * |
1611 | 1355 | * This does two important things: it quotes table names which as necessary, |
— | — | @@ -1976,17 +1720,37 @@ |
1977 | 1721 | * @param string $pos the binlog position |
1978 | 1722 | * @param integer $timeout the maximum number of seconds to wait for synchronisation |
1979 | 1723 | */ |
1980 | | - function masterPosWait( $file, $pos, $timeout ) { |
| 1724 | + function masterPosWait( MySQLMasterPos $pos, $timeout ) { |
1981 | 1725 | $fname = 'Database::masterPosWait'; |
1982 | 1726 | wfProfileIn( $fname ); |
1983 | 1727 | |
1984 | | - |
1985 | 1728 | # Commit any open transactions |
1986 | | - $this->immediateCommit(); |
| 1729 | + if ( $this->mTrxLevel ) { |
| 1730 | + $this->immediateCommit(); |
| 1731 | + } |
1987 | 1732 | |
| 1733 | + if ( !is_null( $this->mFakeSlaveLag ) ) { |
| 1734 | + $wait = intval( ( $pos->pos - microtime(true) + $this->mFakeSlaveLag ) * 1e6 ); |
| 1735 | + if ( $wait > $timeout * 1e6 ) { |
| 1736 | + wfDebug( "Fake slave timed out waiting for $pos ($wait us)\n" ); |
| 1737 | + wfProfileOut( $fname ); |
| 1738 | + return -1; |
| 1739 | + } elseif ( $wait > 0 ) { |
| 1740 | + wfDebug( "Fake slave waiting $wait us\n" ); |
| 1741 | + usleep( $wait ); |
| 1742 | + wfProfileOut( $fname ); |
| 1743 | + return 1; |
| 1744 | + } else { |
| 1745 | + wfDebug( "Fake slave up to date ($wait us)\n" ); |
| 1746 | + wfProfileOut( $fname ); |
| 1747 | + return 0; |
| 1748 | + } |
| 1749 | + } |
| 1750 | + |
1988 | 1751 | # Call doQuery() directly, to avoid opening a transaction if DBO_TRX is set |
1989 | | - $encFile = $this->strencode( $file ); |
1990 | | - $sql = "SELECT MASTER_POS_WAIT('$encFile', $pos, $timeout)"; |
| 1752 | + $encFile = $this->addQuotes( $pos->file ); |
| 1753 | + $encPos = intval( $pos->pos ); |
| 1754 | + $sql = "SELECT MASTER_POS_WAIT($encFile, $encPos, $timeout)"; |
1991 | 1755 | $res = $this->doQuery( $sql ); |
1992 | 1756 | if ( $res && $row = $this->fetchRow( $res ) ) { |
1993 | 1757 | $this->freeResult( $res ); |
— | — | @@ -2002,12 +1766,17 @@ |
2003 | 1767 | * Get the position of the master from SHOW SLAVE STATUS |
2004 | 1768 | */ |
2005 | 1769 | function getSlavePos() { |
| 1770 | + if ( !is_null( $this->mFakeSlaveLag ) ) { |
| 1771 | + $pos = new MySQLMasterPos( 'fake', microtime(true) - $this->mFakeSlaveLag ); |
| 1772 | + wfDebug( __METHOD__.": fake slave pos = $pos\n" ); |
| 1773 | + return $pos; |
| 1774 | + } |
2006 | 1775 | $res = $this->query( 'SHOW SLAVE STATUS', 'Database::getSlavePos' ); |
2007 | 1776 | $row = $this->fetchObject( $res ); |
2008 | 1777 | if ( $row ) { |
2009 | | - return array( $row->Master_Log_File, $row->Read_Master_Log_Pos ); |
| 1778 | + return new MySQLMasterPos( $row->Master_Log_File, $row->Read_Master_Log_Pos ); |
2010 | 1779 | } else { |
2011 | | - return array( false, false ); |
| 1780 | + return false; |
2012 | 1781 | } |
2013 | 1782 | } |
2014 | 1783 | |
— | — | @@ -2015,12 +1784,15 @@ |
2016 | 1785 | * Get the position of the master from SHOW MASTER STATUS |
2017 | 1786 | */ |
2018 | 1787 | function getMasterPos() { |
| 1788 | + if ( $this->mFakeMaster ) { |
| 1789 | + return new MySQLMasterPos( 'fake', microtime( true ) ); |
| 1790 | + } |
2019 | 1791 | $res = $this->query( 'SHOW MASTER STATUS', 'Database::getMasterPos' ); |
2020 | 1792 | $row = $this->fetchObject( $res ); |
2021 | 1793 | if ( $row ) { |
2022 | | - return array( $row->File, $row->Position ); |
| 1794 | + return new MySQLMasterPos( $row->File, $row->Position ); |
2023 | 1795 | } else { |
2024 | | - return array( false, false ); |
| 1796 | + return false; |
2025 | 1797 | } |
2026 | 1798 | } |
2027 | 1799 | |
— | — | @@ -2149,6 +1921,10 @@ |
2150 | 1922 | * At the moment, this will only work if the DB user has the PROCESS privilege |
2151 | 1923 | */ |
2152 | 1924 | function getLag() { |
| 1925 | + if ( !is_null( $this->mFakeSlaveLag ) ) { |
| 1926 | + wfDebug( "getLag: fake slave lagged {$this->mFakeSlaveLag} seconds\n" ); |
| 1927 | + return $this->mFakeSlaveLag; |
| 1928 | + } |
2153 | 1929 | $res = $this->query( 'SHOW PROCESSLIST' ); |
2154 | 1930 | # Find slave SQL thread |
2155 | 1931 | while ( $row = $this->fetchObject( $res ) ) { |
— | — | @@ -2349,8 +2125,304 @@ |
2350 | 2126 | # Inherit all |
2351 | 2127 | } |
2352 | 2128 | |
| 2129 | +/****************************************************************************** |
| 2130 | + * Utility classes |
| 2131 | + *****************************************************************************/ |
2353 | 2132 | |
2354 | 2133 | /** |
| 2134 | + * Utility class. |
| 2135 | + * @addtogroup Database |
| 2136 | + */ |
| 2137 | +class DBObject { |
| 2138 | + public $mData; |
| 2139 | + |
| 2140 | + function DBObject($data) { |
| 2141 | + $this->mData = $data; |
| 2142 | + } |
| 2143 | + |
| 2144 | + function isLOB() { |
| 2145 | + return false; |
| 2146 | + } |
| 2147 | + |
| 2148 | + function data() { |
| 2149 | + return $this->mData; |
| 2150 | + } |
| 2151 | +} |
| 2152 | + |
| 2153 | +/** |
| 2154 | + * Utility class |
| 2155 | + * @addtogroup Database |
| 2156 | + * |
| 2157 | + * This allows us to distinguish a blob from a normal string and an array of strings |
| 2158 | + */ |
| 2159 | +class Blob { |
| 2160 | + private $mData; |
| 2161 | + function __construct($data) { |
| 2162 | + $this->mData = $data; |
| 2163 | + } |
| 2164 | + function fetch() { |
| 2165 | + return $this->mData; |
| 2166 | + } |
| 2167 | +} |
| 2168 | + |
| 2169 | +/** |
| 2170 | + * Utility class. |
| 2171 | + * @addtogroup Database |
| 2172 | + */ |
| 2173 | +class MySQLField { |
| 2174 | + private $name, $tablename, $default, $max_length, $nullable, |
| 2175 | + $is_pk, $is_unique, $is_key, $type; |
| 2176 | + function __construct ($info) { |
| 2177 | + $this->name = $info->name; |
| 2178 | + $this->tablename = $info->table; |
| 2179 | + $this->default = $info->def; |
| 2180 | + $this->max_length = $info->max_length; |
| 2181 | + $this->nullable = !$info->not_null; |
| 2182 | + $this->is_pk = $info->primary_key; |
| 2183 | + $this->is_unique = $info->unique_key; |
| 2184 | + $this->is_multiple = $info->multiple_key; |
| 2185 | + $this->is_key = ($this->is_pk || $this->is_unique || $this->is_multiple); |
| 2186 | + $this->type = $info->type; |
| 2187 | + } |
| 2188 | + |
| 2189 | + function name() { |
| 2190 | + return $this->name; |
| 2191 | + } |
| 2192 | + |
| 2193 | + function tableName() { |
| 2194 | + return $this->tableName; |
| 2195 | + } |
| 2196 | + |
| 2197 | + function defaultValue() { |
| 2198 | + return $this->default; |
| 2199 | + } |
| 2200 | + |
| 2201 | + function maxLength() { |
| 2202 | + return $this->max_length; |
| 2203 | + } |
| 2204 | + |
| 2205 | + function nullable() { |
| 2206 | + return $this->nullable; |
| 2207 | + } |
| 2208 | + |
| 2209 | + function isKey() { |
| 2210 | + return $this->is_key; |
| 2211 | + } |
| 2212 | + |
| 2213 | + function isMultipleKey() { |
| 2214 | + return $this->is_multiple; |
| 2215 | + } |
| 2216 | + |
| 2217 | + function type() { |
| 2218 | + return $this->type; |
| 2219 | + } |
| 2220 | +} |
| 2221 | + |
| 2222 | +/****************************************************************************** |
| 2223 | + * Error classes |
| 2224 | + *****************************************************************************/ |
| 2225 | + |
| 2226 | +/** |
| 2227 | + * Database error base class |
| 2228 | + * @addtogroup Database |
| 2229 | + */ |
| 2230 | +class DBError extends MWException { |
| 2231 | + public $db; |
| 2232 | + |
| 2233 | + /** |
| 2234 | + * Construct a database error |
| 2235 | + * @param Database $db The database object which threw the error |
| 2236 | + * @param string $error A simple error message to be used for debugging |
| 2237 | + */ |
| 2238 | + function __construct( Database &$db, $error ) { |
| 2239 | + $this->db =& $db; |
| 2240 | + parent::__construct( $error ); |
| 2241 | + } |
| 2242 | +} |
| 2243 | + |
| 2244 | +/** |
| 2245 | + * @addtogroup Database |
| 2246 | + */ |
| 2247 | +class DBConnectionError extends DBError { |
| 2248 | + public $error; |
| 2249 | + |
| 2250 | + function __construct( Database &$db, $error = 'unknown error' ) { |
| 2251 | + $msg = 'DB connection error'; |
| 2252 | + if ( trim( $error ) != '' ) { |
| 2253 | + $msg .= ": $error"; |
| 2254 | + } |
| 2255 | + $this->error = $error; |
| 2256 | + parent::__construct( $db, $msg ); |
| 2257 | + } |
| 2258 | + |
| 2259 | + function useOutputPage() { |
| 2260 | + // Not likely to work |
| 2261 | + return false; |
| 2262 | + } |
| 2263 | + |
| 2264 | + function useMessageCache() { |
| 2265 | + // Not likely to work |
| 2266 | + return false; |
| 2267 | + } |
| 2268 | + |
| 2269 | + function getText() { |
| 2270 | + return $this->getMessage() . "\n"; |
| 2271 | + } |
| 2272 | + |
| 2273 | + function getLogMessage() { |
| 2274 | + # Don't send to the exception log |
| 2275 | + return false; |
| 2276 | + } |
| 2277 | + |
| 2278 | + function getPageTitle() { |
| 2279 | + global $wgSitename; |
| 2280 | + return "$wgSitename has a problem"; |
| 2281 | + } |
| 2282 | + |
| 2283 | + function getHTML() { |
| 2284 | + global $wgTitle, $wgUseFileCache, $title, $wgInputEncoding; |
| 2285 | + global $wgSitename, $wgServer, $wgMessageCache; |
| 2286 | + |
| 2287 | + # I give up, Brion is right. Getting the message cache to work when there is no DB is tricky. |
| 2288 | + # Hard coding strings instead. |
| 2289 | + |
| 2290 | + $noconnect = "<p><strong>Sorry! This site is experiencing technical difficulties.</strong></p><p>Try waiting a few minutes and reloading.</p><p><small>(Can't contact the database server: $1)</small></p>"; |
| 2291 | + $mainpage = 'Main Page'; |
| 2292 | + $searchdisabled = <<<EOT |
| 2293 | +<p style="margin: 1.5em 2em 1em">$wgSitename search is disabled for performance reasons. You can search via Google in the meantime. |
| 2294 | +<span style="font-size: 89%; display: block; margin-left: .2em">Note that their indexes of $wgSitename content may be out of date.</span></p>', |
| 2295 | +EOT; |
| 2296 | + |
| 2297 | + $googlesearch = " |
| 2298 | +<!-- SiteSearch Google --> |
| 2299 | +<FORM method=GET action=\"http://www.google.com/search\"> |
| 2300 | +<TABLE bgcolor=\"#FFFFFF\"><tr><td> |
| 2301 | +<A HREF=\"http://www.google.com/\"> |
| 2302 | +<IMG SRC=\"http://www.google.com/logos/Logo_40wht.gif\" |
| 2303 | +border=\"0\" ALT=\"Google\"></A> |
| 2304 | +</td> |
| 2305 | +<td> |
| 2306 | +<INPUT TYPE=text name=q size=31 maxlength=255 value=\"$1\"> |
| 2307 | +<INPUT type=submit name=btnG VALUE=\"Google Search\"> |
| 2308 | +<font size=-1> |
| 2309 | +<input type=hidden name=domains value=\"$wgServer\"><br /><input type=radio name=sitesearch value=\"\"> WWW <input type=radio name=sitesearch value=\"$wgServer\" checked> $wgServer <br /> |
| 2310 | +<input type='hidden' name='ie' value='$2'> |
| 2311 | +<input type='hidden' name='oe' value='$2'> |
| 2312 | +</font> |
| 2313 | +</td></tr></TABLE> |
| 2314 | +</FORM> |
| 2315 | +<!-- SiteSearch Google -->"; |
| 2316 | + $cachederror = "The following is a cached copy of the requested page, and may not be up to date. "; |
| 2317 | + |
| 2318 | + # No database access |
| 2319 | + if ( is_object( $wgMessageCache ) ) { |
| 2320 | + $wgMessageCache->disable(); |
| 2321 | + } |
| 2322 | + |
| 2323 | + if ( trim( $this->error ) == '' ) { |
| 2324 | + $this->error = $this->db->getProperty('mServer'); |
| 2325 | + } |
| 2326 | + |
| 2327 | + $text = str_replace( '$1', $this->error, $noconnect ); |
| 2328 | + $text .= wfGetSiteNotice(); |
| 2329 | + |
| 2330 | + if($wgUseFileCache) { |
| 2331 | + if($wgTitle) { |
| 2332 | + $t =& $wgTitle; |
| 2333 | + } else { |
| 2334 | + if($title) { |
| 2335 | + $t = Title::newFromURL( $title ); |
| 2336 | + } elseif (@/**/$_REQUEST['search']) { |
| 2337 | + $search = $_REQUEST['search']; |
| 2338 | + return $searchdisabled . |
| 2339 | + str_replace( array( '$1', '$2' ), array( htmlspecialchars( $search ), |
| 2340 | + $wgInputEncoding ), $googlesearch ); |
| 2341 | + } else { |
| 2342 | + $t = Title::newFromText( $mainpage ); |
| 2343 | + } |
| 2344 | + } |
| 2345 | + |
| 2346 | + $cache = new HTMLFileCache( $t ); |
| 2347 | + if( $cache->isFileCached() ) { |
| 2348 | + // @todo, FIXME: $msg is not defined on the next line. |
| 2349 | + $msg = '<p style="color: red"><b>'.$msg."<br />\n" . |
| 2350 | + $cachederror . "</b></p>\n"; |
| 2351 | + |
| 2352 | + $tag = '<div id="article">'; |
| 2353 | + $text = str_replace( |
| 2354 | + $tag, |
| 2355 | + $tag . $msg, |
| 2356 | + $cache->fetchPageText() ); |
| 2357 | + } |
| 2358 | + } |
| 2359 | + |
| 2360 | + return $text; |
| 2361 | + } |
| 2362 | +} |
| 2363 | + |
| 2364 | +/** |
| 2365 | + * @addtogroup Database |
| 2366 | + */ |
| 2367 | +class DBQueryError extends DBError { |
| 2368 | + public $error, $errno, $sql, $fname; |
| 2369 | + |
| 2370 | + function __construct( Database &$db, $error, $errno, $sql, $fname ) { |
| 2371 | + $message = "A database error has occurred\n" . |
| 2372 | + "Query: $sql\n" . |
| 2373 | + "Function: $fname\n" . |
| 2374 | + "Error: $errno $error\n"; |
| 2375 | + |
| 2376 | + parent::__construct( $db, $message ); |
| 2377 | + $this->error = $error; |
| 2378 | + $this->errno = $errno; |
| 2379 | + $this->sql = $sql; |
| 2380 | + $this->fname = $fname; |
| 2381 | + } |
| 2382 | + |
| 2383 | + function getText() { |
| 2384 | + if ( $this->useMessageCache() ) { |
| 2385 | + return wfMsg( 'dberrortextcl', htmlspecialchars( $this->getSQL() ), |
| 2386 | + htmlspecialchars( $this->fname ), $this->errno, htmlspecialchars( $this->error ) ) . "\n"; |
| 2387 | + } else { |
| 2388 | + return $this->getMessage(); |
| 2389 | + } |
| 2390 | + } |
| 2391 | + |
| 2392 | + function getSQL() { |
| 2393 | + global $wgShowSQLErrors; |
| 2394 | + if( !$wgShowSQLErrors ) { |
| 2395 | + return $this->msg( 'sqlhidden', 'SQL hidden' ); |
| 2396 | + } else { |
| 2397 | + return $this->sql; |
| 2398 | + } |
| 2399 | + } |
| 2400 | + |
| 2401 | + function getLogMessage() { |
| 2402 | + # Don't send to the exception log |
| 2403 | + return false; |
| 2404 | + } |
| 2405 | + |
| 2406 | + function getPageTitle() { |
| 2407 | + return $this->msg( 'databaseerror', 'Database error' ); |
| 2408 | + } |
| 2409 | + |
| 2410 | + function getHTML() { |
| 2411 | + if ( $this->useMessageCache() ) { |
| 2412 | + return wfMsgNoDB( 'dberrortext', htmlspecialchars( $this->getSQL() ), |
| 2413 | + htmlspecialchars( $this->fname ), $this->errno, htmlspecialchars( $this->error ) ); |
| 2414 | + } else { |
| 2415 | + return nl2br( htmlspecialchars( $this->getMessage() ) ); |
| 2416 | + } |
| 2417 | + } |
| 2418 | +} |
| 2419 | + |
| 2420 | +/** |
| 2421 | + * @addtogroup Database |
| 2422 | + */ |
| 2423 | +class DBUnexpectedError extends DBError {} |
| 2424 | + |
| 2425 | + |
| 2426 | +/** |
2355 | 2427 | * Result wrapper for grabbing data queried by someone else |
2356 | 2428 | * @addtogroup Database |
2357 | 2429 | */ |
— | — | @@ -2454,4 +2526,15 @@ |
2455 | 2527 | } |
2456 | 2528 | } |
2457 | 2529 | |
| 2530 | +class MySQLMasterPos { |
| 2531 | + var $file, $pos; |
2458 | 2532 | |
| 2533 | + function __construct( $file, $pos ) { |
| 2534 | + $this->file = $file; |
| 2535 | + $this->pos = $pos; |
| 2536 | + } |
| 2537 | + |
| 2538 | + function __toString() { |
| 2539 | + return "{$this->file}/{$this->pos}"; |
| 2540 | + } |
| 2541 | +} |
Index: trunk/phase3/includes/LBFactory.php |
— | — | @@ -0,0 +1,211 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * An interface for generating database load balancers |
| 6 | + */ |
| 7 | +abstract class LBFactory { |
| 8 | + static $instance; |
| 9 | + |
| 10 | + /** |
| 11 | + * Get an LBFactory instance |
| 12 | + */ |
| 13 | + static function &singleton() { |
| 14 | + if ( is_null( self::$instance ) ) { |
| 15 | + global $wgLBFactoryConf; |
| 16 | + $class = $wgLBFactoryConf['class']; |
| 17 | + self::$instance = new $class( $wgLBFactoryConf ); |
| 18 | + } |
| 19 | + return self::$instance; |
| 20 | + } |
| 21 | + |
| 22 | + /** |
| 23 | + * Construct a factory based on a configuration array (typically from $wgLBFactoryConf) |
| 24 | + */ |
| 25 | + abstract function __construct( $conf ); |
| 26 | + |
| 27 | + /** |
| 28 | + * Get a load balancer object. |
| 29 | + * |
| 30 | + * @param string $wiki Wiki ID, or false for the current wiki |
| 31 | + * @return LoadBalancer |
| 32 | + */ |
| 33 | + abstract function getMainLB( $wiki = false ); |
| 34 | + |
| 35 | + /* |
| 36 | + * Get a load balancer for external storage |
| 37 | + * |
| 38 | + * @param string $cluster External storage cluster, or false for core |
| 39 | + * @param string $wiki Wiki ID, or false for the current wiki |
| 40 | + */ |
| 41 | + abstract function getExternalLB( $cluster, $wiki = false ); |
| 42 | + |
| 43 | + /** |
| 44 | + * Execute a function for each tracked load balancer |
| 45 | + * The callback is called with the load balancer as the first parameter, |
| 46 | + * and $params passed as the subsequent parameters. |
| 47 | + */ |
| 48 | + abstract function forEachLB( $callback, $params = array() ); |
| 49 | + |
| 50 | + /** |
| 51 | + * Prepare all load balancers for shutdown |
| 52 | + * STUB |
| 53 | + */ |
| 54 | + function shutdown() {} |
| 55 | + |
| 56 | + /** |
| 57 | + * Call a method of each load balancer |
| 58 | + */ |
| 59 | + function forEachLBCallMethod( $methodName, $args = array() ) { |
| 60 | + $this->forEachLB( array( $this, 'callMethod' ), array( $methodName, $args ) ); |
| 61 | + } |
| 62 | + |
| 63 | + /** |
| 64 | + * Private helper for forEachLBCallMethod |
| 65 | + */ |
| 66 | + function callMethod( $loadBalancer, $methodName, $args ) { |
| 67 | + call_user_func_array( array( $loadBalancer, $methodName ), $args ); |
| 68 | + } |
| 69 | + |
| 70 | + /** |
| 71 | + * Commit changes on all master connections |
| 72 | + */ |
| 73 | + function commitMasterChanges() { |
| 74 | + $this->forEachLBCallMethod( 'commitMasterChanges' ); |
| 75 | + } |
| 76 | +} |
| 77 | + |
| 78 | +/** |
| 79 | + * A simple single-master LBFactory that gets its configuration from the b/c globals |
| 80 | + */ |
| 81 | +class LBFactory_Simple extends LBFactory { |
| 82 | + var $mainLB; |
| 83 | + var $extLBs = array(); |
| 84 | + |
| 85 | + # Chronology protector |
| 86 | + var $chronProt; |
| 87 | + |
| 88 | + function __construct( $conf ) { |
| 89 | + $this->chronProt = new ChronologyProtector; |
| 90 | + } |
| 91 | + |
| 92 | + function getMainLB( $wiki = false ) { |
| 93 | + if ( !isset( $this->mainLB ) ) { |
| 94 | + global $wgDBservers, $wgMasterWaitTimeout; |
| 95 | + if ( !$wgDBservers ) { |
| 96 | + global $wgDBserver, $wgDBuser, $wgDBpassword, $wgDBname, $wgDBtype, $wgDebugDumpSql; |
| 97 | + $wgDBservers = array(array( |
| 98 | + 'host' => $wgDBserver, |
| 99 | + 'user' => $wgDBuser, |
| 100 | + 'password' => $wgDBpassword, |
| 101 | + 'dbname' => $wgDBname, |
| 102 | + 'type' => $wgDBtype, |
| 103 | + 'load' => 1, |
| 104 | + 'flags' => ($wgDebugDumpSql ? DBO_DEBUG : 0) | DBO_DEFAULT |
| 105 | + )); |
| 106 | + } |
| 107 | + |
| 108 | + $this->mainLB = new LoadBalancer( $wgDBservers, false, $wgMasterWaitTimeout, true ); |
| 109 | + $this->mainLB->parentInfo( array( 'id' => 'main' ) ); |
| 110 | + $this->chronProt->initLB( $this->mainLB ); |
| 111 | + } |
| 112 | + return $this->mainLB; |
| 113 | + } |
| 114 | + |
| 115 | + function getExternalLB( $cluster, $wiki = false ) { |
| 116 | + global $wgExternalServers; |
| 117 | + if ( !isset( $this->extLBs[$cluster] ) ) { |
| 118 | + if ( !isset( $wgExternalServers[$cluster] ) ) { |
| 119 | + throw new MWException( __METHOD__.": Unknown cluster \"$cluster\"" ); |
| 120 | + } |
| 121 | + $this->extLBs[$cluster] = new LoadBalancer( $wgExternalServers[$cluster] ); |
| 122 | + $this->extLBs[$cluster]->parentInfo( array( 'id' => "ext-$cluster" ) ); |
| 123 | + } |
| 124 | + return $this->extLBs[$cluster]; |
| 125 | + } |
| 126 | + |
| 127 | + /** |
| 128 | + * Execute a function for each tracked load balancer |
| 129 | + * The callback is called with the load balancer as the first parameter, |
| 130 | + * and $params passed as the subsequent parameters. |
| 131 | + */ |
| 132 | + function forEachLB( $callback, $params = array() ) { |
| 133 | + if ( isset( $this->mainLB ) ) { |
| 134 | + call_user_func_array( $callback, array_merge( array( $this->mainLB ), $params ) ); |
| 135 | + } |
| 136 | + foreach ( $this->extLBs as $lb ) { |
| 137 | + call_user_func_array( $callback, array_merge( array( $lb ), $params ) ); |
| 138 | + } |
| 139 | + } |
| 140 | + |
| 141 | + function shutdown() { |
| 142 | + if ( $this->mainLB ) { |
| 143 | + $this->chronProt->shutdownLB( $this->mainLB ); |
| 144 | + } |
| 145 | + $this->chronProt->shutdown(); |
| 146 | + $this->commitMasterChanges(); |
| 147 | + } |
| 148 | +} |
| 149 | + |
| 150 | +/** |
| 151 | + * Class for ensuring a consistent ordering of events as seen by the user, despite replication. |
| 152 | + * Kind of like Hawking's [[Chronology Protection Agency]]. |
| 153 | + */ |
| 154 | +class ChronologyProtector { |
| 155 | + var $startupPos; |
| 156 | + var $shutdownPos = array(); |
| 157 | + |
| 158 | + /** |
| 159 | + * Initialise a LoadBalancer to give it appropriate chronology protection. |
| 160 | + * |
| 161 | + * @param LoadBalancer $lb |
| 162 | + */ |
| 163 | + function initLB( $lb ) { |
| 164 | + if ( $this->startupPos === null ) { |
| 165 | + if ( !empty( $_SESSION[__CLASS__] ) ) { |
| 166 | + $this->startupPos = $_SESSION[__CLASS__]; |
| 167 | + } |
| 168 | + } |
| 169 | + if ( !$this->startupPos ) { |
| 170 | + return; |
| 171 | + } |
| 172 | + $masterName = $lb->getServerName( 0 ); |
| 173 | + |
| 174 | + if ( $lb->getServerCount() > 1 && !empty( $this->startupPos[$masterName] ) ) { |
| 175 | + $info = $lb->parentInfo(); |
| 176 | + $pos = $this->startupPos[$masterName]; |
| 177 | + wfDebug( __METHOD__.": LB " . $info['id'] . " waiting for master pos $pos\n" ); |
| 178 | + $lb->waitFor( $this->startupPos[$masterName] ); |
| 179 | + } |
| 180 | + } |
| 181 | + |
| 182 | + /** |
| 183 | + * Notify the ChronologyProtector that the LoadBalancer is about to shut |
| 184 | + * down. Saves replication positions. |
| 185 | + * |
| 186 | + * @param LoadBalancer $lb |
| 187 | + */ |
| 188 | + function shutdownLB( $lb ) { |
| 189 | + if ( session_id() != '' && $lb->getServerCount() > 1 ) { |
| 190 | + $masterName = $lb->getServerName( 0 ); |
| 191 | + if ( !isset( $this->shutdownPos[$masterName] ) ) { |
| 192 | + $pos = $lb->getMasterPos(); |
| 193 | + $info = $lb->parentInfo(); |
| 194 | + wfDebug( __METHOD__.": LB " . $info['id'] . " has master pos $pos\n" ); |
| 195 | + $this->shutdownPos[$masterName] = $pos; |
| 196 | + } |
| 197 | + } |
| 198 | + } |
| 199 | + |
| 200 | + /** |
| 201 | + * Notify the ChronologyProtector that the LBFactory is done calling shutdownLB() for now. |
| 202 | + * May commit chronology data to persistent storage. |
| 203 | + */ |
| 204 | + function shutdown() { |
| 205 | + if ( session_id() != '' && count( $this->shutdownPos ) ) { |
| 206 | + wfDebug( __METHOD__.": saving master pos for " . |
| 207 | + count( $this->shutdownPos ) . " master(s)\n" ); |
| 208 | + $_SESSION[__CLASS__] = $this->shutdownPos; |
| 209 | + } |
| 210 | + } |
| 211 | +} |
| 212 | + |
Property changes on: trunk/phase3/includes/LBFactory.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 213 | + native |
Index: trunk/phase3/index.php |
— | — | @@ -47,7 +47,7 @@ |
48 | 48 | |
49 | 49 | $maxLag = $wgRequest->getVal( 'maxlag' ); |
50 | 50 | if ( !is_null( $maxLag ) ) { |
51 | | - if ( !$mediaWiki->checkMaxLag( $wgLoadBalancer, $maxLag ) ) { |
| 51 | + if ( !$mediaWiki->checkMaxLag( $maxLag ) ) { |
52 | 52 | exit; |
53 | 53 | } |
54 | 54 | } |
— | — | @@ -69,7 +69,7 @@ |
70 | 70 | |
71 | 71 | $dispatcher = new AjaxDispatcher(); |
72 | 72 | $dispatcher->performAction(); |
73 | | - $mediaWiki->restInPeace( $wgLoadBalancer ); |
| 73 | + $mediaWiki->restInPeace(); |
74 | 74 | exit; |
75 | 75 | } |
76 | 76 | |
— | — | @@ -90,7 +90,7 @@ |
91 | 91 | $mediaWiki->setVal( 'UsePathInfo', $wgUsePathInfo ); |
92 | 92 | |
93 | 93 | $mediaWiki->initialize( $wgTitle, $wgArticle, $wgOut, $wgUser, $wgRequest ); |
94 | | -$mediaWiki->finalCleanup( $wgDeferredUpdateList, $wgLoadBalancer, $wgOut ); |
| 94 | +$mediaWiki->finalCleanup ( $wgDeferredUpdateList, $wgOut ); |
95 | 95 | |
96 | 96 | # Not sure when $wgPostCommitUpdateList gets set, so I keep this separate from finalCleanup |
97 | 97 | $mediaWiki->doUpdates( $wgPostCommitUpdateList ); |
Index: trunk/extensions/MakeDBError/MakeDBError_body.php |
— | — | @@ -7,12 +7,13 @@ |
8 | 8 | } |
9 | 9 | |
10 | 10 | function execute( $par ) { |
11 | | - global $wgOut, $wgLoadBalancer; |
| 11 | + global $wgOut; |
12 | 12 | $this->setHeaders(); |
13 | 13 | if ( $par == 'connection' ) { |
14 | | - $wgLoadBalancer->mServers[1234] = $wgLoadBalancer->mServers[0]; |
15 | | - $wgLoadBalancer->mServers[1234]['user'] = 'chicken'; |
16 | | - $wgLoadBalancer->mServers[1234]['password'] = 'cluck cluck'; |
| 14 | + $lb = wfGetLB(); |
| 15 | + $lb->mServers[1234] = $lb->mServers[0]; |
| 16 | + $lb->mServers[1234]['user'] = 'chicken'; |
| 17 | + $lb->mServers[1234]['password'] = 'cluck cluck'; |
17 | 18 | $db =& wfGetDB( 1234 ); |
18 | 19 | $wgOut->addHTML("<pre>" . var_export( $db, true ) . "</pre>" ); |
19 | 20 | } else { |
Index: trunk/extensions/OAI/OAIRepo_body.php |
— | — | @@ -279,11 +279,9 @@ |
280 | 280 | */ |
281 | 281 | private function getAuditDatabase() { |
282 | 282 | if( !isset( $this->mAuditDb ) ) { |
283 | | - global $wgLoadBalancer, $oaiAuditDatabase; |
284 | | - $i = $wgLoadBalancer->getGroupIndex( 'oaiAudit' ); |
285 | | - $dbinfo = $wgLoadBalancer->mServers[$i]; |
286 | | - $this->mAuditDb = new Database( $dbinfo['host'], $dbinfo['user'], |
287 | | - $dbinfo['password'], $oaiAuditDatabase ); |
| 283 | + global $oaiAuditDatabase; |
| 284 | + $lb = wfGetLB( $oaiAuditDatabase ); |
| 285 | + $this->mAuditDb = $lb->getConnection( DB_MASTER, 'oaiAudit', $oaiAuditDatabase ); |
288 | 286 | } |
289 | 287 | return $this->mAuditDb; |
290 | 288 | } |
Index: trunk/extensions/CentralAuth/CentralAuthUser.php |
— | — | @@ -11,35 +11,6 @@ |
12 | 12 | |
13 | 13 | */ |
14 | 14 | |
15 | | -class CentralAuthHelper { |
16 | | - private static $connections = array(); |
17 | | - |
18 | | - public static function get( $dbname ) { |
19 | | - global $wgDBname; |
20 | | - if( $dbname == $wgDBname ) { |
21 | | - return wfGetDB( DB_MASTER ); |
22 | | - } |
23 | | - |
24 | | - global $wgDBuser, $wgDBpassword; |
25 | | - $server = self::getServer( $dbname ); |
26 | | - if( !isset( self::$connections[$server] ) ) { |
27 | | - self::$connections[$server] = new Database( $server, $wgDBuser, $wgDBpassword, $dbname ); |
28 | | - } |
29 | | - self::$connections[$server]->selectDB( $dbname ); |
30 | | - return self::$connections[$server]; |
31 | | - } |
32 | | - |
33 | | - private static function getServer( $dbname ) { |
34 | | - global $wgAlternateMaster, $wgDBserver; |
35 | | - if( isset( $wgAlternateMaster[$dbname] ) ) { |
36 | | - return $wgAlternateMaster[$dbname]; |
37 | | - } elseif( isset( $wgAlternateMaster['DEFAULT'] ) ) { |
38 | | - return $wgAlternateMaster['DEFAULT']; |
39 | | - } |
40 | | - return $wgDBserver; |
41 | | - } |
42 | | -} |
43 | | - |
44 | 15 | class CentralAuthUser { |
45 | 16 | |
46 | 17 | /** |
— | — | @@ -52,18 +23,16 @@ |
53 | 24 | $this->resetState(); |
54 | 25 | } |
55 | 26 | |
56 | | - /** |
57 | | - * @fixme Make use of some info to get the appropriate master DB |
58 | | - */ |
59 | 27 | public static function getCentralDB() { |
60 | | - return CentralAuthHelper::get( 'centralauth' ); |
| 28 | + return wfGetLB( 'centralauth' )->getConnection( DB_MASTER, 'centralauth', 'centralauth' ); |
61 | 29 | } |
62 | 30 | |
63 | | - /** |
64 | | - * @fixme Make use of some info to get the appropriate master DB |
65 | | - */ |
| 31 | + public static function getCentralSlaveDB() { |
| 32 | + return wfGetLB( 'centralauth' )->getConnection( DB_SLAVE, 'centralauth', 'centralauth' ); |
| 33 | + } |
| 34 | + |
66 | 35 | public static function getLocalDB( $dbname ) { |
67 | | - return CentralAuthHelper::get( $dbname ); |
| 36 | + return wfGetLB( $dbname )->getConnection( DB_MASTER, array(), $dbname ); |
68 | 37 | } |
69 | 38 | |
70 | 39 | public static function tableName( $name ) { |
— | — | @@ -845,18 +814,20 @@ |
846 | 815 | */ |
847 | 816 | function importLocalNames() { |
848 | 817 | $rows = array(); |
849 | | - foreach( self::getWikiList() as $db ) { |
850 | | - $dbr = self::getLocalDB( $db ); |
| 818 | + foreach( self::getWikiList() as $dbname ) { |
| 819 | + $lb = wfGetLB( $dbname ); |
| 820 | + $dbr = $lb->getConnection( DB_MASTER, array(), $dbname ); |
851 | 821 | $id = $dbr->selectField( |
852 | | - "`$db`.`user`", |
| 822 | + "`$dbname`.`user`", |
853 | 823 | 'user_id', |
854 | 824 | array( 'user_name' => $this->mName ), |
855 | 825 | __METHOD__ ); |
856 | 826 | if( $id ) { |
857 | 827 | $rows[] = array( |
858 | | - 'ln_dbname' => $db, |
| 828 | + 'ln_dbname' => $dbname, |
859 | 829 | 'ln_name' => $this->mName ); |
860 | 830 | } |
| 831 | + $lb->reuseConnection( $dbr ); |
861 | 832 | } |
862 | 833 | |
863 | 834 | $dbw = self::getCentralDB(); |
— | — | @@ -980,16 +951,27 @@ |
981 | 952 | * Fetch a row of user data needed for migration. |
982 | 953 | */ |
983 | 954 | protected function localUserData( $dbname ) { |
984 | | - $db = self::getLocalDB( $dbname ); |
985 | | - $row = $db->selectRow( "`$dbname`.user", |
986 | | - array( |
| 955 | + $lb = wfGetLB( $dbname ); |
| 956 | + $db = $lb->getConnection( DB_SLAVE, array(), $dbname ); |
| 957 | + $table = "`$dbname`.user"; |
| 958 | + $fields = array( |
987 | 959 | 'user_id', |
988 | 960 | 'user_email', |
989 | 961 | 'user_email_authenticated', |
990 | 962 | 'user_password', |
991 | | - 'user_editcount' ), |
992 | | - array( 'user_name' => $this->mName ), |
993 | | - __METHOD__ ); |
| 963 | + 'user_editcount' ); |
| 964 | + $conds = array( 'user_name' => $this->mName ); |
| 965 | + $row = $db->selectRow( $table, $fields, $conds, __METHOD__ ); |
| 966 | + if ( !$row ) { |
| 967 | + # Row missing from slave, try the master instead |
| 968 | + $lb->reuseConnection( $db ); |
| 969 | + $db = $lb->getConnection( DB_MASTER, array(), $dbname ); |
| 970 | + $row = $db->selectRow( $table, $fields, $conds, __METHOD__ ); |
| 971 | + } |
| 972 | + if ( !$row ) { |
| 973 | + $lb->reuseConnection( $db ); |
| 974 | + return false; |
| 975 | + } |
994 | 976 | |
995 | 977 | $data = array( |
996 | 978 | 'dbName' => $dbname, |
— | — | @@ -1033,6 +1015,7 @@ |
1034 | 1016 | } |
1035 | 1017 | } |
1036 | 1018 | $result->free(); |
| 1019 | + $lb->reuseConnection( $db ); |
1037 | 1020 | |
1038 | 1021 | return $data; |
1039 | 1022 | } |
Index: trunk/extensions/CentralAuth/SpecialMergeAccount.php |
— | — | @@ -167,9 +167,7 @@ |
168 | 168 | } |
169 | 169 | |
170 | 170 | $password = $wgRequest->getVal( 'wpPassword' ); |
171 | | - if( $password != '' ) { |
172 | | - $this->addWorkingPassword( $password ); |
173 | | - } |
| 171 | + $this->addWorkingPassword( $password ); |
174 | 172 | $passwords = $this->getWorkingPasswords(); |
175 | 173 | |
176 | 174 | $home = false; |
Index: trunk/extensions/DumpHTML/wm-scripts/queueController.php |
— | — | @@ -30,13 +30,10 @@ |
31 | 31 | } else { |
32 | 32 | $wikiSizes = array(); |
33 | 33 | foreach ( $wikiList as $wiki ) { |
34 | | - if ( $wgAlternateMaster[$wiki] ) { |
35 | | - $db = new Database( $wgAlternateMaster[$wiki], $wgDBuser, $wgDBpassword, $wiki ); |
36 | | - } else { |
37 | | - $db = wfGetDB( DB_SLAVE ); |
38 | | - } |
39 | | - |
| 34 | + $lb = wfGetLB( $wiki ); |
| 35 | + $db = $lb->getConnection( DB_SLAVE, array(), $wiki ); |
40 | 36 | $wikiSizes[$wiki] = $db->selectField( "`$wiki`.site_stats", 'ss_total_pages' ); |
| 37 | + $lb->reuseConnection( $db ); |
41 | 38 | } |
42 | 39 | file_put_contents( "$baseDir/var/checkpoints/wikiSizes", serialize( $wikiSizes ) ); |
43 | 40 | } |