public function limitRate($key) { $cas = null; do { $info = $this->client->get($key, null, $cas); if (!$info) { return false; } $info['calls']++; $this->client->cas($cas, $key, $info); } while ($this->client->getResultCode() != \Memcached::RES_SUCCESS); return $this->getRateInfo($key); }
/** * Performs a "check and set" to store data. * * The set will be successful only if the no other request has updated the value since it was fetched since * this request. * * @link http://www.php.net/manual/en/memcached.cas.php * * @param float $cas_token Unique value associated with the existing item. Generated by memcached. * @param string $key The key under which to store the value. * @param mixed $value The value to store. * @param string $group The group value appended to the $key. * @param int $expiration The expiration time, defaults to 0. * @param string $server_key The key identifying the server to store the value on. * @param bool $byKey True to store in internal cache by key; false to not store by key * @return bool Returns TRUE on success or FALSE on failure. */ public function cas($cas_token, $key, $value, $group = 'default', $expiration = 0, $server_key = '', $byKey = false) { $derived_key = $this->buildKey($key, $group); $expiration = $this->sanitize_expiration($expiration); /** * If group is a non-Memcached group, save to runtime cache, not Memcached. Note * that since check and set cannot be emulated in the run time cache, this value * operation is treated as a normal "add" for no_mc_groups. */ if (in_array($group, $this->no_mc_groups)) { $this->add_to_internal_cache($derived_key, $value); return true; } // Save to Memcached if (false !== $byKey) { $result = $this->mc->casByKey($cas_token, $server_key, $derived_key, $value, $expiration); } else { $result = $this->mc->cas($cas_token, $derived_key, $value, $expiration); } // Store in runtime cache if cas was successful if (Memcached::RES_SUCCESS === $this->getResultCode()) { $this->add_to_internal_cache($derived_key, $value); } return $result; }
/** * Check and set item * * @param float $token * @param string $key * @param mixed $value * @param array $options * @return bool */ public function checkAndSetItem($token, $key, $value, array $options = array()) { $baseOptions = $this->getOptions(); if (!$baseOptions->getWritable()) { return false; } $this->normalizeOptions($options); $this->normalizeKey($key); $args = new ArrayObject(array('token' => &$token, 'key' => &$key, 'value' => &$value, 'options' => &$options)); try { $eventRs = $this->triggerPre(__FUNCTION__, $args); if ($eventRs->stopped()) { return $eventRs->last(); } $this->memcached->setOption(MemcachedResource::OPT_PREFIX_KEY, $options['namespace']); $expiration = $this->expirationTime($options['ttl']); $result = $this->memcached->cas($token, $key, $value, $expiration); if ($result === false) { $rsCode = $this->memcached->getResultCode(); if ($rsCode !== 0 && $rsCode != MemcachedResource::RES_DATA_EXISTS) { throw $this->getExceptionByResultCode($rsCode); } } return $this->triggerPost(__FUNCTION__, $args, $result); } catch (\Exception $e) { return $this->triggerException(__FUNCTION__, $args, $e); } }
/** * cas * @param $key * @param $val * @param $expireTime * @param $cas_token * @return bool */ public function cas($key, $val, $expireTime, &$cas_token) { $ret = $this->dbh->cas($cas_token, $key, $val, $expireTime); if ($ret === false) { $this->error('cas', $key); } return true; }
public function set($key, $value) { $this->get($key); if (!$value) { throw new StorageException('[STORAGE] Given value for "' . $this->getStorageName() . '::set" not valid! Key: ' . $key); } if ($this->memcachedObj->getResultCode() == \Memcached::RES_NOTFOUND) { $this->memcachedObj->add($key, $value); unset($this->casArray[$key]); } else { $this->memcachedObj->cas($this->casArray[$key], $key, $value); } $resultCode = $this->memcachedObj->getResultCode(); if ($resultCode != \Memcached::RES_SUCCESS) { throw new StorageException('[STORAGE] "' . $this->getStorageName() . '::set" failed!' . ' StorageRespCode: ' . $resultCode . ', Key: ' . $key . ', Cas: ' . (isset($this->casArray[$key]) ? $this->casArray[$key] : 'null')); } return $resultCode; }
/** * Internal method to set an item only if token matches * * @param mixed $token * @param string $normalizedKey * @param mixed $value * @return boolean * @throws Exception\ExceptionInterface * @see getItem() * @see setItem() */ protected function internalCheckAndSetItem(&$token, &$normalizedKey, &$value) { $expiration = $this->expirationTime(); $result = $this->memcached->cas($token, $normalizedKey, $value, $expiration); if ($result === false) { $rsCode = $this->memcached->getResultCode(); if ($rsCode !== 0 && $rsCode != MemcachedResource::RES_DATA_EXISTS) { throw $this->getExceptionByResultCode($rsCode); } } return $result; }
public function cas($key, $value) { if (($rValue = $this->memcached->get($key, null, $token)) !== false) { return $rValue; } if ($this->memcached->getResultCode() === Memcached::RES_NOTFOUND) { $this->memcached->add($key, $value); } else { $this->memcached->cas($token, $key, $value); } return $value; }
/** * Internal method to set an item only if token matches * * Options: * - ttl <float> * - The time-to-life * - namespace <string> * - The namespace to use * - tags <array> * - An array of tags * * @param mixed $token * @param string $normalizedKey * @param mixed $value * @param array $normalizedOptions * @return boolean * @throws Exception\ExceptionInterface * @see getItem() * @see setItem() */ protected function internalCheckAndSetItem(&$token, &$normalizedKey, &$value, array &$normalizedOptions) { $this->memcached->setOption(MemcachedResource::OPT_PREFIX_KEY, $normalizedOptions['namespace']); $expiration = $this->expirationTime($normalizedOptions['ttl']); $result = $this->memcached->cas($token, $normalizedKey, $value, $expiration); if ($result === false) { $rsCode = $this->memcached->getResultCode(); if ($rsCode !== 0 && $rsCode != MemcachedResource::RES_DATA_EXISTS) { throw $this->getExceptionByResultCode($rsCode); } } return $result; }
/** * Shared between increment/decrement: both have mostly the same logic * (decrement just increments a negative value), but need their validation * split up (increment won't accept negative values). * * @param string $key * @param int $offset * @param int $initial * @param int $expire * * @return int|bool */ protected function doIncrement($key, $offset, $initial, $expire) { $value = $this->get($key, $token); if ($value === false) { $this->set($key, $initial, $expire); return $initial; } if (!is_numeric($value) || $value < 0) { return false; } $value += $offset; // value can never be lower than 0 $value = max(0, $value); $success = $this->client->cas($token, $key, $value, $expire); return $success ? $value : false; }
/** * Set|update cache key. * * @since 151216 Memcached utilities. * * @param string $primary_key Primary key. * @param string|int $sub_key Sub-key to set. * @param string $value Value to cache (1MB max). * @param int $expires_after Expires after (in seconds). * * @return bool True on success. */ public function set(string $primary_key, $sub_key, $value, int $expires_after = 0) : bool { if (!$this->Pool) { return false; // Not possible. } $time = time(); $expires_after = max(0, $expires_after); $expires = $expires_after ? $time + $expires_after : 0; if (!($key = $this->key($primary_key, $sub_key))) { return false; // Fail; e.g., race condition. } elseif ($value === null || is_resource($value)) { throw $this->c::issue('Incompatible data type.'); } do { // Avoid race issues with a loop. $attempts = $attempts ?? 0; ++$attempts; // Counter. $this->Pool->get($key, null, $cas); if ($this->Pool->getResultCode() === \Memcached::RES_NOTFOUND) { if ($this->Pool->add($key, $value, $expires)) { return true; // All good; stop here. } } elseif ($this->Pool->cas($cas, $key, $value, $expires)) { return true; // All good; stop here. } $result_code = $this->Pool->getResultCode(); // } while ($attempts < $this::MAX_WRITE_ATTEMPTS && ($result_code === \Memcached::RES_NOTSTORED || $result_code === \Memcached::RES_DATA_EXISTS)); return false; // Fail; e.g., race condition or unexpected error. }
public function testCasFailure() { $request = new MemcacheSetRequest(); $item = $request->addItem(); $item->setKey("widgets_float"); $item->setValue("2"); $item->setFlags(6); // float $item->setCasId(12345); $item->setSetPolicy(SetPolicy::CAS); $item->setExpirationTime(30); $response = new MemcacheSetResponse(); $response->addSetStatus(SetStatusCode::EXISTS); $this->apiProxyMock->expectCall('memcache', 'Set', $request, $response); $memcached = new Memcached(); $memcached->setOption(Memcached::OPT_PREFIX_KEY, "widgets_"); $this->assertFalse($memcached->cas(12345, "float", 2.0, 30)); $this->assertEquals($memcached->getResultCode(), Memcached::RES_DATA_EXISTS); $this->apiProxyMock->verify(); }
<?php $m = new Memcached(); $m->addServer('127.0.0.1', 11211); do { $ips = $m->get('ips', null, $cas); if ($m->getResultCode() == Memcached::RES_NOTFOUND) { $ips = array($_SERVER['REMOTE_ADDR']); $m->add('ips', $ips); } else { $ips[] = $_SERVER['REMOTE_ADDR']; $m->cas($cas, 'ips', $ips); } } while ($m->getResultCode() != Memcached::RES_SUCCESS);
protected function cas($casToken, $key, $value, $exptime = 0) { return $this->client->cas($casToken, $this->validateKeyEncoding($key), $value, $this->fixExpiry($exptime)); }
<?php $memc = new Memcached(); $memc->addServer('localhost', '11211'); $key = 'cas_test'; var_dump($memc->set($key, 10, 60)); var_dump($memc->get($key, null, $cas)); var_dump(is_double($cas)); var_dump($memc->cas($cas, $key, 11, 60)); var_dump($memc->get($key)); var_dump($memc->cas($cas, $key, 12, 60)); var_dump($memc->get($key));
/** * Sets the value for this key only if the value hasn't changed in the cache * since it was originally pulled * * @param \r8\Cache\Result $result A result object that was returned by * the getForUpdate method * @param mixed $value The value to set * @param Integer $expire The lifespan of this cache value, in seconds * @return \r8\iface\Cache Returns a self reference */ public function setIfSame(\r8\Cache\Result $result, $value, $expire = 0) { $this->memcached->cas($result->getHash(), $this->prepareKey($result->getKey()), $value, $this->prepareExpire($expire)); $this->handleErrors(); return $this; }
/** * @inheritdoc */ public function cas($cas_token, $key, $value, $expiration = null) { $key = $this->prefix . $key; return parent::cas($cas_token, $key, $value, $expiration); }