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