BagOStuff.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821
  1. <?php
  2. #
  3. # Copyright (C) 2003-2004 Brion Vibber <brion@pobox.com>
  4. # http://www.mediawiki.org/
  5. #
  6. # This program is free software; you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation; either version 2 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License along
  17. # with this program; if not, write to the Free Software Foundation, Inc.,
  18. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. # http://www.gnu.org/copyleft/gpl.html
  20. /**
  21. * @defgroup Cache Cache
  22. *
  23. * @file
  24. * @ingroup Cache
  25. */
  26. /**
  27. * interface is intended to be more or less compatible with
  28. * the PHP memcached client.
  29. *
  30. * backends for local hash array and SQL table included:
  31. * <code>
  32. * $bag = new HashBagOStuff();
  33. * $bag = new MediaWikiBagOStuff($tablename); # connect to db first
  34. * </code>
  35. *
  36. * @ingroup Cache
  37. */
  38. class BagOStuff {
  39. var $debugmode;
  40. function __construct() {
  41. $this->set_debug( false );
  42. }
  43. function set_debug($bool) {
  44. $this->debugmode = $bool;
  45. }
  46. /* *** THE GUTS OF THE OPERATION *** */
  47. /* Override these with functional things in subclasses */
  48. function get($key) {
  49. /* stub */
  50. return false;
  51. }
  52. function set($key, $value, $exptime=0) {
  53. /* stub */
  54. return false;
  55. }
  56. function delete($key, $time=0) {
  57. /* stub */
  58. return false;
  59. }
  60. function lock($key, $timeout = 0) {
  61. /* stub */
  62. return true;
  63. }
  64. function unlock($key) {
  65. /* stub */
  66. return true;
  67. }
  68. function keys() {
  69. /* stub */
  70. return array();
  71. }
  72. /* *** Emulated functions *** */
  73. /* Better performance can likely be got with custom written versions */
  74. function get_multi($keys) {
  75. $out = array();
  76. foreach($keys as $key)
  77. $out[$key] = $this->get($key);
  78. return $out;
  79. }
  80. function set_multi($hash, $exptime=0) {
  81. foreach($hash as $key => $value)
  82. $this->set($key, $value, $exptime);
  83. }
  84. function add($key, $value, $exptime=0) {
  85. if( $this->get($key) == false ) {
  86. $this->set($key, $value, $exptime);
  87. return true;
  88. }
  89. }
  90. function add_multi($hash, $exptime=0) {
  91. foreach($hash as $key => $value)
  92. $this->add($key, $value, $exptime);
  93. }
  94. function delete_multi($keys, $time=0) {
  95. foreach($keys as $key)
  96. $this->delete($key, $time);
  97. }
  98. function replace($key, $value, $exptime=0) {
  99. if( $this->get($key) !== false )
  100. $this->set($key, $value, $exptime);
  101. }
  102. function incr($key, $value=1) {
  103. if ( !$this->lock($key) ) {
  104. return false;
  105. }
  106. $value = intval($value);
  107. if($value < 0) $value = 0;
  108. $n = false;
  109. if( ($n = $this->get($key)) !== false ) {
  110. $n += $value;
  111. $this->set($key, $n); // exptime?
  112. }
  113. $this->unlock($key);
  114. return $n;
  115. }
  116. function decr($key, $value=1) {
  117. if ( !$this->lock($key) ) {
  118. return false;
  119. }
  120. $value = intval($value);
  121. if($value < 0) $value = 0;
  122. $m = false;
  123. if( ($n = $this->get($key)) !== false ) {
  124. $m = $n - $value;
  125. if($m < 0) $m = 0;
  126. $this->set($key, $m); // exptime?
  127. }
  128. $this->unlock($key);
  129. return $m;
  130. }
  131. function _debug($text) {
  132. if($this->debugmode)
  133. wfDebug("BagOStuff debug: $text\n");
  134. }
  135. /**
  136. * Convert an optionally relative time to an absolute time
  137. */
  138. static function convertExpiry( $exptime ) {
  139. if(($exptime != 0) && ($exptime < 3600*24*30)) {
  140. return time() + $exptime;
  141. } else {
  142. return $exptime;
  143. }
  144. }
  145. }
  146. /**
  147. * Functional versions!
  148. * This is a test of the interface, mainly. It stores things in an associative
  149. * array, which is not going to persist between program runs.
  150. *
  151. * @ingroup Cache
  152. */
  153. class HashBagOStuff extends BagOStuff {
  154. var $bag;
  155. function __construct() {
  156. $this->bag = array();
  157. }
  158. function _expire($key) {
  159. $et = $this->bag[$key][1];
  160. if(($et == 0) || ($et > time()))
  161. return false;
  162. $this->delete($key);
  163. return true;
  164. }
  165. function get($key) {
  166. if(!$this->bag[$key])
  167. return false;
  168. if($this->_expire($key))
  169. return false;
  170. return $this->bag[$key][0];
  171. }
  172. function set($key,$value,$exptime=0) {
  173. $this->bag[$key] = array( $value, BagOStuff::convertExpiry( $exptime ) );
  174. }
  175. function delete($key,$time=0) {
  176. if(!$this->bag[$key])
  177. return false;
  178. unset($this->bag[$key]);
  179. return true;
  180. }
  181. function keys() {
  182. return array_keys( $this->bag );
  183. }
  184. }
  185. /**
  186. * Generic class to store objects in a database
  187. *
  188. * @ingroup Cache
  189. */
  190. abstract class SqlBagOStuff extends BagOStuff {
  191. var $table;
  192. var $lastexpireall = 0;
  193. /**
  194. * Constructor
  195. *
  196. * @param $tablename String: name of the table to use
  197. */
  198. function __construct($tablename = 'objectcache') {
  199. $this->table = $tablename;
  200. }
  201. function get($key) {
  202. /* expire old entries if any */
  203. $this->garbageCollect();
  204. $res = $this->_query(
  205. "SELECT value,exptime FROM $0 WHERE keyname='$1'", $key);
  206. if(!$res) {
  207. $this->_debug("get: ** error: " . $this->_dberror($res) . " **");
  208. return false;
  209. }
  210. if($row=$this->_fetchobject($res)) {
  211. $this->_debug("get: retrieved data; exp time is " . $row->exptime);
  212. if ( $row->exptime != $this->_maxdatetime() &&
  213. wfTimestamp( TS_UNIX, $row->exptime ) < time() )
  214. {
  215. $this->_debug("get: key has expired, deleting");
  216. $this->delete($key);
  217. return false;
  218. }
  219. return $this->_unserialize($this->_blobdecode($row->value));
  220. } else {
  221. $this->_debug('get: no matching rows');
  222. }
  223. return false;
  224. }
  225. function set($key,$value,$exptime=0) {
  226. if ( $this->_readonly() ) {
  227. return false;
  228. }
  229. $exptime = intval($exptime);
  230. if($exptime < 0) $exptime = 0;
  231. if($exptime == 0) {
  232. $exp = $this->_maxdatetime();
  233. } else {
  234. if($exptime < 3.16e8) # ~10 years
  235. $exptime += time();
  236. $exp = $this->_fromunixtime($exptime);
  237. }
  238. $this->_begin();
  239. $this->_query(
  240. "DELETE FROM $0 WHERE keyname='$1'", $key );
  241. $this->_doinsert($this->getTableName(), array(
  242. 'keyname' => $key,
  243. 'value' => $this->_blobencode($this->_serialize($value)),
  244. 'exptime' => $exp
  245. ));
  246. $this->_commit();
  247. return true; /* ? */
  248. }
  249. function delete($key,$time=0) {
  250. if ( $this->_readonly() ) {
  251. return false;
  252. }
  253. $this->_begin();
  254. $this->_query(
  255. "DELETE FROM $0 WHERE keyname='$1'", $key );
  256. $this->_commit();
  257. return true; /* ? */
  258. }
  259. function keys() {
  260. $res = $this->_query( "SELECT keyname FROM $0" );
  261. if(!$res) {
  262. $this->_debug("keys: ** error: " . $this->_dberror($res) . " **");
  263. return array();
  264. }
  265. $result = array();
  266. while( $row = $this->_fetchobject($res) ) {
  267. $result[] = $row->keyname;
  268. }
  269. return $result;
  270. }
  271. function getTableName() {
  272. return $this->table;
  273. }
  274. function _query($sql) {
  275. $reps = func_get_args();
  276. $reps[0] = $this->getTableName();
  277. // ewwww
  278. for($i=0;$i<count($reps);$i++) {
  279. $sql = str_replace(
  280. '$' . $i,
  281. $i > 0 ? $this->_strencode($reps[$i]) : $reps[$i],
  282. $sql);
  283. }
  284. $res = $this->_doquery($sql);
  285. if($res == false) {
  286. $this->_debug('query failed: ' . $this->_dberror($res));
  287. }
  288. return $res;
  289. }
  290. function _strencode($str) {
  291. /* Protect strings in SQL */
  292. return str_replace( "'", "''", $str );
  293. }
  294. function _blobencode($str) {
  295. return $str;
  296. }
  297. function _blobdecode($str) {
  298. return $str;
  299. }
  300. abstract function _doinsert($table, $vals);
  301. abstract function _doquery($sql);
  302. abstract function _readonly();
  303. function _begin() {}
  304. function _commit() {}
  305. function _freeresult($result) {
  306. /* stub */
  307. return false;
  308. }
  309. function _dberror($result) {
  310. /* stub */
  311. return 'unknown error';
  312. }
  313. abstract function _maxdatetime();
  314. abstract function _fromunixtime($ts);
  315. function garbageCollect() {
  316. /* Ignore 99% of requests */
  317. if ( !mt_rand( 0, 100 ) ) {
  318. $nowtime = time();
  319. /* Avoid repeating the delete within a few seconds */
  320. if ( $nowtime > ($this->lastexpireall + 1) ) {
  321. $this->lastexpireall = $nowtime;
  322. $this->expireall();
  323. }
  324. }
  325. }
  326. function expireall() {
  327. /* Remove any items that have expired */
  328. if ( $this->_readonly() ) {
  329. return false;
  330. }
  331. $now = $this->_fromunixtime( time() );
  332. $this->_begin();
  333. $this->_query( "DELETE FROM $0 WHERE exptime < '$now'" );
  334. $this->_commit();
  335. }
  336. function deleteall() {
  337. /* Clear *all* items from cache table */
  338. if ( $this->_readonly() ) {
  339. return false;
  340. }
  341. $this->_begin();
  342. $this->_query( "DELETE FROM $0" );
  343. $this->_commit();
  344. }
  345. /**
  346. * Serialize an object and, if possible, compress the representation.
  347. * On typical message and page data, this can provide a 3X decrease
  348. * in storage requirements.
  349. *
  350. * @param $data mixed
  351. * @return string
  352. */
  353. function _serialize( &$data ) {
  354. $serial = serialize( $data );
  355. if( function_exists( 'gzdeflate' ) ) {
  356. return gzdeflate( $serial );
  357. } else {
  358. return $serial;
  359. }
  360. }
  361. /**
  362. * Unserialize and, if necessary, decompress an object.
  363. * @param $serial string
  364. * @return mixed
  365. */
  366. function _unserialize( $serial ) {
  367. if( function_exists( 'gzinflate' ) ) {
  368. $decomp = @gzinflate( $serial );
  369. if( false !== $decomp ) {
  370. $serial = $decomp;
  371. }
  372. }
  373. $ret = unserialize( $serial );
  374. return $ret;
  375. }
  376. }
  377. /**
  378. * Stores objects in the main database of the wiki
  379. *
  380. * @ingroup Cache
  381. */
  382. class MediaWikiBagOStuff extends SqlBagOStuff {
  383. var $tableInitialised = false;
  384. var $lb, $db;
  385. function _getDB(){
  386. if ( !isset( $this->lb ) ) {
  387. $this->lb = wfGetLBFactory()->newMainLB();
  388. $this->db = $this->lb->getConnection( DB_MASTER );
  389. $this->db->clearFlag( DBO_TRX );
  390. }
  391. return $this->db;
  392. }
  393. function _begin() {
  394. $this->_getDB()->begin();
  395. }
  396. function _commit() {
  397. $this->_getDB()->commit();
  398. }
  399. function _doquery($sql) {
  400. return $this->_getDB()->query( $sql, __METHOD__ );
  401. }
  402. function _doinsert($t, $v) {
  403. return $this->_getDB()->insert($t, $v, __METHOD__, array( 'IGNORE' ) );
  404. }
  405. function _fetchobject($result) {
  406. return $this->_getDB()->fetchObject($result);
  407. }
  408. function _freeresult($result) {
  409. return $this->_getDB()->freeResult($result);
  410. }
  411. function _dberror($result) {
  412. return $this->_getDB()->lastError();
  413. }
  414. function _maxdatetime() {
  415. if ( time() > 0x7fffffff ) {
  416. return $this->_fromunixtime( 1<<62 );
  417. } else {
  418. return $this->_fromunixtime( 0x7fffffff );
  419. }
  420. }
  421. function _fromunixtime($ts) {
  422. return $this->_getDB()->timestamp($ts);
  423. }
  424. /***
  425. * Note -- this should *not* check wfReadOnly().
  426. * Read-only mode has been repurposed from the original
  427. * "nothing must write to the database" to "users should not
  428. * be able to edit or alter anything user-visible".
  429. *
  430. * Backend bits like the object cache should continue
  431. * to work in this mode, otherwise things will blow up
  432. * like the message cache failing to save its state,
  433. * causing long delays (bug 11533).
  434. */
  435. function _readonly(){
  436. return false;
  437. }
  438. function _strencode($s) {
  439. return $this->_getDB()->strencode($s);
  440. }
  441. function _blobencode($s) {
  442. return $this->_getDB()->encodeBlob($s);
  443. }
  444. function _blobdecode($s) {
  445. return $this->_getDB()->decodeBlob($s);
  446. }
  447. function getTableName() {
  448. if ( !$this->tableInitialised ) {
  449. $dbw = $this->_getDB();
  450. /* This is actually a hack, we should be able
  451. to use Language classes here... or not */
  452. if (!$dbw)
  453. throw new MWException("Could not connect to database");
  454. $this->table = $dbw->tableName( $this->table );
  455. $this->tableInitialised = true;
  456. }
  457. return $this->table;
  458. }
  459. }
  460. /**
  461. * This is a wrapper for Turck MMCache's shared memory functions.
  462. *
  463. * You can store objects with mmcache_put() and mmcache_get(), but Turck seems
  464. * to use a weird custom serializer that randomly segfaults. So we wrap calls
  465. * with serialize()/unserialize().
  466. *
  467. * The thing I noticed about the Turck serialized data was that unlike ordinary
  468. * serialize(), it contained the names of methods, and judging by the amount of
  469. * binary data, perhaps even the bytecode of the methods themselves. It may be
  470. * that Turck's serializer is faster, so a possible future extension would be
  471. * to use it for arrays but not for objects.
  472. *
  473. * @ingroup Cache
  474. */
  475. class TurckBagOStuff extends BagOStuff {
  476. function get($key) {
  477. $val = mmcache_get( $key );
  478. if ( is_string( $val ) ) {
  479. $val = unserialize( $val );
  480. }
  481. return $val;
  482. }
  483. function set($key, $value, $exptime=0) {
  484. mmcache_put( $key, serialize( $value ), $exptime );
  485. return true;
  486. }
  487. function delete($key, $time=0) {
  488. mmcache_rm( $key );
  489. return true;
  490. }
  491. function lock($key, $waitTimeout = 0 ) {
  492. mmcache_lock( $key );
  493. return true;
  494. }
  495. function unlock($key) {
  496. mmcache_unlock( $key );
  497. return true;
  498. }
  499. }
  500. /**
  501. * This is a wrapper for APC's shared memory functions
  502. *
  503. * @ingroup Cache
  504. */
  505. class APCBagOStuff extends BagOStuff {
  506. function get($key) {
  507. $val = apc_fetch($key);
  508. if ( is_string( $val ) ) {
  509. $val = unserialize( $val );
  510. }
  511. return $val;
  512. }
  513. function set($key, $value, $exptime=0) {
  514. apc_store($key, serialize($value), $exptime);
  515. return true;
  516. }
  517. function delete($key, $time=0) {
  518. apc_delete($key);
  519. return true;
  520. }
  521. }
  522. /**
  523. * This is a wrapper for eAccelerator's shared memory functions.
  524. *
  525. * This is basically identical to the Turck MMCache version,
  526. * mostly because eAccelerator is based on Turck MMCache.
  527. *
  528. * @ingroup Cache
  529. */
  530. class eAccelBagOStuff extends BagOStuff {
  531. function get($key) {
  532. $val = eaccelerator_get( $key );
  533. if ( is_string( $val ) ) {
  534. $val = unserialize( $val );
  535. }
  536. return $val;
  537. }
  538. function set($key, $value, $exptime=0) {
  539. eaccelerator_put( $key, serialize( $value ), $exptime );
  540. return true;
  541. }
  542. function delete($key, $time=0) {
  543. eaccelerator_rm( $key );
  544. return true;
  545. }
  546. function lock($key, $waitTimeout = 0 ) {
  547. eaccelerator_lock( $key );
  548. return true;
  549. }
  550. function unlock($key) {
  551. eaccelerator_unlock( $key );
  552. return true;
  553. }
  554. }
  555. /**
  556. * Wrapper for XCache object caching functions; identical interface
  557. * to the APC wrapper
  558. *
  559. * @ingroup Cache
  560. */
  561. class XCacheBagOStuff extends BagOStuff {
  562. /**
  563. * Get a value from the XCache object cache
  564. *
  565. * @param $key String: cache key
  566. * @return mixed
  567. */
  568. public function get( $key ) {
  569. $val = xcache_get( $key );
  570. if( is_string( $val ) )
  571. $val = unserialize( $val );
  572. return $val;
  573. }
  574. /**
  575. * Store a value in the XCache object cache
  576. *
  577. * @param $key String: cache key
  578. * @param $value Mixed: object to store
  579. * @param $expire Int: expiration time
  580. * @return bool
  581. */
  582. public function set( $key, $value, $expire = 0 ) {
  583. xcache_set( $key, serialize( $value ), $expire );
  584. return true;
  585. }
  586. /**
  587. * Remove a value from the XCache object cache
  588. *
  589. * @param $key String: cache key
  590. * @param $time Int: not used in this implementation
  591. * @return bool
  592. */
  593. public function delete( $key, $time = 0 ) {
  594. xcache_unset( $key );
  595. return true;
  596. }
  597. }
  598. /**
  599. * @todo document
  600. * @ingroup Cache
  601. */
  602. class DBABagOStuff extends BagOStuff {
  603. var $mHandler, $mFile, $mReader, $mWriter, $mDisabled;
  604. function __construct( $handler = 'db3', $dir = false ) {
  605. if ( $dir === false ) {
  606. global $wgTmpDirectory;
  607. $dir = $wgTmpDirectory;
  608. }
  609. $this->mFile = "$dir/mw-cache-" . wfWikiID();
  610. $this->mFile .= '.db';
  611. wfDebug( __CLASS__.": using cache file {$this->mFile}\n" );
  612. $this->mHandler = $handler;
  613. }
  614. /**
  615. * Encode value and expiry for storage
  616. */
  617. function encode( $value, $expiry ) {
  618. # Convert to absolute time
  619. $expiry = BagOStuff::convertExpiry( $expiry );
  620. return sprintf( '%010u', intval( $expiry ) ) . ' ' . serialize( $value );
  621. }
  622. /**
  623. * @return list containing value first and expiry second
  624. */
  625. function decode( $blob ) {
  626. if ( !is_string( $blob ) ) {
  627. return array( null, 0 );
  628. } else {
  629. return array(
  630. unserialize( substr( $blob, 11 ) ),
  631. intval( substr( $blob, 0, 10 ) )
  632. );
  633. }
  634. }
  635. function getReader() {
  636. if ( file_exists( $this->mFile ) ) {
  637. $handle = dba_open( $this->mFile, 'rl', $this->mHandler );
  638. } else {
  639. $handle = $this->getWriter();
  640. }
  641. if ( !$handle ) {
  642. wfDebug( "Unable to open DBA cache file {$this->mFile}\n" );
  643. }
  644. return $handle;
  645. }
  646. function getWriter() {
  647. $handle = dba_open( $this->mFile, 'cl', $this->mHandler );
  648. if ( !$handle ) {
  649. wfDebug( "Unable to open DBA cache file {$this->mFile}\n" );
  650. }
  651. return $handle;
  652. }
  653. function get( $key ) {
  654. wfProfileIn( __METHOD__ );
  655. wfDebug( __METHOD__."($key)\n" );
  656. $handle = $this->getReader();
  657. if ( !$handle ) {
  658. return null;
  659. }
  660. $val = dba_fetch( $key, $handle );
  661. list( $val, $expiry ) = $this->decode( $val );
  662. # Must close ASAP because locks are held
  663. dba_close( $handle );
  664. if ( !is_null( $val ) && $expiry && $expiry < time() ) {
  665. # Key is expired, delete it
  666. $handle = $this->getWriter();
  667. dba_delete( $key, $handle );
  668. dba_close( $handle );
  669. wfDebug( __METHOD__.": $key expired\n" );
  670. $val = null;
  671. }
  672. wfProfileOut( __METHOD__ );
  673. return $val;
  674. }
  675. function set( $key, $value, $exptime=0 ) {
  676. wfProfileIn( __METHOD__ );
  677. wfDebug( __METHOD__."($key)\n" );
  678. $blob = $this->encode( $value, $exptime );
  679. $handle = $this->getWriter();
  680. if ( !$handle ) {
  681. return false;
  682. }
  683. $ret = dba_replace( $key, $blob, $handle );
  684. dba_close( $handle );
  685. wfProfileOut( __METHOD__ );
  686. return $ret;
  687. }
  688. function delete( $key, $time = 0 ) {
  689. wfProfileIn( __METHOD__ );
  690. wfDebug( __METHOD__."($key)\n" );
  691. $handle = $this->getWriter();
  692. if ( !$handle ) {
  693. return false;
  694. }
  695. $ret = dba_delete( $key, $handle );
  696. dba_close( $handle );
  697. wfProfileOut( __METHOD__ );
  698. return $ret;
  699. }
  700. function add( $key, $value, $exptime = 0 ) {
  701. wfProfileIn( __METHOD__ );
  702. $blob = $this->encode( $value, $exptime );
  703. $handle = $this->getWriter();
  704. if ( !$handle ) {
  705. return false;
  706. }
  707. $ret = dba_insert( $key, $blob, $handle );
  708. # Insert failed, check to see if it failed due to an expired key
  709. if ( !$ret ) {
  710. list( $value, $expiry ) = $this->decode( dba_fetch( $key, $handle ) );
  711. if ( $expiry < time() ) {
  712. # Yes expired, delete and try again
  713. dba_delete( $key, $handle );
  714. $ret = dba_insert( $key, $blob, $handle );
  715. # This time if it failed then it will be handled by the caller like any other race
  716. }
  717. }
  718. dba_close( $handle );
  719. wfProfileOut( __METHOD__ );
  720. return $ret;
  721. }
  722. function keys() {
  723. $reader = $this->getReader();
  724. $k1 = dba_firstkey( $reader );
  725. if( !$k1 ) {
  726. return array();
  727. }
  728. $result[] = $k1;
  729. while( $key = dba_nextkey( $reader ) ) {
  730. $result[] = $key;
  731. }
  732. return $result;
  733. }
  734. }