MediaWiki  REL1_21
MemcachedPeclBagOStuff.php
Go to the documentation of this file.
00001 <?php
00029 class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
00030 
00044         function __construct( $params ) {
00045                 $params = $this->applyDefaultParams( $params );
00046 
00047                 if ( $params['persistent'] ) {
00048                         // The pool ID must be unique to the server/option combination.
00049                         // The Memcached object is essentially shared for each pool ID.
00050                         // We can only reuse a pool ID if we keep the config consistent.
00051                         $this->client = new Memcached( md5( serialize( $params ) ) );
00052                         if ( count( $this->client->getServerList() ) ) {
00053                                 wfDebug( __METHOD__ . ": persistent Memcached object already loaded.\n" );
00054                                 return; // already initialized; don't add duplicate servers
00055                         }
00056                 } else {
00057                         $this->client = new Memcached;
00058                 }
00059 
00060                 if ( !isset( $params['serializer'] ) ) {
00061                         $params['serializer'] = 'php';
00062                 }
00063 
00064                 // The compression threshold is an undocumented php.ini option for some
00065                 // reason. There's probably not much harm in setting it globally, for
00066                 // compatibility with the settings for the PHP client.
00067                 ini_set( 'memcached.compression_threshold', $params['compress_threshold'] );
00068 
00069                 // Set timeouts
00070                 $this->client->setOption( Memcached::OPT_CONNECT_TIMEOUT, $params['connect_timeout'] * 1000 );
00071                 $this->client->setOption( Memcached::OPT_SEND_TIMEOUT, $params['timeout'] );
00072                 $this->client->setOption( Memcached::OPT_RECV_TIMEOUT, $params['timeout'] );
00073                 $this->client->setOption( Memcached::OPT_POLL_TIMEOUT, $params['timeout'] / 1000 );
00074 
00075                 // Set libketama mode since it's recommended by the documentation and
00076                 // is as good as any. There's no way to configure libmemcached to use
00077                 // hashes identical to the ones currently in use by the PHP client, and
00078                 // even implementing one of the libmemcached hashes in pure PHP for
00079                 // forwards compatibility would require MWMemcached::get_sock() to be
00080                 // rewritten.
00081                 $this->client->setOption( Memcached::OPT_LIBKETAMA_COMPATIBLE, true );
00082 
00083                 // Set the serializer
00084                 switch ( $params['serializer'] ) {
00085                         case 'php':
00086                                 $this->client->setOption( Memcached::OPT_SERIALIZER, Memcached::SERIALIZER_PHP );
00087                                 break;
00088                         case 'igbinary':
00089                                 if ( !Memcached::HAVE_IGBINARY ) {
00090                                         throw new MWException( __CLASS__.': the igbinary extension is not available ' .
00091                                                 'but igbinary serialization was requested.' );
00092                                 }
00093                                 $this->client->setOption( Memcached::OPT_SERIALIZER, Memcached::SERIALIZER_IGBINARY );
00094                                 break;
00095                         default:
00096                                 throw new MWException( __CLASS__.': invalid value for serializer parameter' );
00097                 }
00098                 $servers = array();
00099                 foreach ( $params['servers'] as $host ) {
00100                         $servers[] = IP::splitHostAndPort( $host ); // (ip, port)
00101                 }
00102                 $this->client->addServers( $servers );
00103         }
00104 
00110         public function get( $key, &$casToken = null ) {
00111                 wfProfileIn( __METHOD__ );
00112                 $this->debugLog( "get($key)" );
00113                 $result = $this->client->get( $this->encodeKey( $key ), null, $casToken );
00114                 $result = $this->checkResult( $key, $result );
00115                 wfProfileOut( __METHOD__ );
00116                 return $result;
00117         }
00118 
00125         public function set( $key, $value, $exptime = 0 ) {
00126                 $this->debugLog( "set($key)" );
00127                 return $this->checkResult( $key, parent::set( $key, $value, $exptime ) );
00128         }
00129 
00137         public function cas( $casToken, $key, $value, $exptime = 0 ) {
00138                 $this->debugLog( "cas($key)" );
00139                 return $this->checkResult( $key, parent::cas( $casToken, $key, $value, $exptime ) );
00140         }
00141 
00147         public function delete( $key, $time = 0 ) {
00148                 $this->debugLog( "delete($key)" );
00149                 $result = parent::delete( $key, $time );
00150                 if ( $result === false && $this->client->getResultCode() === Memcached::RES_NOTFOUND ) {
00151                         // "Not found" is counted as success in our interface
00152                         return true;
00153                 } else {
00154                         return $this->checkResult( $key, $result );
00155                 }
00156         }
00157 
00164         public function add( $key, $value, $exptime = 0 ) {
00165                 $this->debugLog( "add($key)" );
00166                 return $this->checkResult( $key, parent::add( $key, $value, $exptime ) );
00167         }
00168 
00175         public function replace( $key, $value, $exptime = 0 ) {
00176                 $this->debugLog( "replace($key)" );
00177                 return $this->checkResult( $key, parent::replace( $key, $value, $exptime ) );
00178         }
00179 
00185         public function incr( $key, $value = 1 ) {
00186                 $this->debugLog( "incr($key)" );
00187                 $result = $this->client->increment( $key, $value );
00188                 return $this->checkResult( $key, $result );
00189         }
00190 
00196         public function decr( $key, $value = 1 ) {
00197                 $this->debugLog( "decr($key)" );
00198                 $result = $this->client->decrement( $key, $value );
00199                 return $this->checkResult( $key, $result );
00200         }
00201 
00213         protected function checkResult( $key, $result ) {
00214                 if ( $result !== false ) {
00215                         return $result;
00216                 }
00217                 switch ( $this->client->getResultCode() ) {
00218                         case Memcached::RES_SUCCESS:
00219                                 break;
00220                         case Memcached::RES_DATA_EXISTS:
00221                         case Memcached::RES_NOTSTORED:
00222                         case Memcached::RES_NOTFOUND:
00223                                 $this->debugLog( "result: " . $this->client->getResultMessage() );
00224                                 break;
00225                         default:
00226                                 $msg = $this->client->getResultMessage();
00227                                 if ( $key !== false ) {
00228                                         $server = $this->client->getServerByKey( $key );
00229                                         $serverName = "{$server['host']}:{$server['port']}";
00230                                         $msg = "Memcached error for key \"$key\" on server \"$serverName\": $msg";
00231                                 } else {
00232                                         $msg = "Memcached error: $msg";
00233                                 }
00234                                 wfDebugLog( 'memcached-serious', $msg );
00235                 }
00236                 return $result;
00237         }
00238 
00243         public function getMulti( array $keys ) {
00244                 wfProfileIn( __METHOD__ );
00245                 $this->debugLog( 'getMulti(' . implode( ', ', $keys ) . ')' );
00246                 $callback = array( $this, 'encodeKey' );
00247                 $result = $this->client->getMulti( array_map( $callback, $keys ) );
00248                 wfProfileOut( __METHOD__ );
00249                 return $this->checkResult( false, $result );
00250         }
00251 
00252         /* NOTE: there is no cas() method here because it is currently not supported
00253          * by the BagOStuff interface and other BagOStuff subclasses, such as
00254          * SqlBagOStuff.
00255          */
00256 }