42 Memcached::OPT_NO_BLOCK =>
false,
43 Memcached::OPT_BUFFER_WRITES =>
false
47 Memcached::OPT_NO_BLOCK =>
true,
48 Memcached::OPT_BUFFER_WRITES =>
true
68 parent::__construct( $params );
72 'compress_threshold' => 1500,
73 'connect_timeout' => 0.5,
74 'serializer' =>
'php',
75 'use_binary_protocol' =>
false,
76 'allow_tcp_nagle_delay' =>
true
79 if ( $params[
'persistent'] ) {
83 $connectionPoolId = md5(
serialize( $params ) );
84 $syncClient =
new Memcached(
"$connectionPoolId-sync" );
86 $asyncClient =
new Memcached(
"$connectionPoolId-async" );
103 ini_set(
'memcached.compression_threshold', $params[
'compress_threshold'] );
116 if ( $client->getServerList() ) {
117 $this->logger->debug( __METHOD__ .
": pre-initialized client instance." );
122 $this->logger->debug( __METHOD__ .
": initializing new client instance." );
125 Memcached::OPT_NO_BLOCK =>
false,
126 Memcached::OPT_BUFFER_WRITES =>
false,
128 Memcached::OPT_BINARY_PROTOCOL => $params[
'use_binary_protocol'],
130 Memcached::OPT_CONNECT_TIMEOUT => $params[
'connect_timeout'] * 1000,
131 Memcached::OPT_SEND_TIMEOUT => $params[
'timeout'],
132 Memcached::OPT_RECV_TIMEOUT => $params[
'timeout'],
133 Memcached::OPT_POLL_TIMEOUT => $params[
'timeout'] / 1000,
135 Memcached::OPT_TCP_NODELAY => !$params[
'allow_tcp_nagle_delay'],
137 Memcached::OPT_LIBKETAMA_COMPATIBLE =>
true
139 if ( isset( $params[
'retry_timeout'] ) ) {
140 $options[Memcached::OPT_RETRY_TIMEOUT] = $params[
'retry_timeout'];
142 if ( isset( $params[
'server_failure_limit'] ) ) {
143 $options[Memcached::OPT_SERVER_FAILURE_LIMIT] = $params[
'server_failure_limit'];
145 if ( $params[
'serializer'] ===
'php' ) {
146 $options[Memcached::OPT_SERIALIZER] = Memcached::SERIALIZER_PHP;
147 } elseif ( $params[
'serializer'] ===
'igbinary' ) {
149 if ( !Memcached::HAVE_IGBINARY ) {
150 throw new RuntimeException(
151 __CLASS__ .
': the igbinary extension is not available ' .
152 'but igbinary serialization was requested.'
155 $options[Memcached::OPT_SERIALIZER] = Memcached::SERIALIZER_IGBINARY;
158 if ( !$client->setOptions( $options ) ) {
159 throw new RuntimeException(
160 "Invalid options: " . json_encode( $options, JSON_PRETTY_PRINT )
165 foreach ( $params[
'servers'] as $host ) {
166 if ( preg_match(
'/^\[(.+)\]:(\d+)$/', $host, $m ) ) {
167 $servers[] = [ $m[1], (int)$m[2] ];
168 } elseif ( preg_match(
'/^([^:]+):(\d+)$/', $host, $m ) ) {
169 $servers[] = [ $m[1], (int)$m[2] ];
171 $servers[] = [ $host, false ];
175 if ( !$client->addServers( $servers ) ) {
176 throw new RuntimeException(
"Failed to inject server address list" );
180 protected function doGet( $key, $flags = 0, &$casToken =
null ) {
181 $getToken = ( $casToken === self::PASS_BY_REF );
184 $this->
debug(
"get($key)" );
192 $flags = Memcached::GET_EXTENDED;
193 $res = $client->get( $routeKey,
null, $flags );
194 if ( is_array(
$res ) ) {
195 $result =
$res[
'value'];
196 $casToken =
$res[
'cas'];
201 $result = $client->get( $routeKey );
207 protected function doSet( $key, $value, $exptime = 0, $flags = 0 ) {
208 $this->
debug(
"set($key)" );
212 $result = $client->set( $routeKey, $value, $this->
fixExpiry( $exptime ) );
214 return ( $result ===
false && $client->getResultCode() === Memcached::RES_NOTSTORED )
220 protected function doCas( $casToken, $key, $value, $exptime = 0, $flags = 0 ) {
221 $this->
debug(
"cas($key)" );
234 $this->
debug(
"delete($key)" );
238 $result = $client->delete( $routeKey );
240 return ( $result ===
false && $client->getResultCode() === Memcached::RES_NOTFOUND )
246 protected function doAdd( $key, $value, $exptime = 0, $flags = 0 ) {
247 $this->
debug(
"add($key)" );
259 public function incr( $key, $value = 1, $flags = 0 ) {
260 $this->
debug(
"incr($key)" );
268 public function decr( $key, $value = 1, $flags = 0 ) {
269 $this->
debug(
"decr($key)" );
280 foreach ( $valueByKey as $value ) {
299 if ( $result !==
false ) {
304 switch ( $client->getResultCode() ) {
305 case Memcached::RES_SUCCESS:
307 case Memcached::RES_DATA_EXISTS:
308 case Memcached::RES_NOTSTORED:
309 case Memcached::RES_NOTFOUND:
310 $this->
debug(
"result: " . $client->getResultMessage() );
313 $msg = $client->getResultMessage();
315 if ( $key !==
false ) {
316 $server = $client->getServerByKey( $key );
317 $logCtx[
'memcached-server'] =
"{$server['host']}:{$server['port']}";
318 $logCtx[
'memcached-key'] = $key;
319 $msg =
"Memcached error for key \"{memcached-key}\" " .
320 "on server \"{memcached-server}\": $msg";
322 $msg =
"Memcached error: $msg";
324 $this->logger->error( $msg, $logCtx );
331 $this->
debug(
'getMulti(' . implode(
', ',
$keys ) .
')' );
334 foreach (
$keys as $key ) {
343 if ( is_array( $resByRouteKey ) ) {
345 foreach ( $resByRouteKey as $routeKey => $value ) {
355 protected function doSetMulti( array $data, $exptime = 0, $flags = 0 ) {
356 $this->
debug(
'setMulti(' . implode(
', ', array_keys( $data ) ) .
')' );
359 $dataByRouteKey = [];
360 foreach ( $data as $key => $value ) {
366 if ( $this->
fieldHasFlags( $flags, self::WRITE_BACKGROUND ) ) {
370 $result = @$client->setMulti( $dataByRouteKey, $exptime );
376 $result = @$client->setMulti( $dataByRouteKey, $exptime );
383 $this->
debug(
'deleteMulti(' . implode(
', ',
$keys ) .
')' );
386 foreach (
$keys as $key ) {
392 if ( $this->
fieldHasFlags( $flags, self::WRITE_BACKGROUND ) ) {
394 $resultArray = $client->deleteMulti( $routeKeys ) ?: [];
401 foreach ( $resultArray as $code ) {
402 if ( !in_array( $code, [
true, Memcached::RES_NOTFOUND ],
true ) ) {
412 $this->
debug(
"touch($key)" );
421 if ( is_int( $value ) ) {
425 $serializer = $this->syncClient->getOption( Memcached::OPT_SERIALIZER );
426 if ( $serializer === Memcached::SERIALIZER_PHP ) {
428 } elseif ( $serializer === Memcached::SERIALIZER_IGBINARY ) {
429 return igbinary_serialize( $value );
432 throw new UnexpectedValueException( __METHOD__ .
": got serializer '$serializer'." );
440 $serializer = $this->syncClient->getOption( Memcached::OPT_SERIALIZER );
441 if ( $serializer === Memcached::SERIALIZER_PHP ) {
443 } elseif ( $serializer === Memcached::SERIALIZER_IGBINARY ) {
444 return igbinary_unserialize( $value );
447 throw new UnexpectedValueException( __METHOD__ .
": got serializer '$serializer'." );
454 if ( $this->syncClientIsBuffering ) {
455 throw new RuntimeException(
"The main (unbuffered I/O) client is locked" );
458 if ( $this->hasUnflushedChanges ) {
460 $this->syncClient->fetch();
461 if ( $this->asyncClient ) {
462 $this->asyncClient->fetch();
464 $this->hasUnflushedChanges =
false;
474 if ( $this->asyncClient ) {
479 $this->syncClientIsBuffering =
true;
480 $this->syncClient->setOptions( self::$OPTS_ASYNC_WRITES );
489 $this->hasUnflushedChanges =
true;
491 if ( !$this->asyncClient ) {
493 $client->setOptions( self::$OPTS_SYNC_WRITES );
494 $this->syncClientIsBuffering =
false;
fieldHasFlags( $field, $flags)
guessSerialValueSize( $value, $depth=0, &$loops=0)
Estimate the size of a variable once serialized.
isInteger( $value)
Check if a value is an integer.
setLastError( $err)
Set the "last error" registry.
Base class for memcached clients.
validateKeyAndPrependRoute( $key)
A wrapper class for the PECL memcached client.
doAdd( $key, $value, $exptime=0, $flags=0)
Insert an item if it does not already exist.
bool $syncClientIsBuffering
Whether the non-buffering client is locked from use.
doGet( $key, $flags=0, &$casToken=null)
doGetMulti(array $keys, $flags=0)
Get an associative array containing the item for each of the keys that have items.
doCas( $casToken, $key, $value, $exptime=0, $flags=0)
Check and set an item.
doSet( $key, $value, $exptime=0, $flags=0)
Set an item.
incr( $key, $value=1, $flags=0)
Increase stored value of $key by $value while preserving its TTL.
doChangeTTL( $key, $exptime, $flags)
doDeleteMulti(array $keys, $flags=0)
decr( $key, $value=1, $flags=0)
Decrease stored value of $key by $value while preserving its TTL.
releaseAsyncClient( $client)
setNewPreparedValues(array $valueByKey)
Make a "generic" reversible cache key from the given components.
initializeClient(Memcached $client, array $params, array $options)
Initialize the client only if needed and reuse it otherwise.
bool $hasUnflushedChanges
Whether the non-buffering client should be flushed before use.
static array $OPTS_SYNC_WRITES
Memcached options.
doSetMulti(array $data, $exptime=0, $flags=0)
static array $OPTS_ASYNC_WRITES
Memcached options.
checkResult( $key, $result)
Check the return value from a client method call and take any necessary action.
doDelete( $key, $flags=0)
Delete an item.
__construct( $params)
Available parameters are:
Memcached null $asyncClient