Commit 09a3300b by Carsten Brandt

refactored redis cache, added mset()

parent a0824422
......@@ -10,7 +10,7 @@ namespace yii\caching;
use yii\redis\Connection;
/**
* RedisCache implements a cache application component based on [redis](http://redis.io/) version 2.6 or higher.
* RedisCache implements a cache application component based on [redis](http://redis.io/) version 2.6.12 or higher.
*
* RedisCache needs to be configured with [[hostname]], [[port]] and [[database]] of the server
* to connect to. By default RedisCache assumes there is a redis server running on localhost at
......@@ -119,10 +119,7 @@ class RedisCache extends Cache
}
/**
* Retrieves a value from cache with a specified key.
* This is the implementation of the method declared in the parent class.
* @param string $key a unique key identifying the cached value
* @return string|boolean the value stored in cache, false if the value is not in the cache or expired.
* @inheritDocs
*/
protected function getValue($key)
{
......@@ -130,9 +127,7 @@ class RedisCache extends Cache
}
/**
* Retrieves multiple values from cache with the specified keys.
* @param array $keys a list of keys identifying the cached values
* @return array a list of cached values indexed by the keys
* @inheritDocs
*/
protected function getValues($keys)
{
......@@ -146,55 +141,67 @@ class RedisCache extends Cache
}
/**
* Stores a value identified by a key in cache.
* This is the implementation of the method declared in the parent class.
*
* @param string $key the key identifying the value to be cached
* @param string $value the value to be cached
* @param float $expire the number of seconds in which the cached value will expire. 0 means never expire.
* This can be a floating point number to specify the time in milliseconds.
* @return boolean true if the value is successfully stored into cache, false otherwise
* @inheritDocs
*/
protected function setValue($key,$value,$expire)
protected function setValue($key, $value, $expire)
{
if ($expire == 0) {
return (bool) $this->_connection->executeCommand('SET', [$key, $value]);
} else {
$expire = (int) ($expire * 1000);
return (bool) $this->_connection->executeCommand('PSETEX', [$key, $expire, $value]);
return (bool) $this->_connection->executeCommand('SET', [$key, $value, 'PX', $expire]);
}
}
/**
* Stores a value identified by a key into cache if the cache does not contain this key.
* This is the implementation of the method declared in the parent class.
*
* @param string $key the key identifying the value to be cached
* @param string $value the value to be cached
* @param float $expire the number of seconds in which the cached value will expire. 0 means never expire.
* This can be a floating point number to specify the time in milliseconds.
* @return boolean true if the value is successfully stored into cache, false otherwise
* @inheritDocs
*/
protected function addValue($key,$value,$expire)
protected function setValues($data, $expire)
{
$args = [];
foreach($data as $key => $value) {
$args[] = $key;
$args[] = $value;
}
$failedKeys = [];
if ($expire == 0) {
return (bool) $this->_connection->executeCommand('SETNX', [$key, $value]);
$this->_connection->executeCommand('MSET', $args);
} else {
// TODO consider requiring redis version >= 2.6.12 that supports this in one command
$expire = (int) ($expire * 1000);
$this->_connection->executeCommand('MULTI');
$this->_connection->executeCommand('SETNX', [$key, $value]);
$this->_connection->executeCommand('MSET', $args);
$index = [];
foreach ($data as $key => $value) {
$this->_connection->executeCommand('PEXPIRE', [$key, $expire]);
$response = $this->_connection->executeCommand('EXEC');
return (bool) $response[0];
$index[] = $key;
}
$result = $this->_connection->executeCommand('EXEC');
array_shift($result);
foreach($result as $i => $r) {
if ($r != 1) {
$failedKeys[] = $index[$i];
}
}
}
return $failedKeys;
}
/**
* @inheritDocs
*/
protected function addValue($key, $value, $expire)
{
if ($expire == 0) {
return (bool) $this->_connection->executeCommand('SET', [$key, $value, 'NX']);
} else {
$expire = (int) ($expire * 1000);
return (bool) $this->_connection->executeCommand('SET', [$key, $value, 'PX', $expire, 'NX']);
}
}
/**
* Deletes a value with the specified key from cache
* This is the implementation of the method declared in the parent class.
* @param string $key the key of the value to be deleted
* @return boolean if no error happens during deletion
* @inheritDocs
*/
protected function deleteValue($key)
{
......@@ -202,9 +209,7 @@ class RedisCache extends Cache
}
/**
* Deletes all values from cache.
* This is the implementation of the method declared in the parent class.
* @return boolean whether the flush operation was successful.
* @inheritDocs
*/
protected function flushValues()
{
......
......@@ -37,4 +37,9 @@ class ApcCacheTest extends CacheTestCase
{
$this->markTestSkipped("APC keys are expiring only on the next request.");
}
public function testExpireAdd()
{
$this->markTestSkipped("APC keys are expiring only on the next request.");
}
}
......@@ -91,7 +91,18 @@ abstract class CacheTestCase extends TestCase
$this->assertEquals('array_test', $array['array_test']);
}
public function testMset()
/**
* @return array testing mset with and without expiry
*/
public function msetExpiry()
{
return [[0], [2]];
}
/**
* @dataProvider msetExpiry
*/
public function testMset($expiry)
{
$cache = $this->getCacheInstance();
$cache->flush();
......@@ -100,7 +111,7 @@ abstract class CacheTestCase extends TestCase
'string_test' => 'string_test',
'number_test' => 42,
'array_test' => ['array_test' => 'array_test'],
]);
], $expiry);
$this->assertEquals('string_test', $cache->get('string_test'));
......@@ -170,6 +181,17 @@ abstract class CacheTestCase extends TestCase
$this->assertFalse($cache->get('expire_test'));
}
public function testExpireAdd()
{
$cache = $this->getCacheInstance();
$this->assertTrue($cache->add('expire_testa', 'expire_testa', 2));
usleep(500000);
$this->assertEquals('expire_testa', $cache->get('expire_testa'));
usleep(2500000);
$this->assertFalse($cache->get('expire_testa'));
}
public function testAdd()
{
$cache = $this->prepare();
......
......@@ -83,4 +83,16 @@ class DbCacheTest extends CacheTestCase
static::$time++;
$this->assertFalse($cache->get('expire_test'));
}
public function testExpireAdd()
{
$cache = $this->getCacheInstance();
static::$time = \time();
$this->assertTrue($cache->add('expire_testa', 'expire_testa', 2));
static::$time++;
$this->assertEquals('expire_testa', $cache->get('expire_testa'));
static::$time++;
$this->assertFalse($cache->get('expire_testa'));
}
}
......@@ -33,4 +33,16 @@ class FileCacheTest extends CacheTestCase
static::$time++;
$this->assertFalse($cache->get('expire_test'));
}
public function testExpireAdd()
{
$cache = $this->getCacheInstance();
static::$time = \time();
$this->assertTrue($cache->add('expire_testa', 'expire_testa', 2));
static::$time++;
$this->assertEquals('expire_testa', $cache->get('expire_testa'));
static::$time++;
$this->assertFalse($cache->get('expire_testa'));
}
}
......@@ -34,4 +34,12 @@ class MemCacheTest extends CacheTestCase
}
parent::testExpire();
}
public function testExpireAdd()
{
if (getenv('TRAVIS') == 'true') {
$this->markTestSkipped('Can not reliably test memcache expiry on travis-ci.');
}
parent::testExpireAdd();
}
}
......@@ -34,4 +34,12 @@ class MemCachedTest extends CacheTestCase
}
parent::testExpire();
}
public function testExpireAdd()
{
if (getenv('TRAVIS') == 'true') {
$this->markTestSkipped('Can not reliably test memcached expiry on travis-ci.');
}
parent::testExpireAdd();
}
}
......@@ -45,6 +45,17 @@ class RedisCacheTest extends CacheTestCase
$this->assertFalse($cache->get('expire_test_ms'));
}
public function testExpireAddMilliseconds()
{
$cache = $this->getCacheInstance();
$this->assertTrue($cache->add('expire_testa_ms', 'expire_testa_ms', 0.2));
usleep(100000);
$this->assertEquals('expire_testa_ms', $cache->get('expire_testa_ms'));
usleep(300000);
$this->assertFalse($cache->get('expire_testa_ms'));
}
/**
* Store a value that is 2 times buffer size big
* https://github.com/yiisoft/yii2/issues/743
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment