Index: branches/wmf/1.17wmf1/includes/objectcache/SqlBagOStuff.php |
— | — | @@ -9,6 +9,8 @@ |
10 | 10 | var $lb, $db, $serverInfo; |
11 | 11 | var $lastExpireAll = 0; |
12 | 12 | var $purgePeriod = 100; |
| 13 | + var $shards = 1; |
| 14 | + var $tableName = 'objectcache'; |
13 | 15 | |
14 | 16 | /** |
15 | 17 | * Constructor. Parameters are: |
— | — | @@ -21,6 +23,18 @@ |
22 | 24 | * reciprocal of the probability of purging on any given |
23 | 25 | * request. If this is set to zero, purging will never be |
24 | 26 | * done. |
| 27 | + * |
| 28 | + * - tableName: The table name to use, default is "objectcache". |
| 29 | + * |
| 30 | + * - shards: The number of tables to use for data storage. If this is |
| 31 | + * more than 1, table names will be formed in the style |
| 32 | + * objectcacheNNN where NNN is the shard index, between 0 and |
| 33 | + * shards-1. The number of digits will be the minimum number |
| 34 | + * required to hold the largest shard index. Data will be |
| 35 | + * distributed across all tables by key hash. This is for |
| 36 | + * MySQL bugs 61735 and 61736. |
| 37 | + * |
| 38 | + * @param $params array |
25 | 39 | */ |
26 | 40 | public function __construct( $params ) { |
27 | 41 | if ( isset( $params['server'] ) ) { |
— | — | @@ -30,6 +44,12 @@ |
31 | 45 | if ( isset( $params['purgePeriod'] ) ) { |
32 | 46 | $this->purgePeriod = intval( $params['purgePeriod'] ); |
33 | 47 | } |
| 48 | + if ( isset( $params['tableName'] ) ) { |
| 49 | + $this->tableName = $params['tableName']; |
| 50 | + } |
| 51 | + if ( isset( $params['shards'] ) ) { |
| 52 | + $this->shards = intval( $params['shards'] ); |
| 53 | + } |
34 | 54 | } |
35 | 55 | |
36 | 56 | protected function getDB() { |
— | — | @@ -57,11 +77,37 @@ |
58 | 78 | return $this->db; |
59 | 79 | } |
60 | 80 | |
| 81 | + /** |
| 82 | + * Get the table name for a given key |
| 83 | + */ |
| 84 | + protected function getTableByKey( $key ) { |
| 85 | + if ( $this->shards > 1 ) { |
| 86 | + $hash = hexdec( substr( md5( $key ), 0, 8 ) ) & 0x7fffffff; |
| 87 | + return $this->getTableByShard( $hash % $this->shards ); |
| 88 | + } else { |
| 89 | + return $this->tableName; |
| 90 | + } |
| 91 | + } |
| 92 | + |
| 93 | + /** |
| 94 | + * Get the table name for a given shard index |
| 95 | + */ |
| 96 | + protected function getTableByShard( $index ) { |
| 97 | + if ( $this->shards > 1 ) { |
| 98 | + $decimals = strlen( $this->shards - 1 ); |
| 99 | + return $this->tableName . |
| 100 | + sprintf( "%0{$decimals}d", $index ); |
| 101 | + } else { |
| 102 | + return $this->tableName; |
| 103 | + } |
| 104 | + } |
| 105 | + |
61 | 106 | public function get( $key ) { |
62 | 107 | # expire old entries if any |
63 | 108 | $this->garbageCollect(); |
64 | 109 | $db = $this->getDB(); |
65 | | - $row = $db->selectRow( 'objectcache', array( 'value', 'exptime' ), |
| 110 | + $tableName = $this->getTableByKey( $key ); |
| 111 | + $row = $db->selectRow( $tableName, array( 'value', 'exptime' ), |
66 | 112 | array( 'keyname' => $key ), __METHOD__ ); |
67 | 113 | |
68 | 114 | if ( !$row ) { |
— | — | @@ -77,7 +123,7 @@ |
78 | 124 | $db->begin(); |
79 | 125 | # Put the expiry time in the WHERE condition to avoid deleting a |
80 | 126 | # newly-inserted value |
81 | | - $db->delete( 'objectcache', |
| 127 | + $db->delete( $tableName, |
82 | 128 | array( |
83 | 129 | 'keyname' => $key, |
84 | 130 | 'exptime' => $row->exptime |
— | — | @@ -114,7 +160,9 @@ |
115 | 161 | $db->begin(); |
116 | 162 | // (bug 24425) use a replace if the db supports it instead of |
117 | 163 | // delete/insert to avoid clashes with conflicting keynames |
118 | | - $db->replace( 'objectcache', array( 'keyname' ), |
| 164 | + $db->replace( |
| 165 | + $this->getTableByKey( $key ), |
| 166 | + array( 'keyname' ), |
119 | 167 | array( |
120 | 168 | 'keyname' => $key, |
121 | 169 | 'value' => $db->encodeBlob( $this->serialize( $value ) ), |
— | — | @@ -135,7 +183,10 @@ |
136 | 184 | |
137 | 185 | try { |
138 | 186 | $db->begin(); |
139 | | - $db->delete( 'objectcache', array( 'keyname' => $key ), __METHOD__ ); |
| 187 | + $db->delete( |
| 188 | + $this->getTableByKey( $key ), |
| 189 | + array( 'keyname' => $key ), |
| 190 | + __METHOD__ ); |
140 | 191 | $db->commit(); |
141 | 192 | } catch ( DBQueryError $e ) { |
142 | 193 | $this->handleWriteError( $e ); |
— | — | @@ -148,19 +199,24 @@ |
149 | 200 | |
150 | 201 | public function incr( $key, $step = 1 ) { |
151 | 202 | $db = $this->getDB(); |
| 203 | + $tableName = $this->getTableByKey( $key ); |
152 | 204 | $step = intval( $step ); |
153 | 205 | |
154 | 206 | try { |
155 | 207 | $db->begin(); |
156 | | - $row = $db->selectRow( 'objectcache', array( 'value', 'exptime' ), |
157 | | - array( 'keyname' => $key ), __METHOD__, array( 'FOR UPDATE' ) ); |
| 208 | + $row = $db->selectRow( |
| 209 | + $tableName, |
| 210 | + array( 'value', 'exptime' ), |
| 211 | + array( 'keyname' => $key ), |
| 212 | + __METHOD__, |
| 213 | + array( 'FOR UPDATE' ) ); |
158 | 214 | if ( $row === false ) { |
159 | 215 | // Missing |
160 | 216 | $db->commit(); |
161 | 217 | |
162 | 218 | return null; |
163 | 219 | } |
164 | | - $db->delete( 'objectcache', array( 'keyname' => $key ), __METHOD__ ); |
| 220 | + $db->delete( $tableName, array( 'keyname' => $key ), __METHOD__ ); |
165 | 221 | if ( $this->isExpired( $row->exptime ) ) { |
166 | 222 | // Expired, do not reinsert |
167 | 223 | $db->commit(); |
— | — | @@ -170,7 +226,7 @@ |
171 | 227 | |
172 | 228 | $oldValue = intval( $this->unserialize( $db->decodeBlob( $row->value ) ) ); |
173 | 229 | $newValue = $oldValue + $step; |
174 | | - $db->insert( 'objectcache', |
| 230 | + $db->insert( $tableName, |
175 | 231 | array( |
176 | 232 | 'keyname' => $key, |
177 | 233 | 'value' => $db->encodeBlob( $this->serialize( $newValue ) ), |
— | — | @@ -188,11 +244,14 @@ |
189 | 245 | |
190 | 246 | public function keys() { |
191 | 247 | $db = $this->getDB(); |
192 | | - $res = $db->select( 'objectcache', array( 'keyname' ), false, __METHOD__ ); |
193 | 248 | $result = array(); |
194 | 249 | |
195 | | - foreach ( $res as $row ) { |
196 | | - $result[] = $row->keyname; |
| 250 | + for ( $i = 0; $i < $this->shards; $i++ ) { |
| 251 | + $res = $db->select( $this->getTableByShard( $i ), |
| 252 | + array( 'keyname' ), false, __METHOD__ ); |
| 253 | + foreach ( $res as $row ) { |
| 254 | + $result[] = $row->keyname; |
| 255 | + } |
197 | 256 | } |
198 | 257 | |
199 | 258 | return $result; |
— | — | @@ -232,9 +291,14 @@ |
233 | 292 | $now = $db->timestamp(); |
234 | 293 | |
235 | 294 | try { |
236 | | - $db->begin(); |
237 | | - $db->delete( 'objectcache', array( 'exptime < ' . $db->addQuotes( $now ) ), __METHOD__ ); |
238 | | - $db->commit(); |
| 295 | + for ( $i = 0; $i < $this->shards; $i++ ) { |
| 296 | + $db->begin(); |
| 297 | + $db->delete( |
| 298 | + $this->getTableByShard( $i ), |
| 299 | + array( 'exptime < ' . $db->addQuotes( $now ) ), |
| 300 | + __METHOD__ ); |
| 301 | + $db->commit(); |
| 302 | + } |
239 | 303 | } catch ( DBQueryError $e ) { |
240 | 304 | $this->handleWriteError( $e ); |
241 | 305 | } |
— | — | @@ -244,9 +308,11 @@ |
245 | 309 | $db = $this->getDB(); |
246 | 310 | |
247 | 311 | try { |
248 | | - $db->begin(); |
249 | | - $db->delete( 'objectcache', '*', __METHOD__ ); |
250 | | - $db->commit(); |
| 312 | + for ( $i = 0; $i < $this->shards; $i++ ) { |
| 313 | + $db->begin(); |
| 314 | + $db->delete( $this->getTableByShard( $i ), '*', __METHOD__ ); |
| 315 | + $db->commit(); |
| 316 | + } |
251 | 317 | } catch ( DBQueryError $e ) { |
252 | 318 | $this->handleWriteError( $e ); |
253 | 319 | } |
— | — | @@ -308,6 +374,27 @@ |
309 | 375 | wfDebug( __METHOD__ . ": ignoring query error\n" ); |
310 | 376 | $db->ignoreErrors( false ); |
311 | 377 | } |
| 378 | + |
| 379 | + /** |
| 380 | + * Create shard tables. For use from eval.php. |
| 381 | + */ |
| 382 | + public function createTables() { |
| 383 | + $db = $this->getDB(); |
| 384 | + if ( $db->getType() !== 'mysql' |
| 385 | + || version_compare( $db->getServerVersion(), '4.1.0', '<' ) ) |
| 386 | + { |
| 387 | + throw new MWException( __METHOD__ . ' is not supported on this DB server' ); |
| 388 | + } |
| 389 | + |
| 390 | + for ( $i = 0; $i < $this->shards; $i++ ) { |
| 391 | + $db->begin(); |
| 392 | + $db->query( |
| 393 | + 'CREATE TABLE ' . $db->tableName( $this->getTableByShard( $i ) ) . |
| 394 | + ' LIKE ' . $db->tableName( 'objectcache' ), |
| 395 | + __METHOD__ ); |
| 396 | + $db->commit(); |
| 397 | + } |
| 398 | + } |
312 | 399 | } |
313 | 400 | |
314 | 401 | /** |
Property changes on: branches/wmf/1.17wmf1/includes/objectcache/SqlBagOStuff.php |
___________________________________________________________________ |
Modified: svn:mergeinfo |
315 | 402 | Merged /trunk/phase3/includes/objectcache/SqlBagOStuff.php:r91402 |