MediaWiki
REL1_24
|
00001 <?php 00029 class MemcachedPeclBagOStuff extends MemcachedBagOStuff { 00030 00047 function __construct( $params ) { 00048 $params = $this->applyDefaultParams( $params ); 00049 00050 if ( $params['persistent'] ) { 00051 // The pool ID must be unique to the server/option combination. 00052 // The Memcached object is essentially shared for each pool ID. 00053 // We can only reuse a pool ID if we keep the config consistent. 00054 $this->client = new Memcached( md5( serialize( $params ) ) ); 00055 if ( count( $this->client->getServerList() ) ) { 00056 wfDebug( __METHOD__ . ": persistent Memcached object already loaded.\n" ); 00057 return; // already initialized; don't add duplicate servers 00058 } 00059 } else { 00060 $this->client = new Memcached; 00061 } 00062 00063 if ( !isset( $params['serializer'] ) ) { 00064 $params['serializer'] = 'php'; 00065 } 00066 00067 if ( isset( $params['retry_timeout'] ) ) { 00068 $this->client->setOption( Memcached::OPT_RETRY_TIMEOUT, $params['retry_timeout'] ); 00069 } 00070 00071 if ( isset( $params['server_failure_limit'] ) ) { 00072 $this->client->setOption( Memcached::OPT_SERVER_FAILURE_LIMIT, $params['server_failure_limit'] ); 00073 } 00074 00075 // The compression threshold is an undocumented php.ini option for some 00076 // reason. There's probably not much harm in setting it globally, for 00077 // compatibility with the settings for the PHP client. 00078 ini_set( 'memcached.compression_threshold', $params['compress_threshold'] ); 00079 00080 // Set timeouts 00081 $this->client->setOption( Memcached::OPT_CONNECT_TIMEOUT, $params['connect_timeout'] * 1000 ); 00082 $this->client->setOption( Memcached::OPT_SEND_TIMEOUT, $params['timeout'] ); 00083 $this->client->setOption( Memcached::OPT_RECV_TIMEOUT, $params['timeout'] ); 00084 $this->client->setOption( Memcached::OPT_POLL_TIMEOUT, $params['timeout'] / 1000 ); 00085 00086 // Set libketama mode since it's recommended by the documentation and 00087 // is as good as any. There's no way to configure libmemcached to use 00088 // hashes identical to the ones currently in use by the PHP client, and 00089 // even implementing one of the libmemcached hashes in pure PHP for 00090 // forwards compatibility would require MWMemcached::get_sock() to be 00091 // rewritten. 00092 $this->client->setOption( Memcached::OPT_LIBKETAMA_COMPATIBLE, true ); 00093 00094 // Set the serializer 00095 switch ( $params['serializer'] ) { 00096 case 'php': 00097 $this->client->setOption( Memcached::OPT_SERIALIZER, Memcached::SERIALIZER_PHP ); 00098 break; 00099 case 'igbinary': 00100 if ( !Memcached::HAVE_IGBINARY ) { 00101 throw new MWException( __CLASS__ . ': the igbinary extension is not available ' . 00102 'but igbinary serialization was requested.' ); 00103 } 00104 $this->client->setOption( Memcached::OPT_SERIALIZER, Memcached::SERIALIZER_IGBINARY ); 00105 break; 00106 default: 00107 throw new MWException( __CLASS__ . ': invalid value for serializer parameter' ); 00108 } 00109 $servers = array(); 00110 foreach ( $params['servers'] as $host ) { 00111 $servers[] = IP::splitHostAndPort( $host ); // (ip, port) 00112 } 00113 $this->client->addServers( $servers ); 00114 } 00115 00121 public function get( $key, &$casToken = null ) { 00122 wfProfileIn( __METHOD__ ); 00123 $this->debugLog( "get($key)" ); 00124 $result = $this->client->get( $this->encodeKey( $key ), null, $casToken ); 00125 $result = $this->checkResult( $key, $result ); 00126 wfProfileOut( __METHOD__ ); 00127 return $result; 00128 } 00129 00136 public function set( $key, $value, $exptime = 0 ) { 00137 $this->debugLog( "set($key)" ); 00138 return $this->checkResult( $key, parent::set( $key, $value, $exptime ) ); 00139 } 00140 00148 public function cas( $casToken, $key, $value, $exptime = 0 ) { 00149 $this->debugLog( "cas($key)" ); 00150 return $this->checkResult( $key, parent::cas( $casToken, $key, $value, $exptime ) ); 00151 } 00152 00158 public function delete( $key, $time = 0 ) { 00159 $this->debugLog( "delete($key)" ); 00160 $result = parent::delete( $key, $time ); 00161 if ( $result === false && $this->client->getResultCode() === Memcached::RES_NOTFOUND ) { 00162 // "Not found" is counted as success in our interface 00163 return true; 00164 } else { 00165 return $this->checkResult( $key, $result ); 00166 } 00167 } 00168 00175 public function add( $key, $value, $exptime = 0 ) { 00176 $this->debugLog( "add($key)" ); 00177 return $this->checkResult( $key, parent::add( $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 $this->setLastError( BagOStuff::ERR_UNEXPECTED ); 00236 } 00237 return $result; 00238 } 00239 00244 public function getMulti( array $keys ) { 00245 wfProfileIn( __METHOD__ ); 00246 $this->debugLog( 'getMulti(' . implode( ', ', $keys ) . ')' ); 00247 $callback = array( $this, 'encodeKey' ); 00248 $result = $this->client->getMulti( array_map( $callback, $keys ) ); 00249 wfProfileOut( __METHOD__ ); 00250 $result = $result ?: array(); // must be an array 00251 return $this->checkResult( false, $result ); 00252 } 00253 00259 public function setMulti( array $data, $exptime = 0 ) { 00260 wfProfileIn( __METHOD__ ); 00261 foreach ( $data as $key => $value ) { 00262 $encKey = $this->encodeKey( $key ); 00263 if ( $encKey !== $key ) { 00264 $data[$encKey] = $value; 00265 unset( $data[$key] ); 00266 } 00267 } 00268 $this->debugLog( 'setMulti(' . implode( ', ', array_keys( $data ) ) . ')' ); 00269 $result = $this->client->setMulti( $data, $this->fixExpiry( $exptime ) ); 00270 wfProfileOut( __METHOD__ ); 00271 return $this->checkResult( false, $result ); 00272 } 00273 }