r55079 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r55078‎ | r55079 | r55080 >
Date:03:45, 15 August 2009
Author:tstarling
Status:ok (Comments)
Tags:
Comment:
* Converted BagOStuff.php from the style of memcached-client.php to the standard MediaWiki style, including camel case, using protected visibility instead of initial underscore, abstract functions instead of stubs, stylize.php.
* In SqlBagOStuff, ignore errors due to a read-only database, per my comments on CR r42796. Same for LocalisationCache.
* Merged SqlBagOStuff and MediaWikiBagOStuff, that proved to be an awkward and unnecessary generalisation. Use the standard quoting wrapper functions instead of $db->query().
* Implemented atomic incr() and decr() functions for SqlBagOStuff.
* Made incr() and decr() generally work roughly the same as it does in memcached, respecting negative steps instead of ignoring such operations. This allows decr() to be implemented in terms of incr().
* Per bug 11533, in MessageCache.php, don't retry 20 times on a cache failure, that's really memcached-specific and won't be useful for other cache types. It's not really very useful for memcached either.
* Moved MySQL-specific implementations of wasDeadlock() and wasErrorReissuable() to DatabaseMysql.
* Briefly tested page views with $wgReadOnly=read_only=1, fixed an error from Article::viewUpdates(). A CentralAuth fix will be in a subsequent commit.
Modified paths:
  • /trunk/phase3/includes/Article.php (modified) (history)
  • /trunk/phase3/includes/BagOStuff.php (modified) (history)
  • /trunk/phase3/includes/LocalisationCache.php (modified) (history)
  • /trunk/phase3/includes/MessageCache.php (modified) (history)
  • /trunk/phase3/includes/ObjectCache.php (modified) (history)
  • /trunk/phase3/includes/db/Database.php (modified) (history)
  • /trunk/phase3/includes/db/DatabaseMysql.php (modified) (history)
  • /trunk/phase3/includes/db/DatabaseSqlite.php (modified) (history)

Diff [purge]

Index: trunk/phase3/includes/BagOStuff.php
@@ -37,129 +37,122 @@
3838 *
3939 * @ingroup Cache
4040 */
41 -class BagOStuff {
42 - var $debugmode;
 41+abstract class BagOStuff {
 42+ var $debugMode = false;
4343
44 - function __construct() {
45 - $this->set_debug( false );
 44+ public function set_debug( $bool ) {
 45+ $this->debugMode = $bool;
4646 }
4747
48 - function set_debug($bool) {
49 - $this->debugmode = $bool;
50 - }
51 -
5248 /* *** THE GUTS OF THE OPERATION *** */
5349 /* Override these with functional things in subclasses */
5450
55 - function get($key) {
56 - /* stub */
57 - return false;
58 - }
 51+ /**
 52+ * Get an item with the given key. Returns false if it does not exist.
 53+ * @param $key string
 54+ */
 55+ abstract public function get( $key );
5956
60 - function set($key, $value, $exptime=0) {
61 - /* stub */
62 - return false;
63 - }
 57+ /**
 58+ * Set an item.
 59+ * @param $key string
 60+ * @param $value mixed
 61+ * @param $exptime int Either an interval in seconds or a unix timestamp for expiry
 62+ */
 63+ abstract public function set( $key, $value, $exptime = 0 );
6464
65 - function delete($key, $time=0) {
66 - /* stub */
67 - return false;
68 - }
 65+ /*
 66+ * Delete an item.
 67+ * @param $key string
 68+ * @param $time int Amount of time to delay the operation (mostly memcached-specific)
 69+ */
 70+ abstract public function delete( $key, $time = 0 );
6971
70 - function lock($key, $timeout = 0) {
 72+ public function lock( $key, $timeout = 0 ) {
7173 /* stub */
7274 return true;
7375 }
7476
75 - function unlock($key) {
 77+ public function unlock( $key ) {
7678 /* stub */
7779 return true;
7880 }
7981
80 - function keys() {
 82+ public function keys() {
8183 /* stub */
8284 return array();
8385 }
8486
8587 /* *** Emulated functions *** */
8688 /* Better performance can likely be got with custom written versions */
87 - function get_multi($keys) {
 89+ public function get_multi( $keys ) {
8890 $out = array();
89 - foreach($keys as $key)
90 - $out[$key] = $this->get($key);
 91+ foreach ( $keys as $key ) {
 92+ $out[$key] = $this->get( $key );
 93+ }
9194 return $out;
9295 }
9396
94 - function set_multi($hash, $exptime=0) {
95 - foreach($hash as $key => $value)
96 - $this->set($key, $value, $exptime);
 97+ public function set_multi( $hash, $exptime = 0 ) {
 98+ foreach ( $hash as $key => $value ) {
 99+ $this->set( $key, $value, $exptime );
 100+ }
97101 }
98102
99 - function add($key, $value, $exptime=0) {
100 - if( $this->get($key) == false ) {
101 - $this->set($key, $value, $exptime);
 103+ public function add( $key, $value, $exptime = 0 ) {
 104+ if ( $this->get( $key ) == false ) {
 105+ $this->set( $key, $value, $exptime );
102106 return true;
103107 }
104108 }
105109
106 - function add_multi($hash, $exptime=0) {
107 - foreach($hash as $key => $value)
108 - $this->add($key, $value, $exptime);
 110+ public function add_multi( $hash, $exptime = 0 ) {
 111+ foreach ( $hash as $key => $value ) {
 112+ $this->add( $key, $value, $exptime );
 113+ }
109114 }
110115
111 - function delete_multi($keys, $time=0) {
112 - foreach($keys as $key)
113 - $this->delete($key, $time);
 116+ public function delete_multi( $keys, $time = 0 ) {
 117+ foreach ( $keys as $key ) {
 118+ $this->delete( $key, $time );
 119+ }
114120 }
115121
116 - function replace($key, $value, $exptime=0) {
117 - if( $this->get($key) !== false )
118 - $this->set($key, $value, $exptime);
 122+ public function replace( $key, $value, $exptime = 0 ) {
 123+ if ( $this->get( $key ) !== false ) {
 124+ $this->set( $key, $value, $exptime );
 125+ }
119126 }
120127
121 - function incr($key, $value=1) {
122 - if ( !$this->lock($key) ) {
 128+ public function incr( $key, $value = 1 ) {
 129+ if ( !$this->lock( $key ) ) {
123130 return false;
124131 }
125 - $value = intval($value);
126 - if($value < 0) $value = 0;
 132+ $value = intval( $value );
127133
128134 $n = false;
129 - if( ($n = $this->get($key)) !== false ) {
 135+ if ( ( $n = $this->get( $key ) ) !== false ) {
130136 $n += $value;
131 - $this->set($key, $n); // exptime?
 137+ $this->set( $key, $n ); // exptime?
132138 }
133 - $this->unlock($key);
 139+ $this->unlock( $key );
134140 return $n;
135141 }
136142
137 - function decr($key, $value=1) {
138 - if ( !$this->lock($key) ) {
139 - return false;
140 - }
141 - $value = intval($value);
142 - if($value < 0) $value = 0;
143 -
144 - $m = false;
145 - if( ($n = $this->get($key)) !== false ) {
146 - $m = $n - $value;
147 - if($m < 0) $m = 0;
148 - $this->set($key, $m); // exptime?
149 - }
150 - $this->unlock($key);
151 - return $m;
 143+ public function decr( $key, $value = 1 ) {
 144+ return $this->incr( $key, -$value );
152145 }
153146
154 - function _debug($text) {
155 - if($this->debugmode)
156 - wfDebug("BagOStuff debug: $text\n");
 147+ public function debug( $text ) {
 148+ if ( $this->debugMode )
 149+ wfDebug( "BagOStuff debug: $text\n" );
157150 }
158151
159152 /**
160153 * Convert an optionally relative time to an absolute time
161154 */
162 - static function convertExpiry( $exptime ) {
163 - if(($exptime != 0) && ($exptime < 3600*24*30)) {
 155+ protected function convertExpiry( $exptime ) {
 156+ if ( ( $exptime != 0 ) && ( $exptime < 3600 * 24 * 30 ) ) {
164157 return time() + $exptime;
165158 } else {
166159 return $exptime;
@@ -182,30 +175,34 @@
183176 $this->bag = array();
184177 }
185178
186 - function _expire($key) {
 179+ protected function expire( $key ) {
187180 $et = $this->bag[$key][1];
188 - if(($et == 0) || ($et > time()))
 181+ if ( ( $et == 0 ) || ( $et > time() ) ) {
189182 return false;
190 - $this->delete($key);
 183+ }
 184+ $this->delete( $key );
191185 return true;
192186 }
193187
194 - function get($key) {
195 - if( !isset( $this->bag[$key] ) )
 188+ function get( $key ) {
 189+ if ( !isset( $this->bag[$key] ) ) {
196190 return false;
197 - if($this->_expire($key))
 191+ }
 192+ if ( $this->expire( $key ) ) {
198193 return false;
 194+ }
199195 return $this->bag[$key][0];
200196 }
201197
202 - function set($key,$value,$exptime=0) {
203 - $this->bag[$key] = array( $value, BagOStuff::convertExpiry( $exptime ) );
 198+ function set( $key, $value, $exptime = 0 ) {
 199+ $this->bag[$key] = array( $value, $this->convertExpiry( $exptime ) );
204200 }
205201
206 - function delete($key,$time=0) {
207 - if( !isset( $this->bag[$key] ) )
 202+ function delete( $key, $time = 0 ) {
 203+ if ( !isset( $this->bag[$key] ) ) {
208204 return false;
209 - unset($this->bag[$key]);
 205+ }
 206+ unset( $this->bag[$key] );
210207 return true;
211208 }
212209
@@ -215,182 +212,187 @@
216213 }
217214
218215 /**
219 - * Generic class to store objects in a database
 216+ * Class to store objects in the database
220217 *
221218 * @ingroup Cache
222219 */
223 -abstract class SqlBagOStuff extends BagOStuff {
224 - var $table;
225 - var $lastexpireall = 0;
 220+class SqlBagOStuff extends BagOStuff {
 221+ var $lb, $db;
 222+ var $lastExpireAll = 0;
226223
227 - /**
228 - * Constructor
229 - *
230 - * @param $tablename String: name of the table to use
231 - */
232 - function __construct($tablename = 'objectcache') {
233 - $this->table = $tablename;
 224+ protected function getDB() {
 225+ if ( !isset( $this->lb ) ) {
 226+ $this->lb = wfGetLBFactory()->newMainLB();
 227+ $this->db = $this->lb->getConnection( DB_MASTER );
 228+ $this->db->clearFlag( DBO_TRX );
 229+ }
 230+ return $this->db;
234231 }
235232
236 - function get($key) {
237 - /* expire old entries if any */
 233+ public function get( $key ) {
 234+ # expire old entries if any
238235 $this->garbageCollect();
239 -
240 - $res = $this->_query(
241 - "SELECT value,exptime FROM $0 WHERE keyname='$1'", $key);
242 - if(!$res) {
243 - $this->_debug("get: ** error: " . $this->_dberror($res) . " **");
 236+ $db = $this->getDB();
 237+ $row = $db->selectRow( 'objectcache', array( 'value', 'exptime' ),
 238+ array( 'keyname' => $key ), __METHOD__ );
 239+ if ( !$row ) {
 240+ $this->debug( 'get: no matching rows' );
244241 return false;
245242 }
246 - if($row=$this->_fetchobject($res)) {
247 - $this->_debug("get: retrieved data; exp time is " . $row->exptime);
248 - if ( $row->exptime != $this->_maxdatetime() &&
249 - wfTimestamp( TS_UNIX, $row->exptime ) < time() )
250 - {
251 - $this->_debug("get: key has expired, deleting");
252 - $this->delete($key);
253 - return false;
 243+
 244+ $this->debug( "get: retrieved data; expiry time is " . $row->exptime );
 245+ if ( $this->isExpired( $row->exptime ) ) {
 246+ $this->debug( "get: key has expired, deleting" );
 247+ try {
 248+ $db->begin();
 249+ # Put the expiry time in the WHERE condition to avoid deleting a
 250+ # newly-inserted value
 251+ $db->delete( 'objectcache',
 252+ array(
 253+ 'keyname' => $key,
 254+ 'exptime' => $row->exptime
 255+ ), __METHOD__ );
 256+ $db->commit();
 257+ } catch ( DBQueryError $e ) {
 258+ $this->handleWriteError( $e );
254259 }
255 - return $this->_unserialize($this->_blobdecode($row->value));
256 - } else {
257 - $this->_debug('get: no matching rows');
 260+ return false;
258261 }
259 - return false;
 262+ return $this->unserialize( $db->decodeBlob( $row->value ) );
260263 }
261264
262 - function set($key,$value,$exptime=0) {
263 - if ( $this->_readonly() ) {
264 - return false;
265 - }
266 - $exptime = intval($exptime);
267 - if($exptime < 0) $exptime = 0;
268 - if($exptime == 0) {
269 - $exp = $this->_maxdatetime();
 265+ public function set( $key, $value, $exptime = 0 ) {
 266+ $db = $this->getDB();
 267+ $exptime = intval( $exptime );
 268+ if ( $exptime < 0 ) $exptime = 0;
 269+ if ( $exptime == 0 ) {
 270+ $encExpiry = $this->getMaxDateTime();
270271 } else {
271 - if($exptime < 3.16e8) # ~10 years
 272+ if ( $exptime < 3.16e8 ) # ~10 years
272273 $exptime += time();
273 - $exp = $this->_fromunixtime($exptime);
 274+ $encExpiry = $db->timestamp( $exptime );
274275 }
275 - $this->_begin();
276 - $this->_query(
277 - "DELETE FROM $0 WHERE keyname='$1'", $key );
278 - $this->_doinsert($this->getTableName(), array(
 276+ try {
 277+ $db->begin();
 278+ $db->delete( 'objectcache', array( 'keyname' => $key ), __METHOD__ );
 279+ $db->insert( 'objectcache',
 280+ array(
279281 'keyname' => $key,
280 - 'value' => $this->_blobencode($this->_serialize($value)),
281 - 'exptime' => $exp
282 - ));
283 - $this->_commit();
284 - return true; /* ? */
 282+ 'value' => $db->encodeBlob( $this->serialize( $value ) ),
 283+ 'exptime' => $encExpiry
 284+ ), __METHOD__ );
 285+ $db->commit();
 286+ } catch ( DBQueryError $e ) {
 287+ $this->handleWriteError( $e );
 288+ return false;
 289+ }
 290+ return true;
285291 }
286292
287 - function delete($key,$time=0) {
288 - if ( $this->_readonly() ) {
 293+ public function delete( $key, $time = 0 ) {
 294+ $db = $this->getDB();
 295+ try {
 296+ $db->begin();
 297+ $db->delete( 'objectcache', array( 'keyname' => $key ), __METHOD__ );
 298+ $db->commit();
 299+ } catch ( DBQueryError $e ) {
 300+ $this->handleWriteError( $e );
289301 return false;
290302 }
291 - $this->_begin();
292 - $this->_query(
293 - "DELETE FROM $0 WHERE keyname='$1'", $key );
294 - $this->_commit();
295 - return true; /* ? */
 303+ return true;
296304 }
297305
298 - function keys() {
299 - $res = $this->_query( "SELECT keyname FROM $0" );
300 - if(!$res) {
301 - $this->_debug("keys: ** error: " . $this->_dberror($res) . " **");
302 - return array();
 306+ public function incr( $key, $step = 1 ) {
 307+ $db = $this->getDB();
 308+ $step = intval( $step );
 309+
 310+ try {
 311+ $db->begin();
 312+ $row = $db->selectRow( 'objectcache', array( 'value', 'exptime' ),
 313+ array( 'keyname' => $key ), __METHOD__, array( 'FOR UPDATE' ) );
 314+ if ( $row === false ) {
 315+ // Missing
 316+ $db->commit();
 317+ return false;
 318+ }
 319+ $db->delete( 'objectcache', array( 'keyname' => $key ), __METHOD__ );
 320+ if ( $this->isExpired( $row->exptime ) ) {
 321+ // Expired, do not reinsert
 322+ $db->commit();
 323+ return false;
 324+ }
 325+
 326+ $oldValue = intval( $this->unserialize( $db->decodeBlob( $row->value ) ) );
 327+ $newValue = $oldValue + $step;
 328+ $db->insert( 'objectcache',
 329+ array(
 330+ 'keyname' => $key,
 331+ 'value' => $db->encodeBlob( $this->serialize( $newValue ) ),
 332+ 'exptime' => $row->exptime
 333+ ), __METHOD__ );
 334+ $db->commit();
 335+ } catch ( DBQueryError $e ) {
 336+ $this->handleWriteError( $e );
 337+ return false;
303338 }
 339+ return $newValue;
 340+ }
 341+
 342+ public function keys() {
 343+ $db = $this->getDB();
 344+ $res = $db->select( 'objectcache', array( 'keyname' ), false, __METHOD__ );
304345 $result = array();
305 - while( $row = $this->_fetchobject($res) ) {
 346+ foreach ( $res as $row ) {
306347 $result[] = $row->keyname;
307348 }
308349 return $result;
309350 }
310351
311 - function getTableName() {
312 - return $this->table;
 352+ protected function isExpired( $exptime ) {
 353+ return $exptime != $this->getMaxDateTime() && wfTimestamp( TS_UNIX, $exptime ) < time();
313354 }
314355
315 - function _query($sql) {
316 - $reps = func_get_args();
317 - $reps[0] = $this->getTableName();
318 - // ewwww
319 - for($i=0;$i<count($reps);$i++) {
320 - $sql = str_replace(
321 - '$' . $i,
322 - $i > 0 ? $this->_strencode($reps[$i]) : $reps[$i],
323 - $sql);
 356+ protected function getMaxDateTime() {
 357+ if ( time() > 0x7fffffff ) {
 358+ return $this->getDB()->timestamp( 1 << 62 );
 359+ } else {
 360+ return $this->getDB()->timestamp( 0x7fffffff );
324361 }
325 - $res = $this->_doquery($sql);
326 - if($res == false) {
327 - $this->_debug('query failed: ' . $this->_dberror($res));
328 - }
329 - return $res;
330362 }
331363
332 - function _strencode($str) {
333 - /* Protect strings in SQL */
334 - return str_replace( "'", "''", $str );
335 - }
336 - function _blobencode($str) {
337 - return $str;
338 - }
339 - function _blobdecode($str) {
340 - return $str;
341 - }
342 -
343 - abstract function _doinsert($table, $vals);
344 - abstract function _doquery($sql);
345 -
346 - abstract function _readonly();
347 -
348 - function _begin() {}
349 - function _commit() {}
350 -
351 - function _freeresult($result) {
352 - /* stub */
353 - return false;
354 - }
355 -
356 - function _dberror($result) {
357 - /* stub */
358 - return 'unknown error';
359 - }
360 -
361 - abstract function _maxdatetime();
362 - abstract function _fromunixtime($ts);
363 -
364 - function garbageCollect() {
 364+ protected function garbageCollect() {
365365 /* Ignore 99% of requests */
366366 if ( !mt_rand( 0, 100 ) ) {
367 - $nowtime = time();
 367+ $now = time();
368368 /* Avoid repeating the delete within a few seconds */
369 - if ( $nowtime > ($this->lastexpireall + 1) ) {
370 - $this->lastexpireall = $nowtime;
371 - $this->expireall();
 369+ if ( $now > ( $this->lastExpireAll + 1 ) ) {
 370+ $this->lastExpireAll = $now;
 371+ $this->expireAll();
372372 }
373373 }
374374 }
375375
376 - function expireall() {
377 - /* Remove any items that have expired */
378 - if ( $this->_readonly() ) {
379 - return false;
 376+ public function expireAll() {
 377+ $db = $this->getDB();
 378+ $now = $db->timestamp();
 379+ try {
 380+ $db->begin();
 381+ $db->delete( 'objectcache', array( 'exptime < ' . $db->addQuotes( $now ) ), __METHOD__ );
 382+ $db->commit();
 383+ } catch ( DBQueryError $e ) {
 384+ $this->handleWriteError( $e );
380385 }
381 - $now = $this->_fromunixtime( time() );
382 - $this->_begin();
383 - $this->_query( "DELETE FROM $0 WHERE exptime < '$now'" );
384 - $this->_commit();
385386 }
386387
387 - function deleteall() {
388 - /* Clear *all* items from cache table */
389 - if ( $this->_readonly() ) {
390 - return false;
 388+ public function deleteAll() {
 389+ $db = $this->getDB();
 390+ try {
 391+ $db->begin();
 392+ $db->delete( 'objectcache', '*', __METHOD__ );
 393+ $db->commit();
 394+ } catch ( DBQueryError $e ) {
 395+ $this->handleWriteError( $e );
391396 }
392 - $this->_begin();
393 - $this->_query( "DELETE FROM $0" );
394 - $this->_commit();
395397 }
396398
397399 /**
@@ -401,9 +403,9 @@
402404 * @param $data mixed
403405 * @return string
404406 */
405 - function _serialize( &$data ) {
 407+ protected function serialize( &$data ) {
406408 $serial = serialize( $data );
407 - if( function_exists( 'gzdeflate' ) ) {
 409+ if ( function_exists( 'gzdeflate' ) ) {
408410 return gzdeflate( $serial );
409411 } else {
410412 return $serial;
@@ -415,104 +417,41 @@
416418 * @param $serial string
417419 * @return mixed
418420 */
419 - function _unserialize( $serial ) {
420 - if( function_exists( 'gzinflate' ) ) {
 421+ protected function unserialize( $serial ) {
 422+ if ( function_exists( 'gzinflate' ) ) {
421423 $decomp = @gzinflate( $serial );
422 - if( false !== $decomp ) {
 424+ if ( false !== $decomp ) {
423425 $serial = $decomp;
424426 }
425427 }
426428 $ret = unserialize( $serial );
427429 return $ret;
428430 }
429 -}
430431
431 -/**
432 - * Stores objects in the main database of the wiki
433 - *
434 - * @ingroup Cache
435 - */
436 -class MediaWikiBagOStuff extends SqlBagOStuff {
437 - var $tableInitialised = false;
438 - var $lb, $db;
439 -
440 - function _getDB(){
441 - if ( !isset( $this->lb ) ) {
442 - $this->lb = wfGetLBFactory()->newMainLB();
443 - $this->db = $this->lb->getConnection( DB_MASTER );
444 - $this->db->clearFlag( DBO_TRX );
 432+ /**
 433+ * Handle a DBQueryError which occurred during a write operation.
 434+ * Ignore errors which are due to a read-only database, rethrow others.
 435+ */
 436+ protected function handleWriteError( $exception ) {
 437+ $db = $this->getDB();
 438+ if ( !$db->wasReadOnlyError() ) {
 439+ throw $exception;
445440 }
446 - return $this->db;
447 - }
448 - function _begin() {
449 - $this->_getDB()->begin();
450 - }
451 - function _commit() {
452 - $this->_getDB()->commit();
453 - }
454 - function _doquery($sql) {
455 - return $this->_getDB()->query( $sql, __METHOD__ );
456 - }
457 - function _doinsert($t, $v) {
458 - return $this->_getDB()->insert($t, $v, __METHOD__, array( 'IGNORE' ) );
459 - }
460 - function _fetchobject($result) {
461 - return $this->_getDB()->fetchObject($result);
462 - }
463 - function _freeresult($result) {
464 - return $this->_getDB()->freeResult($result);
465 - }
466 - function _dberror($result) {
467 - return $this->_getDB()->lastError();
468 - }
469 - function _maxdatetime() {
470 - if ( time() > 0x7fffffff ) {
471 - return $this->_fromunixtime( 1<<62 );
472 - } else {
473 - return $this->_fromunixtime( 0x7fffffff );
 441+ try {
 442+ $db->rollback();
 443+ } catch ( DBQueryError $e ) {
474444 }
 445+ wfDebug( __METHOD__ . ": ignoring query error\n" );
 446+ $db->ignoreErrors( false );
475447 }
476 - function _fromunixtime($ts) {
477 - return $this->_getDB()->timestamp($ts);
478 - }
479 - /***
480 - * Note -- this should *not* check wfReadOnly().
481 - * Read-only mode has been repurposed from the original
482 - * "nothing must write to the database" to "users should not
483 - * be able to edit or alter anything user-visible".
484 - *
485 - * Backend bits like the object cache should continue
486 - * to work in this mode, otherwise things will blow up
487 - * like the message cache failing to save its state,
488 - * causing long delays (bug 11533).
489 - */
490 - function _readonly(){
491 - return false;
492 - }
493 - function _strencode($s) {
494 - return $this->_getDB()->strencode($s);
495 - }
496 - function _blobencode($s) {
497 - return $this->_getDB()->encodeBlob($s);
498 - }
499 - function _blobdecode($s) {
500 - return $this->_getDB()->decodeBlob($s);
501 - }
502 - function getTableName() {
503 - if ( !$this->tableInitialised ) {
504 - $dbw = $this->_getDB();
505 - /* This is actually a hack, we should be able
506 - to use Language classes here... or not */
507 - if (!$dbw)
508 - throw new MWException("Could not connect to database");
509 - $this->table = $dbw->tableName( $this->table );
510 - $this->tableInitialised = true;
511 - }
512 - return $this->table;
513 - }
514448 }
515449
516450 /**
 451+ * Backwards compatibility alias
 452+ */
 453+class MediaWikiBagOStuff extends SqlBagOStuff {}
 454+
 455+/**
517456 * This is a wrapper for Turck MMCache's shared memory functions.
518457 *
519458 * You can store objects with mmcache_put() and mmcache_get(), but Turck seems
@@ -528,7 +467,7 @@
529468 * @ingroup Cache
530469 */
531470 class TurckBagOStuff extends BagOStuff {
532 - function get($key) {
 471+ public function get( $key ) {
533472 $val = mmcache_get( $key );
534473 if ( is_string( $val ) ) {
535474 $val = unserialize( $val );
@@ -536,22 +475,22 @@
537476 return $val;
538477 }
539478
540 - function set($key, $value, $exptime=0) {
 479+ public function set( $key, $value, $exptime = 0 ) {
541480 mmcache_put( $key, serialize( $value ), $exptime );
542481 return true;
543482 }
544483
545 - function delete($key, $time=0) {
 484+ public function delete( $key, $time = 0 ) {
546485 mmcache_rm( $key );
547486 return true;
548487 }
549488
550 - function lock($key, $waitTimeout = 0 ) {
 489+ public function lock( $key, $waitTimeout = 0 ) {
551490 mmcache_lock( $key );
552491 return true;
553492 }
554493
555 - function unlock($key) {
 494+ public function unlock( $key ) {
556495 mmcache_unlock( $key );
557496 return true;
558497 }
@@ -563,21 +502,21 @@
564503 * @ingroup Cache
565504 */
566505 class APCBagOStuff extends BagOStuff {
567 - function get($key) {
568 - $val = apc_fetch($key);
 506+ public function get( $key ) {
 507+ $val = apc_fetch( $key );
569508 if ( is_string( $val ) ) {
570509 $val = unserialize( $val );
571510 }
572511 return $val;
573512 }
574513
575 - function set($key, $value, $exptime=0) {
576 - apc_store($key, serialize($value), $exptime);
 514+ public function set( $key, $value, $exptime = 0 ) {
 515+ apc_store( $key, serialize( $value ), $exptime );
577516 return true;
578517 }
579518
580 - function delete($key, $time=0) {
581 - apc_delete($key);
 519+ public function delete( $key, $time = 0 ) {
 520+ apc_delete( $key );
582521 return true;
583522 }
584523 }
@@ -592,7 +531,7 @@
593532 * @ingroup Cache
594533 */
595534 class eAccelBagOStuff extends BagOStuff {
596 - function get($key) {
 535+ public function get( $key ) {
597536 $val = eaccelerator_get( $key );
598537 if ( is_string( $val ) ) {
599538 $val = unserialize( $val );
@@ -600,22 +539,22 @@
601540 return $val;
602541 }
603542
604 - function set($key, $value, $exptime=0) {
 543+ public function set( $key, $value, $exptime = 0 ) {
605544 eaccelerator_put( $key, serialize( $value ), $exptime );
606545 return true;
607546 }
608547
609 - function delete($key, $time=0) {
 548+ public function delete( $key, $time = 0 ) {
610549 eaccelerator_rm( $key );
611550 return true;
612551 }
613552
614 - function lock($key, $waitTimeout = 0 ) {
 553+ public function lock( $key, $waitTimeout = 0 ) {
615554 eaccelerator_lock( $key );
616555 return true;
617556 }
618557
619 - function unlock($key) {
 558+ public function unlock( $key ) {
620559 eaccelerator_unlock( $key );
621560 return true;
622561 }
@@ -637,7 +576,7 @@
638577 */
639578 public function get( $key ) {
640579 $val = xcache_get( $key );
641 - if( is_string( $val ) )
 580+ if ( is_string( $val ) )
642581 $val = unserialize( $val );
643582 return $val;
644583 }
@@ -670,20 +609,24 @@
671610 }
672611
673612 /**
674 - * @todo document
 613+ * Cache that uses DBA as a backend.
 614+ * Slow due to the need to constantly open and close the file to avoid holding
 615+ * writer locks. Intended for development use only, as a memcached workalike
 616+ * for systems that don't have it.
 617+ *
675618 * @ingroup Cache
676619 */
677620 class DBABagOStuff extends BagOStuff {
678621 var $mHandler, $mFile, $mReader, $mWriter, $mDisabled;
679622
680 - function __construct( $handler = 'db3', $dir = false ) {
 623+ public function __construct( $handler = 'db3', $dir = false ) {
681624 if ( $dir === false ) {
682625 global $wgTmpDirectory;
683626 $dir = $wgTmpDirectory;
684627 }
685628 $this->mFile = "$dir/mw-cache-" . wfWikiID();
686629 $this->mFile .= '.db';
687 - wfDebug( __CLASS__.": using cache file {$this->mFile}\n" );
 630+ wfDebug( __CLASS__ . ": using cache file {$this->mFile}\n" );
688631 $this->mHandler = $handler;
689632 }
690633
@@ -692,7 +635,7 @@
693636 */
694637 function encode( $value, $expiry ) {
695638 # Convert to absolute time
696 - $expiry = BagOStuff::convertExpiry( $expiry );
 639+ $expiry = $this->convertExpiry( $expiry );
697640 return sprintf( '%010u', intval( $expiry ) ) . ' ' . serialize( $value );
698641 }
699642
@@ -732,7 +675,7 @@
733676
734677 function get( $key ) {
735678 wfProfileIn( __METHOD__ );
736 - wfDebug( __METHOD__."($key)\n" );
 679+ wfDebug( __METHOD__ . "($key)\n" );
737680 $handle = $this->getReader();
738681 if ( !$handle ) {
739682 return null;
@@ -747,16 +690,16 @@
748691 $handle = $this->getWriter();
749692 dba_delete( $key, $handle );
750693 dba_close( $handle );
751 - wfDebug( __METHOD__.": $key expired\n" );
 694+ wfDebug( __METHOD__ . ": $key expired\n" );
752695 $val = null;
753696 }
754697 wfProfileOut( __METHOD__ );
755698 return $val;
756699 }
757700
758 - function set( $key, $value, $exptime=0 ) {
 701+ function set( $key, $value, $exptime = 0 ) {
759702 wfProfileIn( __METHOD__ );
760 - wfDebug( __METHOD__."($key)\n" );
 703+ wfDebug( __METHOD__ . "($key)\n" );
761704 $blob = $this->encode( $value, $exptime );
762705 $handle = $this->getWriter();
763706 if ( !$handle ) {
@@ -770,7 +713,7 @@
771714
772715 function delete( $key, $time = 0 ) {
773716 wfProfileIn( __METHOD__ );
774 - wfDebug( __METHOD__."($key)\n" );
 717+ wfDebug( __METHOD__ . "($key)\n" );
775718 $handle = $this->getWriter();
776719 if ( !$handle ) {
777720 return false;
@@ -808,11 +751,11 @@
809752 function keys() {
810753 $reader = $this->getReader();
811754 $k1 = dba_firstkey( $reader );
812 - if( !$k1 ) {
 755+ if ( !$k1 ) {
813756 return array();
814757 }
815758 $result[] = $k1;
816 - while( $key = dba_nextkey( $reader ) ) {
 759+ while ( $key = dba_nextkey( $reader ) ) {
817760 $result[] = $key;
818761 }
819762 return $result;
Index: trunk/phase3/includes/Article.php
@@ -3080,6 +3080,9 @@
30813081 */
30823082 public function viewUpdates() {
30833083 global $wgDeferredUpdateList, $wgDisableCounters, $wgUser;
 3084+ if ( wfReadOnly() ) {
 3085+ return;
 3086+ }
30843087 # Don't update page view counters on views from bot users (bug 14044)
30853088 if( !$wgDisableCounters && !$wgUser->isAllowed('bot') && $this->getID() ) {
30863089 Article::incViewCount( $this->getID() );
Index: trunk/phase3/includes/ObjectCache.php
@@ -91,7 +91,7 @@
9292
9393 if ( $type == CACHE_DB || ( $inputType == CACHE_ANYTHING && $cache === false ) ) {
9494 if ( !array_key_exists( CACHE_DB, $wgCaches ) ) {
95 - $wgCaches[CACHE_DB] = new MediaWikiBagOStuff('objectcache');
 95+ $wgCaches[CACHE_DB] = new SqlBagOStuff('objectcache');
9696 }
9797 $cache =& $wgCaches[CACHE_DB];
9898 }
Index: trunk/phase3/includes/LocalisationCache.php
@@ -723,6 +723,7 @@
724724 var $currentLang;
725725 var $writesDone = false;
726726 var $dbw, $batch;
 727+ var $readOnly = false;
727728
728729 public function get( $code, $key ) {
729730 if ( $this->writesDone ) {
@@ -740,17 +741,34 @@
741742 }
742743
743744 public function startWrite( $code ) {
 745+ if ( $this->readOnly ) {
 746+ return;
 747+ }
744748 if ( !$code ) {
745749 throw new MWException( __METHOD__.": Invalid language \"$code\"" );
746750 }
747751 $this->dbw = wfGetDB( DB_MASTER );
748 - $this->dbw->begin();
749 - $this->dbw->delete( 'l10n_cache', array( 'lc_lang' => $code ), __METHOD__ );
 752+ try {
 753+ $this->dbw->begin();
 754+ $this->dbw->delete( 'l10n_cache', array( 'lc_lang' => $code ), __METHOD__ );
 755+ } catch ( DBQueryError $e ) {
 756+ if ( $this->dbw->wasReadOnlyError() ) {
 757+ $this->readOnly = true;
 758+ $this->dbw->rollback();
 759+ $this->dbw->ignoreErrors( false );
 760+ return;
 761+ } else {
 762+ throw $e;
 763+ }
 764+ }
750765 $this->currentLang = $code;
751766 $this->batch = array();
752767 }
753768
754769 public function finishWrite() {
 770+ if ( $this->readOnly ) {
 771+ return;
 772+ }
755773 if ( $this->batch ) {
756774 $this->dbw->insert( 'l10n_cache', $this->batch, __METHOD__ );
757775 }
@@ -762,6 +780,9 @@
763781 }
764782
765783 public function set( $key, $value ) {
 784+ if ( $this->readOnly ) {
 785+ return;
 786+ }
766787 if ( is_null( $this->currentLang ) ) {
767788 throw new MWException( __CLASS__.': must call startWrite() before calling set()' );
768789 }
Index: trunk/phase3/includes/MessageCache.php
@@ -432,16 +432,10 @@
433433
434434 $cacheKey = wfMemcKey( 'messages', $code );
435435
436 - $i = 0;
437436 if ( $memc ) {
438 - # Save in memcached
439 - # Keep trying if it fails, this is kind of important
440 -
441 - for ($i=0; $i<20 &&
442 - !$this->mMemc->set( $cacheKey, $cache, $this->mExpiry );
443 - $i++ ) {
444 - usleep(mt_rand(500000,1500000));
445 - }
 437+ $success = $this->mMemc->set( $cacheKey, $cache, $this->mExpiry );
 438+ } else {
 439+ $success = true;
446440 }
447441
448442 # Save to local cache
@@ -456,11 +450,6 @@
457451 }
458452 }
459453
460 - if ( $i == 20 ) {
461 - $success = false;
462 - } else {
463 - $success = true;
464 - }
465454 wfProfileOut( __METHOD__ );
466455 return $success;
467456 }
Index: trunk/phase3/includes/db/DatabaseMysql.php
@@ -360,6 +360,31 @@
361361 $encValue = $value ? '1' : '0';
362362 $this->query( "SET sql_big_selects=$encValue", __METHOD__ );
363363 }
 364+
 365+
 366+ /**
 367+ * Determines if the last failure was due to a deadlock
 368+ */
 369+ function wasDeadlock() {
 370+ return $this->lastErrno() == 1213;
 371+ }
 372+
 373+ /**
 374+ * Determines if the last query error was something that should be dealt
 375+ * with by pinging the connection and reissuing the query
 376+ */
 377+ function wasErrorReissuable() {
 378+ return $this->lastErrno() == 2013 || $this->lastErrno() == 2006;
 379+ }
 380+
 381+ /**
 382+ * Determines if the last failure was due to the database being read-only.
 383+ */
 384+ function wasReadOnlyError() {
 385+ return $this->lastErrno() == 1223 ||
 386+ ( $this->lastErrno() == 1290 && strpos( $this->lastError(), '--read-only' ) !== false );
 387+ }
 388+
364389 }
365390
366391 /**
Index: trunk/phase3/includes/db/Database.php
@@ -1715,20 +1715,30 @@
17161716
17171717 /**
17181718 * Determines if the last failure was due to a deadlock
 1719+ * STUB
17191720 */
17201721 function wasDeadlock() {
1721 - return $this->lastErrno() == 1213;
 1722+ return false;
17221723 }
17231724
17241725 /**
17251726 * Determines if the last query error was something that should be dealt
1726 - * with by pinging the connection and reissuing the query
 1727+ * with by pinging the connection and reissuing the query.
 1728+ * STUB
17271729 */
17281730 function wasErrorReissuable() {
1729 - return $this->lastErrno() == 2013 || $this->lastErrno() == 2006;
 1731+ return false;
17301732 }
17311733
17321734 /**
 1735+ * Determines if the last failure was due to the database being read-only.
 1736+ * STUB
 1737+ */
 1738+ function wasReadOnlyError() {
 1739+ return false;
 1740+ }
 1741+
 1742+ /**
17331743 * Perform a deadlock-prone transaction.
17341744 *
17351745 * This function invokes a callback function to perform a set of write
Index: trunk/phase3/includes/db/DatabaseSqlite.php
@@ -280,6 +280,10 @@
281281 return $this->lastErrno() == SQLITE_SCHEMA;
282282 }
283283
 284+ function wasReadOnlyError() {
 285+ return $this->lastErrno() == SQLITE_READONLY;
 286+ }
 287+
284288 /**
285289 * @return string wikitext of a link to the server software's web site
286290 */

Follow-up revisions

RevisionCommit summaryAuthorDate
r61126MediaWikiBagOStuff -> SqlBagOStuff in comment, both are the same since r55079ialex14:44, 16 January 2010

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r42796* (bug 11533) Fixed insane slowdown when in read-only mode for long periods...brion01:12, 30 October 2008

Comments

#Comment by Juliano (talk | contribs)   14:31, 11 September 2009

This changed the behavior of BagOStuff::decr(), I don't know if intentionally. Before, decr() never decremented below 0. Now decr() decrements past zero. It doesn't seem to be used anywhere in core, though.

Status & tagging log