MediaWiki
master
|
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 }