Index: trunk/phase3/includes/DefaultSettings.php |
— | — | @@ -60,6 +60,10 @@ |
61 | 61 | $wgDBuser = 'wikiuser'; |
62 | 62 | $wgDBtype = "mysql"; # "mysql" for working code and "pgsql" for development/broken code |
63 | 63 | |
| 64 | +# Database load balancer |
| 65 | +$wgDBservers = false; # e.g. array(0 => "larousse", 1 => "pliny") |
| 66 | +$wgDBloads = false; # e.g. array(0 => 0.6, 1 => 0.4); |
| 67 | + |
64 | 68 | # Sysop SQL queries |
65 | 69 | $wgAllowSysopQueries = false; # Dangerous if not configured properly. |
66 | 70 | $wgDBsqluser = 'sqluser'; |
Index: trunk/phase3/includes/Setup.php |
— | — | @@ -58,6 +58,7 @@ |
59 | 59 | require_once( 'Parser.php' ); |
60 | 60 | require_once( 'ParserCache.php' ); |
61 | 61 | require_once( 'WebRequest.php' ); |
| 62 | +require_once( 'LoadBalancer.php' ); |
62 | 63 | |
63 | 64 | $wgRequest = new WebRequest(); |
64 | 65 | |
— | — | @@ -70,7 +71,8 @@ |
71 | 72 | global $wgMemc, $wgMagicWords, $wgMwRedir, $wgDebugLogFile; |
72 | 73 | global $wgMessageCache, $wgUseMemCached, $wgUseDatabaseMessages; |
73 | 74 | global $wgMsgCacheExpiry, $wgDBname, $wgCommandLineMode; |
74 | | -global $wgBlockCache, $wgParserCache, $wgParser, $wgDontTrustMemcachedWithImportantStuff; |
| 75 | +global $wgBlockCache, $wgParserCache, $wgParser, $wgDBConnections; |
| 76 | +global $wgLoadBalancer, $wgDBservers, $wgDBloads, $wgDBuser, $wgDBpassword; |
75 | 77 | |
76 | 78 | # Useful debug output |
77 | 79 | if ( $wgCommandLineMode ) { |
— | — | @@ -141,6 +143,16 @@ |
142 | 144 | } |
143 | 145 | |
144 | 146 | wfProfileOut( $fname.'-memcached' ); |
| 147 | +wfProfileIn( $fname.'-database' ); |
| 148 | + |
| 149 | +if ( !$wgDBservers ) { |
| 150 | + $wgDBservers = array( $wgDBserver ); |
| 151 | + $wgDBloads = array( 1 ); |
| 152 | +} |
| 153 | +$wgLoadBalancer = LoadBalancer::newFromParams( $wgDBservers, $wgDBloads, $wgDBuser, $wgDBpassword, $wgDBname ); |
| 154 | +$wgLoadBalancer->force(0); |
| 155 | + |
| 156 | +wfProfileOut( $fname.'-database' ); |
145 | 157 | wfProfileIn( $fname.'-language' ); |
146 | 158 | require_once( 'languages/Language.php' ); |
147 | 159 | |
— | — | @@ -218,6 +230,7 @@ |
219 | 231 | $wgParserCache = new ParserCache(); |
220 | 232 | $wgParser = new Parser(); |
221 | 233 | $wgOut->setParserOptions( ParserOptions::newFromUser( $wgUser ) ); |
| 234 | +$wgDBConnections = array(); |
222 | 235 | |
223 | 236 | # Placeholders in case of DB error |
224 | 237 | $wgTitle = Title::newFromText( wfMsg( 'badtitle' ) ); |
Index: trunk/phase3/includes/LoadBalancer.php |
— | — | @@ -1,10 +1,27 @@ |
2 | 2 | <?php |
3 | 3 | # Database load balancing object |
4 | 4 | |
| 5 | +# Valid database indexes |
| 6 | +# Operation-based indexes |
| 7 | +define( "DB_READ", -1 ); # Read from the slave (or only server) |
| 8 | +define( "DB_WRITE", -2 ); # Write to master (or only server) |
| 9 | +define( "DB_LAST", -3 ); # Whatever database was used last |
| 10 | + |
| 11 | +# Task-based indexes |
| 12 | +# ***NOT USED YET, EXPERIMENTAL*** |
| 13 | +# These may be defined in $wgDBservers. If they aren't, the default reader or writer will be used |
| 14 | +# Even numbers are always readers, odd numbers are writers |
| 15 | +define( "DB_TASK_FIRST", 1000 ); # First in list |
| 16 | +define( "DB_SEARCH_R", 1000 ); # Search read |
| 17 | +define( "DB_SEARCH_W", 1001 ); # Search write |
| 18 | +define( "DB_ASKSQL_R", 1002 ); # Special:Asksql read |
| 19 | +define( "DB_WATCHLIST_R", 1004 ); # Watchlist read |
| 20 | +define( "DB_TASK_LAST", 1004) ; # Last in list |
| 21 | + |
5 | 22 | class LoadBalancer { |
6 | 23 | /* private */ var $mServers, $mConnections, $mLoads; |
7 | 24 | /* private */ var $mUser, $mPassword, $mDbName, $mFailFunction; |
8 | | - /* private */ var $mForce, $mReadIndex; |
| 25 | + /* private */ var $mForce, $mReadIndex, $mLastConn; |
9 | 26 | |
10 | 27 | function LoadBalancer() |
11 | 28 | { |
— | — | @@ -17,6 +34,7 @@ |
18 | 35 | $this->mFailFunction = false; |
19 | 36 | $this->mReadIndex = -1; |
20 | 37 | $this->mForce = -1; |
| 38 | + $this->mLastConn = false; |
21 | 39 | } |
22 | 40 | |
23 | 41 | function newFromParams( $servers, $loads, $user, $password, $dbName, $failFunction = false ) |
— | — | @@ -38,6 +56,7 @@ |
39 | 57 | $this->mWriteIndex = -1; |
40 | 58 | $this->mForce = -1; |
41 | 59 | $this->mConnections = array(); |
| 60 | + $this->mLastConn = false; |
42 | 61 | wfSeedRandom(); |
43 | 62 | } |
44 | 63 | |
— | — | @@ -96,21 +115,49 @@ |
97 | 116 | } |
98 | 117 | return $conn; |
99 | 118 | } |
100 | | - |
| 119 | + |
101 | 120 | function &getConnection( $i, $fail = false ) |
102 | 121 | { |
103 | | - if ( !array_key_exists( $i, $this->mConnections) || !$this->mConnections[$i]->isOpen() ) { |
104 | | - $this->mConnections[$i] = Database::newFromParams( $this->mServers[$i], $this->mUser, |
105 | | - $this->mPassword, $this->mDbName, 1 ); |
106 | | - } |
107 | | - if ( !$this->mConnections[$i]->isOpen() ) { |
108 | | - wfDebug( "Failed to connect to database $i at {$this->mServers[$i]}\n" ); |
109 | | - if ( $fail ) { |
110 | | - $this->reportConnectionError( $this->mConnections[$i] ); |
| 122 | + /* |
| 123 | + # Task-based index |
| 124 | + if ( $i >= DB_TASK_FIRST && $i < DB_TASK_LAST ) { |
| 125 | + if ( $i % 2 ) { |
| 126 | + # Odd index use writer |
| 127 | + $i = DB_WRITE; |
| 128 | + } else { |
| 129 | + # Even index use reader |
| 130 | + $i = DB_READ; |
111 | 131 | } |
112 | | - $this->mConnections[$i] = false; |
| 132 | + }*/ |
| 133 | + |
| 134 | + # Operation-based index |
| 135 | + # Note, getReader() and getWriter() will re-enter this function |
| 136 | + if ( $i == DB_READ ) { |
| 137 | + $this->mLastConn =& $this->getReader(); |
| 138 | + } elseif ( $i == DB_WRITE ) { |
| 139 | + $this->mLastConn =& $this->getWriter(); |
| 140 | + } elseif ( $i == DB_LAST ) { |
| 141 | + # Just use $this->mLastConn, which should already be set |
| 142 | + if ( $this->mLastConn === false ) { |
| 143 | + # Oh dear, not set, best to use the writer for safety |
| 144 | + $this->mLastConn =& $this->getWriter(); |
| 145 | + } |
| 146 | + } else { |
| 147 | + # Explicit index |
| 148 | + if ( !array_key_exists( $i, $this->mConnections) || !$this->mConnections[$i]->isOpen() ) { |
| 149 | + $this->mConnections[$i] = Database::newFromParams( $this->mServers[$i], $this->mUser, |
| 150 | + $this->mPassword, $this->mDbName, 1 ); |
| 151 | + } |
| 152 | + if ( !$this->mConnections[$i]->isOpen() ) { |
| 153 | + wfDebug( "Failed to connect to database $i at {$this->mServers[$i]}\n" ); |
| 154 | + if ( $fail ) { |
| 155 | + $this->reportConnectionError( $this->mConnections[$i] ); |
| 156 | + } |
| 157 | + $this->mConnections[$i] = false; |
| 158 | + } |
| 159 | + $this->mLastConn =& $this->mConnections[$i]; |
113 | 160 | } |
114 | | - return $this->mConnections[$i]; |
| 161 | + return $this->mLastConn; |
115 | 162 | } |
116 | 163 | |
117 | 164 | function reportConnectionError( &$conn ) |
— | — | @@ -140,4 +187,9 @@ |
141 | 188 | { |
142 | 189 | $this->mForce = $i; |
143 | 190 | } |
| 191 | + |
| 192 | + function haveIndex( $i ) |
| 193 | + { |
| 194 | + return array_key_exists( $i, $this->mServers ); |
| 195 | + } |
144 | 196 | } |
Index: trunk/phase3/includes/DatabaseFunctions.php |
— | — | @@ -11,6 +11,7 @@ |
12 | 12 | # NB: This file follows a connect on demand scheme. Do |
13 | 13 | # not access the $wgDatabase variable directly unless |
14 | 14 | # you intend to set it. Use wfGetDB(). |
| 15 | +$wgDatabase = false; |
15 | 16 | |
16 | 17 | $wgIsMySQL=false; |
17 | 18 | $wgIsPg=false; |
— | — | @@ -23,46 +24,33 @@ |
24 | 25 | $wgIsPg=true; |
25 | 26 | } |
26 | 27 | |
27 | | -# Query the database |
28 | | -# $db: DB_READ = -1 read from slave (or only server) |
29 | | -# DB_WRITE = -2 write to master (or only server) |
30 | | -# 0,1,2,... query a database with a specific index |
| 28 | + |
31 | 29 | # Replication is not actually implemented just yet |
32 | 30 | # Usually aborts on failure |
33 | 31 | # If errors are explicitly ignored, returns success |
34 | 32 | function wfQuery( $sql, $db, $fname = "" ) |
35 | 33 | { |
36 | | - global $wgDatabase, $wgDBserver, $wgDBuser, $wgDBpassword, $wgDBname, |
37 | | - $wgDebugDumpSql, $wgBufferSQLResults, $wgIgnoreSQLErrors; |
38 | | - |
39 | 34 | if ( !is_numeric( $db ) ) { |
40 | 35 | # Someone has tried to call this the old way |
41 | 36 | $wgOut->fatalError( wfMsgNoDB( "wrong_wfQuery_params", $db, $sql ) ); |
42 | 37 | } |
43 | | - |
44 | | - $db =& wfGetDB(); |
45 | | - return $db->query( $sql, $fname ); |
| 38 | + $c =& wfGetDB( $db ); |
| 39 | + return $c->query( $sql, $fname ); |
46 | 40 | } |
47 | 41 | |
48 | | -# Connect on demand |
49 | | -function &wfGetDB() |
| 42 | +function &wfGetDB( $db = DB_LAST ) |
50 | 43 | { |
51 | | - global $wgDatabase, $wgDBserver, $wgDBuser, $wgDBpassword, $wgDBname, |
52 | | - $wgDebugDumpSql, $wgBufferSQLResults, $wgIgnoreSQLErrors; |
53 | | - if ( !$wgDatabase ) { |
54 | | - $wgDatabase = Database::newFromParams( $wgDBserver, $wgDBuser, $wgDBpassword, |
55 | | - $wgDBname, false, $wgDebugDumpSql, $wgBufferSQLResults, $wgIgnoreSQLErrors ); |
56 | | - } |
57 | | - return $wgDatabase; |
| 44 | + global $wgLoadBalancer; |
| 45 | + return $wgLoadBalancer->getConnection( $db ); |
58 | 46 | } |
59 | 47 | |
60 | 48 | # Turns buffering of SQL result sets on (true) or off (false). Default is |
61 | 49 | # "on" and it should not be changed without good reasons. |
62 | 50 | # Returns the previous state. |
63 | 51 | |
64 | | -function wfBufferSQLResults( $newstate ) |
| 52 | +function wfBufferSQLResults( $newstate, $dbi = DB_LAST ) |
65 | 53 | { |
66 | | - $db =& wfGetDB(); |
| 54 | + $db =& wfGetDB( $dbi ); |
67 | 55 | return $db->setBufferResults( $newstate ); |
68 | 56 | } |
69 | 57 | |
— | — | @@ -73,122 +61,122 @@ |
74 | 62 | # situation as appropriate. |
75 | 63 | # Returns the previous state. |
76 | 64 | |
77 | | -function wfIgnoreSQLErrors( $newstate ) |
| 65 | +function wfIgnoreSQLErrors( $newstate, $dbi = DB_LAST ) |
78 | 66 | { |
79 | | - $db =& wfGetDB(); |
| 67 | + $db =& wfGetDB( $dbi ); |
80 | 68 | return $db->setIgnoreErrors( $newstate ); |
81 | 69 | } |
82 | 70 | |
83 | | -function wfFreeResult( $res ) |
| 71 | +function wfFreeResult( $res, $dbi = DB_LAST ) |
84 | 72 | { |
85 | | - $db =& wfGetDB(); |
| 73 | + $db =& wfGetDB( $dbi ); |
86 | 74 | $db->freeResult( $res ); |
87 | 75 | } |
88 | 76 | |
89 | | -function wfFetchObject( $res ) |
| 77 | +function wfFetchObject( $res, $dbi = DB_LAST ) |
90 | 78 | { |
91 | | - $db =& wfGetDB(); |
92 | | - return $db->fetchObject( $res ); |
| 79 | + $db =& wfGetDB( $dbi ); |
| 80 | + return $db->fetchObject( $res, $dbi = DB_LAST ); |
93 | 81 | } |
94 | 82 | |
95 | | -function wfFetchRow( $res ) |
| 83 | +function wfFetchRow( $res, $dbi = DB_LAST ) |
96 | 84 | { |
97 | | - $db =& wfGetDB(); |
98 | | - return $db->fetchRow ( $res ); |
| 85 | + $db =& wfGetDB( $dbi ); |
| 86 | + return $db->fetchRow ( $res, $dbi = DB_LAST ); |
99 | 87 | } |
100 | 88 | |
101 | | -function wfNumRows( $res ) |
| 89 | +function wfNumRows( $res, $dbi = DB_LAST ) |
102 | 90 | { |
103 | | - $db =& wfGetDB(); |
104 | | - return $db->numRows( $res ); |
| 91 | + $db =& wfGetDB( $dbi ); |
| 92 | + return $db->numRows( $res, $dbi = DB_LAST ); |
105 | 93 | } |
106 | 94 | |
107 | | -function wfNumFields( $res ) |
| 95 | +function wfNumFields( $res, $dbi = DB_LAST ) |
108 | 96 | { |
109 | | - $db =& wfGetDB(); |
| 97 | + $db =& wfGetDB( $dbi ); |
110 | 98 | return $db->numFields( $res ); |
111 | 99 | } |
112 | 100 | |
113 | | -function wfFieldName( $res, $n ) |
| 101 | +function wfFieldName( $res, $n, $dbi = DB_LAST ) |
114 | 102 | { |
115 | | - $db =& wfGetDB(); |
116 | | - return $db->fieldName( $res, $n ); |
| 103 | + $db =& wfGetDB( $dbi ); |
| 104 | + return $db->fieldName( $res, $n, $dbi = DB_LAST ); |
117 | 105 | } |
118 | 106 | |
119 | | -function wfInsertId() |
| 107 | +function wfInsertId( $dbi = DB_LAST ) |
120 | 108 | { |
121 | | - $db =& wfGetDB(); |
| 109 | + $db =& wfGetDB( $dbi ); |
122 | 110 | return $db->insertId(); |
123 | 111 | } |
124 | | -function wfDataSeek( $res, $row ) |
| 112 | +function wfDataSeek( $res, $row, $dbi = DB_LAST ) |
125 | 113 | { |
126 | | - $db =& wfGetDB(); |
| 114 | + $db =& wfGetDB( $dbi ); |
127 | 115 | return $db->dataSeek( $res, $row ); |
128 | 116 | } |
129 | 117 | |
130 | | -function wfLastErrno() |
| 118 | +function wfLastErrno( $dbi = DB_LAST ) |
131 | 119 | { |
132 | | - $db =& wfGetDB(); |
| 120 | + $db =& wfGetDB( $dbi ); |
133 | 121 | return $db->lastErrno(); |
134 | 122 | } |
135 | 123 | |
136 | | -function wfLastError() |
| 124 | +function wfLastError( $dbi = DB_LAST ) |
137 | 125 | { |
138 | | - $db =& wfGetDB(); |
| 126 | + $db =& wfGetDB( $dbi ); |
139 | 127 | return $db->lastError(); |
140 | 128 | } |
141 | 129 | |
142 | | -function wfAffectedRows() |
| 130 | +function wfAffectedRows( $dbi = DB_LAST ) |
143 | 131 | { |
144 | | - $db =& wfGetDB(); |
| 132 | + $db =& wfGetDB( $dbi ); |
145 | 133 | return $db->affectedRows(); |
146 | 134 | } |
147 | 135 | |
148 | | -function wfLastDBquery() |
| 136 | +function wfLastDBquery( $dbi = DB_LAST ) |
149 | 137 | { |
150 | | - $db =& wfGetDB(); |
| 138 | + $db =& wfGetDB( $dbi ); |
151 | 139 | return $db->lastQuery(); |
152 | 140 | } |
153 | 141 | |
154 | | -function wfSetSQL( $table, $var, $value, $cond ) |
| 142 | +function wfSetSQL( $table, $var, $value, $cond, $dbi = DB_WRITE ) |
155 | 143 | { |
156 | | - $db =& wfGetDB(); |
| 144 | + $db =& wfGetDB( $dbi ); |
157 | 145 | return $db->set( $table, $var, $value, $cond ); |
158 | 146 | } |
159 | 147 | |
160 | | -function wfGetSQL( $table, $var, $cond="" ) |
| 148 | +function wfGetSQL( $table, $var, $cond="", $dbi = DB_LAST ) |
161 | 149 | { |
162 | | - $db =& wfGetDB(); |
| 150 | + $db =& wfGetDB( $dbi ); |
163 | 151 | return $db->get( $table, $var, $cond ); |
164 | 152 | } |
165 | 153 | |
166 | | -function wfFieldExists( $table, $field ) |
| 154 | +function wfFieldExists( $table, $field, $dbi = DB_LAST ) |
167 | 155 | { |
168 | | - $db =& wfGetDB(); |
| 156 | + $db =& wfGetDB( $dbi ); |
169 | 157 | return $db->fieldExists( $table, $field ); |
170 | 158 | } |
171 | 159 | |
172 | | -function wfIndexExists( $table, $index ) |
| 160 | +function wfIndexExists( $table, $index, $dbi = DB_LAST ) |
173 | 161 | { |
174 | | - $db =& wfGetDB(); |
| 162 | + $db =& wfGetDB( $dbi ); |
175 | 163 | return $db->indexExists( $table, $index ); |
176 | 164 | } |
177 | 165 | |
178 | | -function wfInsertArray( $table, $array ) |
| 166 | +function wfInsertArray( $table, $array, $fname = "wfInsertArray", $dbi = DB_WRITE ) |
179 | 167 | { |
180 | | - $db =& wfGetDB(); |
181 | | - return $db->insertArray( $table, $array ); |
| 168 | + $db =& wfGetDB( $dbi ); |
| 169 | + return $db->insertArray( $table, $array, $fname ); |
182 | 170 | } |
183 | 171 | |
184 | | -function wfGetArray( $table, $vars, $conds, $fname = "wfGetArray" ) |
| 172 | +function wfGetArray( $table, $vars, $conds, $fname = "wfGetArray", $dbi = DB_LAST ) |
185 | 173 | { |
186 | | - $db =& wfGetDB(); |
| 174 | + $db =& wfGetDB( $dbi ); |
187 | 175 | return $db->getArray( $table, $vars, $conds, $fname ); |
188 | 176 | } |
189 | 177 | |
190 | | -function wfUpdateArray( $table, $values, $conds, $fname = "wfUpdateArray" ) |
| 178 | +function wfUpdateArray( $table, $values, $conds, $fname = "wfUpdateArray", $dbi = DB_WRITE ) |
191 | 179 | { |
192 | | - $db =& wfGetDB(); |
| 180 | + $db =& wfGetDB( $dbi ); |
193 | 181 | $db->updateArray( $table, $values, $conds, $fname ); |
194 | 182 | } |
195 | 183 | |
Index: trunk/phase3/includes/Database.php |
— | — | @@ -5,10 +5,6 @@ |
6 | 6 | # |
7 | 7 | require_once( "CacheManager.php" ); |
8 | 8 | |
9 | | -define( "DB_READ", -1 ); |
10 | | -define( "DB_WRITE", -2 ); |
11 | | -define( "DB_LAST", -3 ); |
12 | | - |
13 | 9 | define( "LIST_COMMA", 0 ); |
14 | 10 | define( "LIST_AND", 1 ); |
15 | 11 | define( "LIST_SET", 2 ); |
— | — | @@ -143,10 +139,11 @@ |
144 | 140 | { |
145 | 141 | if ( $this->mFailFunction ) { |
146 | 142 | if ( !is_int( $this->mFailFunction ) ) { |
147 | | - $this->$mFailFunction( $this ); |
| 143 | + $ff = $this->mFailFunction; |
| 144 | + $ff( $this, mysql_error() ); |
148 | 145 | } |
149 | 146 | } else { |
150 | | - wfEmergencyAbort( $this ); |
| 147 | + wfEmergencyAbort( $this, mysql_error() ); |
151 | 148 | } |
152 | 149 | } |
153 | 150 | |
— | — | @@ -459,7 +456,7 @@ |
460 | 457 | |
461 | 458 | /* Standard fail function, called by default when a connection cannot be established |
462 | 459 | Displays the file cache if possible */ |
463 | | -function wfEmergencyAbort( &$conn ) { |
| 460 | +function wfEmergencyAbort( &$conn, $error ) { |
464 | 461 | global $wgTitle, $wgUseFileCache, $title, $wgInputEncoding, $wgSiteNotice, $wgOutputEncoding; |
465 | 462 | |
466 | 463 | if( !headers_sent() ) { |
— | — | @@ -470,7 +467,7 @@ |
471 | 468 | header( "Pragma: nocache" ); |
472 | 469 | } |
473 | 470 | $msg = $wgSiteNotice; |
474 | | - if($msg == "") $msg = wfMsgNoDB( "noconnect" ); |
| 471 | + if($msg == "") $msg = wfMsgNoDB( "noconnect", $error ); |
475 | 472 | $text = $msg; |
476 | 473 | |
477 | 474 | if($wgUseFileCache) { |