MediaWiki  REL1_22
DBABagOStuff.php
Go to the documentation of this file.
00001 <?php
00035 class DBABagOStuff extends BagOStuff {
00036     var $mHandler, $mFile, $mReader, $mWriter, $mDisabled;
00037 
00041     public function __construct( $params ) {
00042         global $wgDBAhandler;
00043 
00044         if ( !isset( $params['dir'] ) ) {
00045             $params['dir'] = wfTempDir();
00046         }
00047 
00048         $this->mFile = $params['dir'] . '/mw-cache-' . wfWikiID() . '.db';
00049         wfDebug( __CLASS__ . ": using cache file {$this->mFile}\n" );
00050         $this->mHandler = $wgDBAhandler;
00051     }
00052 
00060     protected function encode( $value, $expiry ) {
00061         # Convert to absolute time
00062         $expiry = $this->convertExpiry( $expiry );
00063 
00064         return sprintf( '%010u', intval( $expiry ) ) . ' ' . serialize( $value );
00065     }
00066 
00071     protected function decode( $blob ) {
00072         if ( !is_string( $blob ) ) {
00073             return array( false, 0 );
00074         } else {
00075             return array(
00076                 unserialize( substr( $blob, 11 ) ),
00077                 intval( substr( $blob, 0, 10 ) )
00078             );
00079         }
00080     }
00081 
00085     protected function getReader() {
00086         if ( file_exists( $this->mFile ) ) {
00087             $handle = dba_open( $this->mFile, 'rl', $this->mHandler );
00088         } else {
00089             $handle = $this->getWriter();
00090         }
00091 
00092         if ( !$handle ) {
00093             wfDebug( "Unable to open DBA cache file {$this->mFile}\n" );
00094         }
00095 
00096         return $handle;
00097     }
00098 
00102     protected function getWriter() {
00103         $handle = dba_open( $this->mFile, 'cl', $this->mHandler );
00104 
00105         if ( !$handle ) {
00106             wfDebug( "Unable to open DBA cache file {$this->mFile}\n" );
00107         }
00108 
00109         return $handle;
00110     }
00111 
00117     public function get( $key, &$casToken = null ) {
00118         wfProfileIn( __METHOD__ );
00119         wfDebug( __METHOD__ . "($key)\n" );
00120 
00121         $handle = $this->getReader();
00122         if ( !$handle ) {
00123             wfProfileOut( __METHOD__ );
00124             return false;
00125         }
00126 
00127         $val = dba_fetch( $key, $handle );
00128         list( $val, $expiry ) = $this->decode( $val );
00129 
00130         # Must close ASAP because locks are held
00131         dba_close( $handle );
00132 
00133         if ( $val !== false && $expiry && $expiry < time() ) {
00134             # Key is expired, delete it
00135             $handle = $this->getWriter();
00136             dba_delete( $key, $handle );
00137             dba_close( $handle );
00138             wfDebug( __METHOD__ . ": $key expired\n" );
00139             $val = false;
00140         }
00141 
00142         $casToken = $val;
00143 
00144         wfProfileOut( __METHOD__ );
00145 
00146         return $val;
00147     }
00148 
00155     public function set( $key, $value, $exptime = 0 ) {
00156         wfProfileIn( __METHOD__ );
00157         wfDebug( __METHOD__ . "($key)\n" );
00158 
00159         $blob = $this->encode( $value, $exptime );
00160 
00161         $handle = $this->getWriter();
00162         if ( !$handle ) {
00163             wfProfileOut( __METHOD__ );
00164             return false;
00165         }
00166 
00167         $ret = dba_replace( $key, $blob, $handle );
00168         dba_close( $handle );
00169 
00170         wfProfileOut( __METHOD__ );
00171         return $ret;
00172     }
00173 
00181     public function cas( $casToken, $key, $value, $exptime = 0 ) {
00182         wfProfileIn( __METHOD__ );
00183         wfDebug( __METHOD__ . "($key)\n" );
00184 
00185         $blob = $this->encode( $value, $exptime );
00186 
00187         $handle = $this->getWriter();
00188         if ( !$handle ) {
00189             wfProfileOut( __METHOD__ );
00190             return false;
00191         }
00192 
00193         // DBA is locked to any other write connection, so we can safely
00194         // compare the current & previous value before saving new value
00195         $val = dba_fetch( $key, $handle );
00196         list( $val, $exptime ) = $this->decode( $val );
00197         if ( $casToken !== $val ) {
00198             dba_close( $handle );
00199             wfProfileOut( __METHOD__ );
00200             return false;
00201         }
00202 
00203         $ret = dba_replace( $key, $blob, $handle );
00204         dba_close( $handle );
00205 
00206         wfProfileOut( __METHOD__ );
00207         return $ret;
00208     }
00209 
00215     public function delete( $key, $time = 0 ) {
00216         wfProfileIn( __METHOD__ );
00217         wfDebug( __METHOD__ . "($key)\n" );
00218 
00219         $handle = $this->getWriter();
00220         if ( !$handle ) {
00221             wfProfileOut( __METHOD__ );
00222             return false;
00223         }
00224 
00225         $ret = !dba_exists( $key, $handle ) || dba_delete( $key, $handle );
00226         dba_close( $handle );
00227 
00228         wfProfileOut( __METHOD__ );
00229         return $ret;
00230     }
00231 
00238     public function add( $key, $value, $exptime = 0 ) {
00239         wfProfileIn( __METHOD__ );
00240 
00241         $blob = $this->encode( $value, $exptime );
00242 
00243         $handle = $this->getWriter();
00244 
00245         if ( !$handle ) {
00246             wfProfileOut( __METHOD__ );
00247             return false;
00248         }
00249 
00250         $ret = dba_insert( $key, $blob, $handle );
00251 
00252         # Insert failed, check to see if it failed due to an expired key
00253         if ( !$ret ) {
00254             list( , $expiry ) = $this->decode( dba_fetch( $key, $handle ) );
00255 
00256             if ( $expiry && $expiry < time() ) {
00257                 # Yes expired, delete and try again
00258                 dba_delete( $key, $handle );
00259                 $ret = dba_insert( $key, $blob, $handle );
00260                 # This time if it failed then it will be handled by the caller like any other race
00261             }
00262         }
00263 
00264         dba_close( $handle );
00265 
00266         wfProfileOut( __METHOD__ );
00267         return $ret;
00268     }
00269 
00275     public function incr( $key, $step = 1 ) {
00276         wfProfileIn( __METHOD__ );
00277 
00278         $handle = $this->getWriter();
00279 
00280         if ( !$handle ) {
00281             wfProfileOut( __METHOD__ );
00282             return false;
00283         }
00284 
00285         list( $value, $expiry ) = $this->decode( dba_fetch( $key, $handle ) );
00286         if ( $value !== false ) {
00287             if ( $expiry && $expiry < time() ) {
00288                 # Key is expired, delete it
00289                 dba_delete( $key, $handle );
00290                 wfDebug( __METHOD__ . ": $key expired\n" );
00291                 $value = false;
00292             } else {
00293                 $value += $step;
00294                 $blob = $this->encode( $value, $expiry );
00295 
00296                 $ret = dba_replace( $key, $blob, $handle );
00297                 $value = $ret ? $value : false;
00298             }
00299         }
00300 
00301         dba_close( $handle );
00302 
00303         wfProfileOut( __METHOD__ );
00304 
00305         return ( $value === false ) ? false : (int)$value;
00306     }
00307 }