MediaWiki  master
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 resuse 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 
00109         public function get( $key ) {
00110                 wfProfileIn( __METHOD__ );
00111                 $this->debugLog( "get($key)" );
00112                 $value = $this->checkResult( $key, parent::get( $key ) );
00113                 wfProfileOut( __METHOD__ );
00114                 return $value;
00115         }
00116 
00123         public function set( $key, $value, $exptime = 0 ) {
00124                 $this->debugLog( "set($key)" );
00125                 return $this->checkResult( $key, parent::set( $key, $value, $exptime ) );
00126         }
00127 
00133         public function delete( $key, $time = 0 ) {
00134                 $this->debugLog( "delete($key)" );
00135                 $result = parent::delete( $key, $time );
00136                 if ( $result === false && $this->client->getResultCode() === Memcached::RES_NOTFOUND ) {
00137                         // "Not found" is counted as success in our interface
00138                         return true;
00139                 } else {
00140                         return $this->checkResult( $key, $result );
00141                 }
00142         }
00143 
00150         public function add( $key, $value, $exptime = 0 ) {
00151                 $this->debugLog( "add($key)" );
00152                 return $this->checkResult( $key, parent::add( $key, $value, $exptime ) );
00153         }
00154 
00161         public function replace( $key, $value, $exptime = 0 ) {
00162                 $this->debugLog( "replace($key)" );
00163                 return $this->checkResult( $key, parent::replace( $key, $value, $exptime ) );
00164         }
00165 
00171         public function incr( $key, $value = 1 ) {
00172                 $this->debugLog( "incr($key)" );
00173                 $result = $this->client->increment( $key, $value );
00174                 return $this->checkResult( $key, $result );
00175         }
00176 
00182         public function decr( $key, $value = 1 ) {
00183                 $this->debugLog( "decr($key)" );
00184                 $result = $this->client->decrement( $key, $value );
00185                 return $this->checkResult( $key, $result );
00186         }
00187 
00199         protected function checkResult( $key, $result ) {
00200                 if ( $result !== false ) {
00201                         return $result;
00202                 }
00203                 switch ( $this->client->getResultCode() ) {
00204                         case Memcached::RES_SUCCESS:
00205                                 break;
00206                         case Memcached::RES_DATA_EXISTS:
00207                         case Memcached::RES_NOTSTORED:
00208                         case Memcached::RES_NOTFOUND:
00209                                 $this->debugLog( "result: " . $this->client->getResultMessage() );
00210                                 break;
00211                         default:
00212                                 $msg = $this->client->getResultMessage();
00213                                 if ( $key !== false ) {
00214                                         $server = $this->client->getServerByKey( $key );
00215                                         $serverName = "{$server['host']}:{$server['port']}";
00216                                         $msg = "Memcached error for key \"$key\" on server \"$serverName\": $msg";
00217                                 } else {
00218                                         $msg = "Memcached error: $msg";
00219                                 }
00220                                 wfDebugLog( 'memcached-serious', $msg );
00221                 }
00222                 return $result;
00223         }
00224 
00229         public function getMulti( array $keys ) {
00230                 wfProfileIn( __METHOD__ );
00231                 $this->debugLog( 'getMulti(' . implode( ', ', $keys ) . ')' );
00232                 $callback = array( $this, 'encodeKey' );
00233                 $result = $this->client->getMulti( array_map( $callback, $keys ) );
00234                 wfProfileOut( __METHOD__ );
00235                 return $this->checkResult( false, $result );
00236         }
00237 
00238         /* NOTE: there is no cas() method here because it is currently not supported
00239          * by the BagOStuff interface and other BagOStuff subclasses, such as
00240          * SqlBagOStuff.
00241          */
00242 }