From 28ff2c201585e251e1c035a71f1486fead708737 Mon Sep 17 00:00:00 2001 From: Vladimir Bosiak Date: Mon, 23 Nov 2015 09:23:40 +0100 Subject: [PATCH 1/7] Key prefixes in Lock, Journal, SessionHandler, Storage INIT --- .gitignore | 1 + src/Kdyby/Redis/DI/RedisExtension.php | 36 +++++++++++++++++-------- src/Kdyby/Redis/ExclusiveLock.php | 14 +++++++--- src/Kdyby/Redis/RedisJournal.php | 13 ++++++--- src/Kdyby/Redis/RedisSessionHandler.php | 15 ++++++++--- src/Kdyby/Redis/RedisStorage.php | 15 ++++++++--- 6 files changed, 71 insertions(+), 23 deletions(-) diff --git a/.gitignore b/.gitignore index 7579f74..fa36fe5 100755 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ vendor composer.lock +.idea diff --git a/src/Kdyby/Redis/DI/RedisExtension.php b/src/Kdyby/Redis/DI/RedisExtension.php index c507567..d267794 100644 --- a/src/Kdyby/Redis/DI/RedisExtension.php +++ b/src/Kdyby/Redis/DI/RedisExtension.php @@ -17,7 +17,6 @@ use Nette\DI\Config; - /** * @author Filip Procházka */ @@ -155,8 +154,13 @@ protected function loadJournal(array $config) $builder = $this->getContainerBuilder(); + $constructParams = array( + $this->prefix('@journal_client'), + (!isset($config['keyPrefix']) ? null : $config['keyPrefix']), + ); + $builder->addDefinition($this->prefix('cacheJournal')) - ->setClass('Kdyby\Redis\RedisLuaJournal'); + ->setClass('Kdyby\Redis\RedisLuaJournal', $constructParams); // overwrite $journalService = $builder->getByType('Nette\Caching\Storages\IJournal') ?: 'nette.cacheJournal'; @@ -178,8 +182,13 @@ protected function loadStorage(array $config) 'locks' => TRUE, )); + $constructParams = array( + $this->prefix('@storage_client'), + (!isset($config['keyPrefix']) ? null : $config['keyPrefix']), + ); + $cacheStorage = $builder->addDefinition($this->prefix('cacheStorage')) - ->setClass('Kdyby\Redis\RedisStorage'); + ->setClass('Kdyby\Redis\RedisStorage', $constructParams); if (!$storageConfig['locks']) { $cacheStorage->addSetup('disableLocking'); @@ -206,7 +215,7 @@ protected function loadSession(array $config) 'weight' => 1, 'timeout' => $config['timeout'], 'database' => $config['database'], - 'prefix' => self::DEFAULT_SESSION_PREFIX, + 'prefix' => (!isset($config['keyPrefix']) ? self::DEFAULT_SESSION_PREFIX : $config['keyPrefix']), 'auth' => $config['auth'], 'native' => TRUE, 'lockDuration' => $config['lockDuration'], @@ -220,15 +229,20 @@ protected function loadSession(array $config) if ($sessionConfig['native']) { $this->loadNativeSessionHandler($sessionConfig); + return; + } - } else { - $builder->addDefinition($this->prefix('sessionHandler')) - ->setClass('Kdyby\Redis\RedisSessionHandler', array($this->prefix('@sessionHandler_client'))); + $constructParams = array( + $this->prefix('@sessionHandler_client'), + (!isset($config['keyPrefix']) ? null : $config['keyPrefix']), + ); - $sessionService = $builder->getByType('Nette\Http\Session') ?: 'session'; - $builder->getDefinition($sessionService) - ->addSetup('?->bind(?)', array($this->prefix('@sessionHandler'), '@self')); - } + $builder->addDefinition($this->prefix('sessionHandler')) + ->setClass('Kdyby\Redis\RedisSessionHandler', $constructParams); + + $sessionService = $builder->getByType('Nette\Http\Session') ?: 'session'; + $builder->getDefinition($sessionService) + ->addSetup('?->bind(?)', array($this->prefix('@sessionHandler'), '@self')); } diff --git a/src/Kdyby/Redis/ExclusiveLock.php b/src/Kdyby/Redis/ExclusiveLock.php index c0705d0..c5c5c7c 100644 --- a/src/Kdyby/Redis/ExclusiveLock.php +++ b/src/Kdyby/Redis/ExclusiveLock.php @@ -27,6 +27,11 @@ class ExclusiveLock extends Nette\Object */ private $client; + /** + * @var string + */ + private $keyPrefix = ''; + /** * @var array */ @@ -48,13 +53,16 @@ class ExclusiveLock extends Nette\Object public $acquireTimeout = FALSE; - /** * @param RedisClient $redisClient + * @param string|null $keyPrefix */ - public function __construct(RedisClient $redisClient) + public function __construct(RedisClient $redisClient, $keyPrefix = null) { $this->client = $redisClient; + if (!empty($keyPrefix)) { + $this->keyPrefix = $keyPrefix . '_'; + } } @@ -216,7 +224,7 @@ public function getLockTimeout($key) */ protected function formatLock($key) { - return $key . ':lock'; + return $this->keyPrefix . $key . ':lock'; } diff --git a/src/Kdyby/Redis/RedisJournal.php b/src/Kdyby/Redis/RedisJournal.php index 28f5210..5a15f02 100644 --- a/src/Kdyby/Redis/RedisJournal.php +++ b/src/Kdyby/Redis/RedisJournal.php @@ -14,7 +14,6 @@ use Nette\Caching\Cache; - /** * Redis journal for tags and priorities of cached values. * @@ -36,14 +35,22 @@ class RedisJournal extends Nette\Object implements Nette\Caching\Storages\IJourn */ protected $client; + /** + * @var string + */ + private $keyPrefix = ''; /** * @param RedisClient $client + * @param string|null $keyPrefix */ - public function __construct(RedisClient $client) + public function __construct(RedisClient $client, $keyPrefix = null) { $this->client = $client; + if (!empty($keyPrefix)) { + $this->keyPrefix = $keyPrefix . '_'; + } } @@ -183,7 +190,7 @@ private function tagEntries($tag) */ protected function formatKey($key, $suffix = NULL) { - return self::NS_NETTE . ':' . $key . ($suffix ? ':' . $suffix : NULL); + return self::NS_NETTE . ':' . $this->keyPrefix . $key . ($suffix ? ':' . $suffix : NULL); } } diff --git a/src/Kdyby/Redis/RedisSessionHandler.php b/src/Kdyby/Redis/RedisSessionHandler.php index fb4ffea..3f80536 100644 --- a/src/Kdyby/Redis/RedisSessionHandler.php +++ b/src/Kdyby/Redis/RedisSessionHandler.php @@ -40,6 +40,11 @@ class RedisSessionHandler extends Nette\Object implements \SessionHandlerInterfa */ private $client; + /** + * @var string + */ + private $keyPrefix = ''; + /** * @var Nette\Http\Session */ @@ -51,13 +56,16 @@ class RedisSessionHandler extends Nette\Object implements \SessionHandlerInterfa private $ttl; - /** * @param RedisClient $redisClient + * @param string|null $keyPrefix */ - public function __construct(RedisClient $redisClient) + public function __construct(RedisClient $redisClient, $keyPrefix = null) { $this->client = $redisClient; + if (!empty($keyPrefix)) { + $this->keyPrefix = $keyPrefix . '_'; + } } @@ -228,7 +236,8 @@ protected function lock($id) */ private function formatKey($id) { - return self::NS_NETTE . $id; + $key = $this->keyPrefix . $id; + return self::NS_NETTE . $key; } } diff --git a/src/Kdyby/Redis/RedisStorage.php b/src/Kdyby/Redis/RedisStorage.php index 3c0999a..702b0ef 100644 --- a/src/Kdyby/Redis/RedisStorage.php +++ b/src/Kdyby/Redis/RedisStorage.php @@ -49,21 +49,29 @@ class RedisStorage extends Nette\Object implements IMultiReadStorage */ private $journal; + /** + * @var string + */ + private $keyPrefix = ''; + /** * @var bool */ private $useLocks = TRUE; - /** * @param RedisClient $client * @param \Nette\Caching\Storages\IJournal $journal + * @param string|null $keyPrefix */ - public function __construct(RedisClient $client, IJournal $journal = NULL) + public function __construct(RedisClient $client, IJournal $journal = NULL, $keyPrefix = null) { $this->client = $client; $this->journal = $journal; + if (!empty($keyPrefix)) { + $this->keyPrefix = $keyPrefix . '_'; + } } @@ -301,7 +309,8 @@ public function clean(array $conds) */ protected function formatEntryKey($key) { - return self::NS_NETTE . ':' . str_replace(Cache::NAMESPACE_SEPARATOR, ':', $key); + $prefixedKey = $this->keyPrefix . $key; + return self::NS_NETTE . ':' . str_replace(Cache::NAMESPACE_SEPARATOR, ':', $prefixedKey); } From b7dde8935e02a3f4196b188e5bb4bc2d37fbdd66 Mon Sep 17 00:00:00 2001 From: Vladimir Bosiak Date: Mon, 23 Nov 2015 13:27:38 +0100 Subject: [PATCH 2/7] configuration fixes --- src/Kdyby/Redis/DI/RedisExtension.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Kdyby/Redis/DI/RedisExtension.php b/src/Kdyby/Redis/DI/RedisExtension.php index d267794..6dfa2a9 100644 --- a/src/Kdyby/Redis/DI/RedisExtension.php +++ b/src/Kdyby/Redis/DI/RedisExtension.php @@ -155,7 +155,7 @@ protected function loadJournal(array $config) $builder = $this->getContainerBuilder(); $constructParams = array( - $this->prefix('@journal_client'), + $this->prefix('@client'), (!isset($config['keyPrefix']) ? null : $config['keyPrefix']), ); @@ -183,7 +183,8 @@ protected function loadStorage(array $config) )); $constructParams = array( - $this->prefix('@storage_client'), + $this->prefix('@client'), + $this->prefix('@cacheJournal'), (!isset($config['keyPrefix']) ? null : $config['keyPrefix']), ); From 1eed9f0ae2ae28f6cb1164cf16369df4a66f3c91 Mon Sep 17 00:00:00 2001 From: Vladimir Bosiak Date: Mon, 23 Nov 2015 13:45:28 +0100 Subject: [PATCH 3/7] key prefixes documentation --- docs/en/index.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/docs/en/index.md b/docs/en/index.md index 406e278..628086c 100644 --- a/docs/en/index.md +++ b/docs/en/index.md @@ -124,3 +124,36 @@ You can disable the native driver by this option (and the emulated will take con redis: session: {native: off} ``` + +## Key prefixes + +When you use two instances of one application with one Redis server, it is possible, that your data can be overwritten by second application instance. + +To avoid this problem you can define key prefixes in configuration, for example: + +Instance 1 +```yml +redis: + keyPrefix: "instance1_" + host: 127.0.0.1 + port: 6379 + journal: on + session: on + storage: on + debugger: off +``` + +Instance 2 +```yml +redis: + keyPrefix: "instance2_" + host: 127.0.0.1 + port: 6379 + journal: on + session: on + storage: on + debugger: off +``` + +After configration all keys will be prefixed "keyPrefix_key" + From 27657e1712ea1a114f0e63e83c22e6aca1bee531 Mon Sep 17 00:00:00 2001 From: Vladimir Bosiak Date: Mon, 23 Nov 2015 14:12:43 +0100 Subject: [PATCH 4/7] revert gitignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index fa36fe5..7579f74 100755 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ vendor composer.lock -.idea From 26f38c4f20eb30dcc7bc1483c444ca72b0188186 Mon Sep 17 00:00:00 2001 From: Vladimir Bosiak Date: Mon, 15 Feb 2016 17:30:09 +0100 Subject: [PATCH 5/7] Fixes for comments in PR (https://github.com/Kdyby/Redis/pull/56) using namespace for different types of storage add mock extension for testing (mockery/mockery) --- composer.json | 3 +- docs/en/index.md | 21 +- src/Kdyby/Redis/DI/RedisExtension.php | 64 +-- src/Kdyby/Redis/ExclusiveLock.php | 13 +- src/Kdyby/Redis/RedisJournal.php | 12 +- src/Kdyby/Redis/RedisLuaJournal.php | 2 +- src/Kdyby/Redis/RedisSessionHandler.php | 14 +- src/Kdyby/Redis/RedisStorage.php | 15 +- src/Kdyby/Redis/scripts/common.lua | 12 +- src/Kdyby/Redis/scripts/journal.clean.lua | 2 +- tests/KdybyTests/Redis/ExclusiveLock.phpt | 58 +++ tests/KdybyTests/Redis/Extension.phpt | 32 +- .../Redis/RedisNamespaceJournal.phpt | 406 +++++++++++++++++ .../Redis/RedisNamespaceStorage.phpt | 407 ++++++++++++++++++ .../Redis/files/keyNamespace.config.neon | 7 + 15 files changed, 989 insertions(+), 79 deletions(-) create mode 100644 tests/KdybyTests/Redis/RedisNamespaceJournal.phpt create mode 100644 tests/KdybyTests/Redis/RedisNamespaceStorage.phpt create mode 100644 tests/KdybyTests/Redis/files/keyNamespace.config.neon diff --git a/composer.json b/composer.json index 4a78e2e..0e58e7b 100644 --- a/composer.json +++ b/composer.json @@ -49,7 +49,8 @@ "latte/latte": "~2.3@dev", "tracy/tracy": "~2.3@dev", - "nette/tester": "~1.3@rc" + "nette/tester": "~1.3@rc", + "mockery/mockery": "~0.9" }, "autoload": { "psr-0": { diff --git a/docs/en/index.md b/docs/en/index.md index 628086c..8439863 100644 --- a/docs/en/index.md +++ b/docs/en/index.md @@ -125,19 +125,21 @@ redis: session: {native: off} ``` -## Key prefixes +## Key namespace -When you use two instances of one application with one Redis server, it is possible, that your data can be overwritten by second application instance. +Key namespace gives you availability to create prefixed keys in Redis storage. + +You can define namespace for: journal, session, storage. -To avoid this problem you can define key prefixes in configuration, for example: +To use key prefixed with namespace, add `namespace` option in configuration, for example: Instance 1 ```yml redis: - keyPrefix: "instance1_" host: 127.0.0.1 port: 6379 - journal: on + journal: + namespace: "instance1" session: on storage: on debugger: off @@ -146,14 +148,17 @@ redis: Instance 2 ```yml redis: - keyPrefix: "instance2_" host: 127.0.0.1 port: 6379 - journal: on + journal: + namespace: "instance2" session: on storage: on debugger: off ``` -After configration all keys will be prefixed "keyPrefix_key" +After configuration all keys will be prefixed "namespace:key" +Example use case: +When you use two instances of one application with one Redis server, it is possible, that your data can be overwritten by second application instance. +To avoid this problem you can define key namespaces in configuration. diff --git a/src/Kdyby/Redis/DI/RedisExtension.php b/src/Kdyby/Redis/DI/RedisExtension.php index 6dfa2a9..42312a0 100644 --- a/src/Kdyby/Redis/DI/RedisExtension.php +++ b/src/Kdyby/Redis/DI/RedisExtension.php @@ -51,6 +51,7 @@ class RedisExtension extends Nette\DI\CompilerExtension 'lockAcquireTimeout' => FALSE, 'debugger' => '%debugMode%', 'versionCheck' => TRUE, + 'namespace' => NULL, ); /** @@ -154,10 +155,14 @@ protected function loadJournal(array $config) $builder = $this->getContainerBuilder(); - $constructParams = array( - $this->prefix('@client'), - (!isset($config['keyPrefix']) ? null : $config['keyPrefix']), - ); + $journalConfig = Nette\DI\Config\Helpers::merge(is_array($config['journal']) ? $config['journal'] : array(), array( + 'namespace' => NULL, + )); + + $constructParams = array( + $this->prefix('@client'), + $journalConfig['namespace'], + ); $builder->addDefinition($this->prefix('cacheJournal')) ->setClass('Kdyby\Redis\RedisLuaJournal', $constructParams); @@ -180,16 +185,17 @@ protected function loadStorage(array $config) $storageConfig = Nette\DI\Config\Helpers::merge(is_array($config['storage']) ? $config['storage'] : array(), array( 'locks' => TRUE, + 'namespace' => NULL, )); - $constructParams = array( - $this->prefix('@client'), - $this->prefix('@cacheJournal'), - (!isset($config['keyPrefix']) ? null : $config['keyPrefix']), - ); + $constructParams = array( + $this->prefix('@client'), + $this->prefix('@cacheJournal'), + $storageConfig['namespace'], + ); - $cacheStorage = $builder->addDefinition($this->prefix('cacheStorage')) - ->setClass('Kdyby\Redis\RedisStorage', $constructParams); + $cacheStorage = $builder->addDefinition($this->prefix('cacheStorage')) + ->setClass('Kdyby\Redis\RedisStorage', $constructParams); if (!$storageConfig['locks']) { $cacheStorage->addSetup('disableLocking'); @@ -210,20 +216,20 @@ protected function loadSession(array $config) $builder = $this->getContainerBuilder(); - $sessionConfig = Nette\DI\Config\Helpers::merge(is_array($config['session']) ? $config['session'] : array(), array( - 'host' => $config['host'], - 'port' => $config['port'], - 'weight' => 1, - 'timeout' => $config['timeout'], - 'database' => $config['database'], - 'prefix' => (!isset($config['keyPrefix']) ? self::DEFAULT_SESSION_PREFIX : $config['keyPrefix']), - 'auth' => $config['auth'], - 'native' => TRUE, - 'lockDuration' => $config['lockDuration'], - 'lockAcquireTimeout' => $config['lockAcquireTimeout'], - 'connectionAttempts' => $config['connectionAttempts'], - 'persistent' => $config['persistent'], - )); + $sessionConfig = Nette\DI\Config\Helpers::merge(is_array($config['session']) ? $config['session'] : array(), array( + 'host' => $config['host'], + 'port' => $config['port'], + 'weight' => 1, + 'timeout' => $config['timeout'], + 'database' => $config['database'], + 'prefix' => isset($config['namespace']) ? $config['namespace'] : self::DEFAULT_SESSION_PREFIX, + 'auth' => $config['auth'], + 'native' => TRUE, + 'lockDuration' => $config['lockDuration'], + 'lockAcquireTimeout' => $config['lockAcquireTimeout'], + 'connectionAttempts' => $config['connectionAttempts'], + 'persistent' => $config['persistent'], + )); $sessionConfig = self::fixClientConfig($sessionConfig); $this->buildClient('sessionHandler', array('debugger' => FALSE) + $sessionConfig); @@ -233,10 +239,10 @@ protected function loadSession(array $config) return; } - $constructParams = array( - $this->prefix('@sessionHandler_client'), - (!isset($config['keyPrefix']) ? null : $config['keyPrefix']), - ); + $constructParams = array( + $this->prefix('@sessionHandler_client'), + $sessionConfig['namespace'], + ); $builder->addDefinition($this->prefix('sessionHandler')) ->setClass('Kdyby\Redis\RedisSessionHandler', $constructParams); diff --git a/src/Kdyby/Redis/ExclusiveLock.php b/src/Kdyby/Redis/ExclusiveLock.php index c5c5c7c..5e633ac 100644 --- a/src/Kdyby/Redis/ExclusiveLock.php +++ b/src/Kdyby/Redis/ExclusiveLock.php @@ -27,11 +27,6 @@ class ExclusiveLock extends Nette\Object */ private $client; - /** - * @var string - */ - private $keyPrefix = ''; - /** * @var array */ @@ -55,14 +50,10 @@ class ExclusiveLock extends Nette\Object /** * @param RedisClient $redisClient - * @param string|null $keyPrefix */ - public function __construct(RedisClient $redisClient, $keyPrefix = null) + public function __construct(RedisClient $redisClient) { $this->client = $redisClient; - if (!empty($keyPrefix)) { - $this->keyPrefix = $keyPrefix . '_'; - } } @@ -224,7 +215,7 @@ public function getLockTimeout($key) */ protected function formatLock($key) { - return $this->keyPrefix . $key . ':lock'; + return $key . ':lock'; } diff --git a/src/Kdyby/Redis/RedisJournal.php b/src/Kdyby/Redis/RedisJournal.php index 5a15f02..4dd1499 100644 --- a/src/Kdyby/Redis/RedisJournal.php +++ b/src/Kdyby/Redis/RedisJournal.php @@ -38,18 +38,18 @@ class RedisJournal extends Nette\Object implements Nette\Caching\Storages\IJourn /** * @var string */ - private $keyPrefix = ''; + protected $namespace = ''; /** * @param RedisClient $client - * @param string|null $keyPrefix + * @param string|NULL $namespace */ - public function __construct(RedisClient $client, $keyPrefix = null) + public function __construct(RedisClient $client, $namespace = NULL) { $this->client = $client; - if (!empty($keyPrefix)) { - $this->keyPrefix = $keyPrefix . '_'; + if (!empty($namespace)) { + $this->namespace = $namespace . ':'; } } @@ -190,7 +190,7 @@ private function tagEntries($tag) */ protected function formatKey($key, $suffix = NULL) { - return self::NS_NETTE . ':' . $this->keyPrefix . $key . ($suffix ? ':' . $suffix : NULL); + return self::NS_NETTE . ':' . $this->namespace . $key . ($suffix ? ':' . $suffix : NULL); } } diff --git a/src/Kdyby/Redis/RedisLuaJournal.php b/src/Kdyby/Redis/RedisLuaJournal.php index 642d28d..14a0d61 100644 --- a/src/Kdyby/Redis/RedisLuaJournal.php +++ b/src/Kdyby/Redis/RedisLuaJournal.php @@ -49,7 +49,7 @@ public function clean(array $conds, Nette\Caching\IStorage $storage = NULL) $args = self::flattenDp($conds); - $result = $this->client->evalScript($this->getScript('clean'), array(), array($args)); + $result = $this->client->evalScript($this->getScript('clean'), array(), array($args, $this->namespace)); if (!is_array($result) && $result !== TRUE) { throw new RedisClientException("Failed to successfully execute lua script journal.clean(): " . $this->client->getDriver()->getLastError()); } diff --git a/src/Kdyby/Redis/RedisSessionHandler.php b/src/Kdyby/Redis/RedisSessionHandler.php index 3f80536..6518104 100644 --- a/src/Kdyby/Redis/RedisSessionHandler.php +++ b/src/Kdyby/Redis/RedisSessionHandler.php @@ -43,7 +43,7 @@ class RedisSessionHandler extends Nette\Object implements \SessionHandlerInterfa /** * @var string */ - private $keyPrefix = ''; + private $namespace = ''; /** * @var Nette\Http\Session @@ -58,13 +58,13 @@ class RedisSessionHandler extends Nette\Object implements \SessionHandlerInterfa /** * @param RedisClient $redisClient - * @param string|null $keyPrefix + * @param string|NULL $namespace */ - public function __construct(RedisClient $redisClient, $keyPrefix = null) + public function __construct(RedisClient $redisClient, $namespace = NULL) { $this->client = $redisClient; - if (!empty($keyPrefix)) { - $this->keyPrefix = $keyPrefix . '_'; + if (!empty($namespace)) { + $this->namespace = $namespace . ':'; } } @@ -236,8 +236,8 @@ protected function lock($id) */ private function formatKey($id) { - $key = $this->keyPrefix . $id; - return self::NS_NETTE . $key; + $key = $this->namespace . self::NS_NETTE . $id; + return $key; } } diff --git a/src/Kdyby/Redis/RedisStorage.php b/src/Kdyby/Redis/RedisStorage.php index 702b0ef..8655cb7 100644 --- a/src/Kdyby/Redis/RedisStorage.php +++ b/src/Kdyby/Redis/RedisStorage.php @@ -52,7 +52,7 @@ class RedisStorage extends Nette\Object implements IMultiReadStorage /** * @var string */ - private $keyPrefix = ''; + private $namespace = ''; /** * @var bool @@ -63,14 +63,14 @@ class RedisStorage extends Nette\Object implements IMultiReadStorage /** * @param RedisClient $client * @param \Nette\Caching\Storages\IJournal $journal - * @param string|null $keyPrefix + * @param string|NULL $namespace */ - public function __construct(RedisClient $client, IJournal $journal = NULL, $keyPrefix = null) + public function __construct(RedisClient $client, IJournal $journal = NULL, $namespace = NULL) { $this->client = $client; $this->journal = $journal; - if (!empty($keyPrefix)) { - $this->keyPrefix = $keyPrefix . '_'; + if (!empty($namespace)) { + $this->namespace = $namespace . ':'; } } @@ -283,7 +283,7 @@ public function clean(array $conds) { // cleaning using file iterator if (!empty($conds[Cache::ALL])) { - if ($keys = $this->client->send('keys', array(self::NS_NETTE . ':*'))) { + if ($keys = $this->client->send('keys', array(self::NS_NETTE . ':' . $this->namespace . '*'))) { $this->client->send('del', $keys); } @@ -309,8 +309,7 @@ public function clean(array $conds) */ protected function formatEntryKey($key) { - $prefixedKey = $this->keyPrefix . $key; - return self::NS_NETTE . ':' . str_replace(Cache::NAMESPACE_SEPARATOR, ':', $prefixedKey); + return self::NS_NETTE . ':' . $this->namespace . str_replace(Cache::NAMESPACE_SEPARATOR, ':', $key); } diff --git a/src/Kdyby/Redis/scripts/common.lua b/src/Kdyby/Redis/scripts/common.lua index a1a3b22..c704859 100644 --- a/src/Kdyby/Redis/scripts/common.lua +++ b/src/Kdyby/Redis/scripts/common.lua @@ -1,6 +1,13 @@ +local namespace = ARGV[2] +if namespace == nil then + namespace = "" +end + +rawset(_G, "namespace", namespace) + local formatKey = function (key, suffix) - local res = "Nette.Journal:" .. key + local res = "Nette.Journal:" .. namespace .. key if suffix ~= nil then res = res .. ":" .. suffix end @@ -9,7 +16,7 @@ local formatKey = function (key, suffix) end local formatStorageKey = function(key, suffix) - local res = "Nette.Storage:" .. key + local res = "Nette.Storage:" .. namespace .. key if suffix ~= nil then res = res .. ":" .. suffix end @@ -45,3 +52,4 @@ local cleanEntry = function (keys) -- redis.call('exec') end end + diff --git a/src/Kdyby/Redis/scripts/journal.clean.lua b/src/Kdyby/Redis/scripts/journal.clean.lua index abe74c1..0ab30ee 100644 --- a/src/Kdyby/Redis/scripts/journal.clean.lua +++ b/src/Kdyby/Redis/scripts/journal.clean.lua @@ -3,7 +3,7 @@ local conds = cjson.decode(ARGV[1]) if conds["all"] ~= nil then -- redis.call('multi') - for i, value in pairs(redis.call('keys', "Nette.Journal:*")) do + for i, value in pairs(redis.call('keys', "Nette.Journal:".. namespace .."*")) do redis.call('del', value) end -- redis.call('exec') diff --git a/tests/KdybyTests/Redis/ExclusiveLock.phpt b/tests/KdybyTests/Redis/ExclusiveLock.phpt index a8d0f46..e4ddbd3 100644 --- a/tests/KdybyTests/Redis/ExclusiveLock.phpt +++ b/tests/KdybyTests/Redis/ExclusiveLock.phpt @@ -41,6 +41,21 @@ class ExclusiveLockTest extends AbstractRedisTestCase }, 'Kdyby\Redis\LockException', 'Process ran too long. Increase lock duration, or extend lock regularly.'); } + public function testLockExpiredWithNamespace() + { + $client = $this->client; + Assert::exception(function () use ($client) { + $first = new ExclusiveLock($client, 'foo'); + $first->duration = 1; + + Assert::true($first->acquireLock('foo:bar')); + sleep(3); + + $first->increaseLockTimeout('foo:bar'); + + }, 'Kdyby\Redis\LockException', 'Process ran too long. Increase lock duration, or extend lock regularly.'); + } + public function testDeadlockHandling() @@ -56,6 +71,49 @@ class ExclusiveLockTest extends AbstractRedisTestCase Assert::true($second->acquireLock('foo:bar')); } + public function testDeadlockHandlingWithNamespace() + { + $first = new ExclusiveLock($this->client, 'foo'); + $first->duration = 1; + $second = new ExclusiveLock(new RedisClient()); + $second->duration = 1; + + Assert::true($first->acquireLock('foo:bar')); + sleep(3); // first died? + + Assert::true($second->acquireLock('foo:bar')); + } + + /** + * @return array + */ + public function getLockKeyTestData() + { + return array( + array('bar'), + array('foo:bar'), + array('xxx:foo:bar'), + ); + } + + /** + * @dataProvider getLockKeyTestData + * @param $key + */ + public function testLockKey($key) + { + $client = \Mockery::mock('Kdyby\Redis\RedisClient'); + $client->shouldReceive('setNX')->once()->andReturn(false); + $client->shouldReceive('get')->once()->with($key . ':lock')->andReturn(10); + $client->shouldReceive('getSet')->once()->andReturn(10); + $client->shouldReceive('del')->with($key . ':lock'); + $client->shouldReceive('close')->with(); + $lock = new ExclusiveLock($client); + $lock->duration = 5; + $lock->acquireLock($key); + Assert::true((0 < $lock->getLockTimeout($key))); + \Mockery::close(); + } } \run(new ExclusiveLockTest()); diff --git a/tests/KdybyTests/Redis/Extension.phpt b/tests/KdybyTests/Redis/Extension.phpt index d661288..50e67cc 100644 --- a/tests/KdybyTests/Redis/Extension.phpt +++ b/tests/KdybyTests/Redis/Extension.phpt @@ -26,23 +26,45 @@ class ExtensionTest extends Tester\TestCase { /** - * @return \SystemContainer|\Nette\DI\Container + * @param string $path + * @return Nette\DI\Container|\SystemContainer */ - protected function createContainer() + protected function createContainer($path) { $config = new Nette\Configurator(); $config->setTempDirectory(TEMP_DIR); Kdyby\Redis\DI\RedisExtension::register($config); - $config->addConfig(__DIR__ . '/files/config.neon', $config::NONE); + $config->addConfig($path, $config::NONE); return $config->createContainer(); } + /** + * @return array + */ + public function getConfigs() + { + $directory = __DIR__ . '/files'; + $files = array_diff(scandir($directory), array('..', '.')); + $configs = array(); + foreach ($files as $file) { + if (!preg_match('/config/', $file)) { + continue; + } + $path = $directory . '/' . $file; + $configs[] = array($path); + } + return $configs; + } - public function testFunctional() + /** + * @dataProvider getConfigs + * @param string $path Path to neon config + */ + public function testFunctional($path) { - $dic = $this->createContainer(); + $dic = $this->createContainer($path); Assert::true($dic->getService('redis.client') instanceof Kdyby\Redis\RedisClient); Assert::true($dic->getService('redis.cacheJournal') instanceof Kdyby\Redis\RedisLuaJournal); Assert::true($dic->getService('nette.cacheJournal') instanceof Kdyby\Redis\RedisLuaJournal); diff --git a/tests/KdybyTests/Redis/RedisNamespaceJournal.phpt b/tests/KdybyTests/Redis/RedisNamespaceJournal.phpt new file mode 100644 index 0000000..28533ee --- /dev/null +++ b/tests/KdybyTests/Redis/RedisNamespaceJournal.phpt @@ -0,0 +1,406 @@ + + * @package Kdyby\Redis + */ + +namespace KdybyTests\Redis; + +use Kdyby\Redis\RedisLuaJournal; +use Nette; +use Nette\Caching\Cache; +use Tester; +use Tester\Assert; + +require_once __DIR__ . '/../bootstrap.php'; + + +/** + * @author Filip Procházka + */ +class RedisNamespaceJournalTest extends AbstractRedisTestCase +{ + + /** + * @var RedisLuaJournal|Nette\Caching\Storages\IJournal + */ + private $journal; + + + + protected function setUp() + { + parent::setUp(); + + $this->journal = new RedisLuaJournal($this->getClient(), 'foo'); + } + + + + public function testRemoveByTag() + { + // Assert::same(0, count($this->getClient()->keys('*'))); + $this->assertKeysInDatabase(0); + + $this->journal->write('ok_test1', array( + Cache::TAGS => array('test:homepage'), + )); + + // Assert::same(2, count($this->getClient()->keys('*'))); + $this->assertKeysInDatabase(2); + + $result = $this->journal->clean(array(Cache::TAGS => array('test:homepage'))); + Assert::same(1, count($result)); + Assert::same('ok_test1', $result[0]); + } + + + + public function testRemovingByMultipleTags_OneIsNotDefined() + { + $this->journal->write('ok_test2', array( + Cache::TAGS => array('test:homepage', 'test:homepage2'), + )); + + $result = $this->journal->clean(array(Cache::TAGS => array('test:homepage2'))); + Assert::same(1, count($result)); + Assert::same('ok_test2', $result[0]); + } + + + + public function testRemovingByMultipleTags_BothAreOnOneEntry() + { + $this->journal->write('ok_test2b', array( + Cache::TAGS => array('test:homepage', 'test:homepage2'), + )); + + $result = $this->journal->clean(array(Cache::TAGS => array('test:homepage', 'test:homepage2'))); + Assert::same(1, count($result)); + Assert::same('ok_test2b', $result[0]); + } + + + + public function testRemoveByMultipleTags_TwoSameTags() + { + $this->journal->write('ok_test2c', array( + Cache::TAGS => array('test:homepage', 'test:homepage'), + )); + + $result = $this->journal->clean(array(Cache::TAGS => array('test:homepage', 'test:homepage'))); + Assert::same(1, count($result)); + Assert::same('ok_test2c', $result[0]); + } + + + + public function testRemoveByTagAndPriority() + { + $this->journal->write('ok_test2d', array( + Cache::TAGS => array('test:homepage'), + Cache::PRIORITY => 15, + )); + + $result = $this->journal->clean(array(Cache::TAGS => array('test:homepage'), Cache::PRIORITY => 20)); + Assert::same(1, count($result)); + Assert::same('ok_test2d', $result[0]); + } + + + + public function testRemoveByPriority() + { + $this->journal->write('ok_test3', array( + Cache::PRIORITY => 10, + )); + + $result = $this->journal->clean(array(Cache::PRIORITY => 10)); + Assert::same(1, count($result)); + Assert::same('ok_test3', $result[0]); + } + + + + public function testPriorityAndTag_CleanByTag() + { + $this->journal->write('ok_test4', array( + Cache::TAGS => array('test:homepage'), + Cache::PRIORITY => 10, + )); + + $result = $this->journal->clean(array(Cache::TAGS => array('test:homepage'))); + Assert::same(1, count($result)); + Assert::same('ok_test4', $result[0]); + } + + + + public function testPriorityAndTag_CleanByPriority() + { + $this->journal->write('ok_test5', array( + Cache::TAGS => array('test:homepage'), + Cache::PRIORITY => 10, + )); + + $result = $this->journal->clean(array(Cache::PRIORITY => 10)); + Assert::same(1, count($result)); + Assert::same('ok_test5', $result[0]); + } + + + + public function testMultipleWritesAndMultipleClean() + { + for ($i = 1; $i <= 10; $i++) { + $this->journal->write('ok_test6_' . $i, array( + Cache::TAGS => array('test:homepage', 'test:homepage/' . $i), + Cache::PRIORITY => $i, + )); + } + + $result = $this->journal->clean(array(Cache::PRIORITY => 5)); + Assert::same(5, count($result), "clean priority lower then 5"); + Assert::same('ok_test6_1', $result[0], "clean priority lower then 5"); + + $result = $this->journal->clean(array(Cache::TAGS => array('test:homepage/7'))); + Assert::same(1, count($result), "clean tag homepage/7"); + Assert::same('ok_test6_7', $result[0], "clean tag homepage/7"); + + $result = $this->journal->clean(array(Cache::TAGS => array('test:homepage/4'))); + Assert::same(0, count($result), "clean non exists tag"); + + $result = $this->journal->clean(array(Cache::PRIORITY => 4)); + Assert::same(0, count($result), "clean non exists priority"); + + $result = $this->journal->clean(array(Cache::TAGS => array('test:homepage'))); + Assert::same(4, count($result), "clean other"); + sort($result); + Assert::same(array('ok_test6_10', 'ok_test6_6', 'ok_test6_8', 'ok_test6_9'), $result, "clean other"); + } + + + + public function testSpecialChars() + { + $this->journal->write('ok_test7ščřžýáíé', array( + Cache::TAGS => array('čšřýýá', 'ýřžčýž/10') + )); + + $result = $this->journal->clean(array(Cache::TAGS => array('čšřýýá'))); + Assert::same(1, count($result)); + Assert::same('ok_test7ščřžýáíé', $result[0]); + } + + + + public function testDuplicates_SameTag() + { + $this->journal->write('ok_test_a', array( + Cache::TAGS => array('homepage') + )); + + $this->journal->write('ok_test_a', array( + Cache::TAGS => array('homepage') + )); + + $result = $this->journal->clean(array(Cache::TAGS => array('homepage'))); + Assert::same(1, count($result)); + Assert::same('ok_test_a', $result[0]); + } + + + + public function testDuplicates_SamePriority() + { + $this->journal->write('ok_test_b', array( + Cache::PRIORITY => 12 + )); + + $this->journal->write('ok_test_b', array( + Cache::PRIORITY => 12 + )); + + $result = $this->journal->clean(array(Cache::PRIORITY => 12)); + Assert::same(1, count($result)); + Assert::same('ok_test_b', $result[0]); + } + + + + public function testDuplicates_DifferentTags() + { + $this->journal->write('ok_test_ba', array( + Cache::TAGS => array('homepage') + )); + + $this->journal->write('ok_test_ba', array( + Cache::TAGS => array('homepage2') + )); + + $result = $this->journal->clean(array(Cache::TAGS => array('homepage'))); + Assert::same(0, count($result)); + + $result2 = $this->journal->clean(array(Cache::TAGS => array('homepage2'))); + Assert::same(1, count($result2)); + Assert::same('ok_test_ba', $result2[0]); + } + + + + public function testDuplicates_DifferentPriorities() + { + $this->journal->write('ok_test_bb', array( + Cache::PRIORITY => 15 + )); + + $this->journal->write('ok_test_bb', array( + Cache::PRIORITY => 20 + )); + + $result = $this->journal->clean(array(Cache::PRIORITY => 30)); + Assert::same(1, count($result)); + Assert::same('ok_test_bb', $result[0]); + } + + + + public function testCleanAll() + { + $this->journal->write('ok_test_all_tags', array( + Cache::TAGS => array('test:all', 'test:all') + )); + + $this->journal->write('ok_test_all_priority', array( + Cache::PRIORITY => 5, + )); + + $result = $this->journal->clean(array(Cache::ALL => TRUE)); + Assert::null($result); + + $result2 = $this->journal->clean(array(Cache::TAGS => 'test:all')); + Assert::true(empty($result2)); + } + + + + public function testBigCache() + { + if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { + Tester\Helpers::skip("Linux only"); + } + + $script = $this->cacheGeneratorScripts(); + $script .= <<getClient()->evalScript($script)); + $this->assertKeysInDatabase(5100); + + $this->journal->clean(array(Cache::TAGS => 'test.4356')); + $this->assertKeysInDatabase(0); + } + + + + public function testBigCache_ShitloadOfEntries() + { + if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { + Tester\Helpers::skip("Linux only"); + } + + $script = $this->cacheGeneratorScripts(); + $script .= <<getClient()->evalScript($script)); + $this->assertKeysInDatabase(100001); + + $this->journal->clean(array(Cache::TAGS => 'kdyby')); + $this->assertKeysInDatabase(0); + } + + + + protected function assertKeysInDatabase($number) + { + $dbNum = $this->getClient()->getDriver()->getDBNum(); + $dbInfo = $this->getClient()->info('db' . $dbNum); + + if ($number > 0 && !$dbInfo) { + Assert::fail("Number of keys in database couldn't be determined"); + } + + Assert::equal($number, $dbInfo ? (int) $dbInfo['keys'] : 0); + } + + + + private function cacheGeneratorScripts() + { + $script = "local ARGV = {}\n"; + $script .= 'ARGV[2] = "foo:"'. "\n"; + $script .= file_get_contents(__DIR__ . '/../../../src/Kdyby/Redis/scripts/common.lua'); + $script .= << 0 and + function(_, lastvalue) + local nextvalue = lastvalue + step + if nextvalue <= to then return nextvalue end + end or + step < 0 and + function(_, lastvalue) + local nextvalue = lastvalue + step + if nextvalue >= to then return nextvalue end + end or + function(_, lastvalue) return lastvalue end + return f, nil, from - step +end + + +LUA; + return $script; + } + + + + public function testNullByte() + { + $key = "prefix\x00test:\\2"; + $this->journal->write($key, array( + Cache::TAGS => array("test:nullByte") + )); + + $result = $this->journal->clean(array( + Cache::TAGS => array("test:nullByte") + )); + Assert::same(array($key), $result); + } + +} + +\run(new RedisNamespaceJournalTest()); diff --git a/tests/KdybyTests/Redis/RedisNamespaceStorage.phpt b/tests/KdybyTests/Redis/RedisNamespaceStorage.phpt new file mode 100644 index 0000000..ed5d017 --- /dev/null +++ b/tests/KdybyTests/Redis/RedisNamespaceStorage.phpt @@ -0,0 +1,407 @@ + + * @package Kdyby\Redis + */ + +namespace KdybyTests\Redis; + +use Kdyby\Redis\RedisLuaJournal; +use Kdyby\Redis\RedisStorage; +use Nette; +use Nette\Caching\Cache; +use Tester; +use Tester\Assert; + +require_once __DIR__ . '/../bootstrap.php'; + + +/** + * @author Vladimir Bosiak + */ +class RedisNamespaceStorageTest extends AbstractRedisTestCase +{ + + /** + * @var \Kdyby\Redis\RedisStorage + */ + private $storage; + + public function setUp() + { + parent::setUp(); + $this->storage = new RedisStorage($this->client, NULL, 'foo'); + } + + public function testBasics() + { + list($key, $value) = $this->basicData(); + + $cache = new Cache($this->storage); + Assert::null($cache->load($key), "Cache content"); + + // Writing cache... + $cache->save($key, $value); + Assert::same($value, $cache->load($key), "Is cache ok?"); + + // Removing from cache using unset()... + $cache->remove($key); + Assert::false($cache->load($key) !== null, "Is cached?"); + + // Removing from cache using set NULL... + $cache->save($key, $value); + $cache->save($key, null); + Assert::false($cache->load($key) !== null, "Is cached?"); + } + + /** + * key and data with special chars + * + * @return array + */ + public function basicData() + { + return array( + $key = array(1, true), + $value = range("\x00", "\xFF"), + ); + } + + /** + * @param mixed $val + * @return mixed + */ + public static function dependency($val) + { + return $val; + } + + + public function testCallbacks() + { + $key = 'nette'; + $value = 'rulez'; + + $cache = new Cache($this->storage); + $cb = get_called_class() . '::dependency'; + + // Writing cache... + $cache->save($key, $value, array( + Cache::CALLBACKS => array(array($cb, 1)), + )); + + Assert::true($cache->load($key) !== null, 'Is cached?'); + + // Writing cache... + $cache->save($key, $value, array( + Cache::CALLBACKS => array(array($cb, 0)), + )); + + Assert::false($cache->load($key) !== null, 'Is cached?'); + } + + + public function testCleanAll() + { + $cacheA = new Cache($this->storage); + $cacheB = new Cache($this->storage, 'B'); + + $cacheA->save('test1', 'David'); + $cacheA->save('test2', 'Grudl'); + $cacheB->save('test1', 'divaD'); + $cacheB->save('test2', 'ldurG'); + + Assert::same('David Grudl divaD ldurG', implode(' ', array( + $cacheA->load('test1'), + $cacheA->load('test2'), + $cacheB->load('test1'), + $cacheB->load('test2'), + ))); + + $this->storage->clean(array(Cache::ALL => true)); + + Assert::null($cacheA->load('test1')); + Assert::null($cacheA->load('test2')); + Assert::null($cacheB->load('test1')); + Assert::null($cacheB->load('test2')); + } + + + public function testExpiration() + { + $key = 'nette'; + $value = 'rulez'; + + $cache = new Cache($this->storage); + + // Writing cache... + $cache->save($key, $value, array( + Cache::EXPIRATION => time() + 3, + )); + + // Sleeping 1 second + sleep(1); + Assert::true($cache->load($key) !== null, 'Is cached?'); + + // Sleeping 3 seconds + sleep(3); + Assert::false($cache->load($key) !== null, 'Is cached?'); + } + + + public function testIntKeys() + { + // key and data with special chars + $key = 0; + $value = range("\x00", "\xFF"); + + $cache = new Cache($this->storage); + Assert::false($cache->load($key) !== null, 'Is cached?'); + Assert::null($cache->load($key), 'Cache content'); + + // Writing cache... + $cache->save($key, $value); + Assert::true($cache->load($key) !== null, 'Is cached?'); + Assert::same($value, $cache->load($key), 'Is cache ok?'); + + // Removing from cache using unset()... + $cache->remove($key); + Assert::false($cache->load($key) !== null, 'Is cached?'); + + // Removing from cache using set NULL... + $cache->save($key, $value); + $cache->save($key, null); + Assert::false($cache->load($key) !== null, 'Is cached?'); + } + + + public function testDependentItems() + { + $key = 'nette'; + $value = 'rulez'; + + $cache = new Cache($this->storage); + + // Writing cache... + $cache->save($key, $value, array( + Cache::ITEMS => array('dependent'), + )); + Assert::true($cache->load($key) !== null, 'Is cached?'); + + // Modifing dependent cached item + $cache->save('dependent', 'hello world'); + Assert::false($cache->load($key) !== null, 'Is cached?'); + + // Writing cache... + $cache->save($key, $value, array( + Cache::ITEMS => 'dependent', + )); + Assert::true($cache->load($key) !== null, 'Is cached?'); + + // Modifing dependent cached item + sleep(2); + $cache->save('dependent', 'hello europe'); + Assert::false($cache->load($key) !== null, 'Is cached?'); + + // Writing cache... + $cache->save($key, $value, array( + Cache::ITEMS => 'dependent', + )); + Assert::true($cache->load($key) !== null, 'Is cached?'); + + // Deleting dependent cached item + $cache->save('dependent', null); + Assert::false($cache->load($key) !== null, 'Is cached?'); + } + + + /** + */ + public function testLoadOrSave() + { + // key and data with special chars + $key = '../' . implode('', range("\x00", "\x1F")); + $value = range("\x00", "\xFF"); + + $cache = new Cache($this->storage); + Assert::false($cache->load($key) !== null, 'Is cached?'); + + // Writing cache using Closure... + $res = $cache->load($key, function (& $dp) use ($value) { + $dp = array( + Cache::EXPIRATION => time() + 2, + ); + + return $value; + }); + + Assert::same($value, $res, 'Is result ok?'); + Assert::same($value, $cache->load($key), 'Is cache ok?'); + + // Sleeping 3 seconds + sleep(3); + Assert::false($cache->load($key) !== null, 'Is cached?'); + } + + + public function testNamespace() + { + $cacheA = new Cache($this->storage, 'a'); + $cacheB = new Cache($this->storage, 'b'); + + // Writing cache... + $cacheA->save('key', 'hello'); + $cacheB->save('key', 'world'); + + Assert::true($cacheA->load('key') !== null, 'Is cached #1?'); + Assert::true($cacheB->load('key') !== null, 'Is cached #2?'); + Assert::same('hello', $cacheA->load('key'), 'Is cache ok #1?'); + Assert::same('world', $cacheB->load('key'), 'Is cache ok #2?'); + + // Removing from cache #2 using unset()... + $cacheB->remove('key'); + Assert::true($cacheA->load('key') !== null, 'Is cached #1?'); + Assert::false($cacheB->load('key') !== null, 'Is cached #2?'); + } + + + public function testPriority() + { + $storage = new RedisStorage($this->client, new Nette\Caching\Storages\FileJournal(TEMP_DIR)); + $cache = new Cache($storage); + + // Writing cache... + $cache->save('key1', 'value1', array( + Cache::PRIORITY => 100, + )); + $cache->save('key2', 'value2', array( + Cache::PRIORITY => 200, + )); + $cache->save('key3', 'value3', array( + Cache::PRIORITY => 300, + )); + $cache->save('key4', 'value4'); + + // Cleaning by priority... + $cache->clean(array( + Cache::PRIORITY => '200', + )); + + Assert::false($cache->load('key1') !== null, 'Is cached key1?'); + Assert::false($cache->load('key2') !== null, 'Is cached key2?'); + Assert::true($cache->load('key3') !== null, 'Is cached key3?'); + Assert::true($cache->load('key4') !== null, 'Is cached key4?'); + } + + + public function testPriority_Optimized() + { + $storage = new RedisStorage($this->client, new RedisLuaJournal($this->client)); + $cache = new Cache($storage); + + // Writing cache... + $cache->save('key1', 'value1', array( + Cache::PRIORITY => 100, + )); + $cache->save('key2', 'value2', array( + Cache::PRIORITY => 200, + )); + $cache->save('key3', 'value3', array( + Cache::PRIORITY => 300, + )); + $cache->save('key4', 'value4'); + + // Cleaning by priority... + $cache->clean(array( + Cache::PRIORITY => '200', + )); + + Assert::false($cache->load('key1') !== null, 'Is cached key1?'); + Assert::false($cache->load('key2') !== null, 'Is cached key2?'); + Assert::true($cache->load('key3') !== null, 'Is cached key3?'); + Assert::true($cache->load('key4') !== null, 'Is cached key4?'); + } + + + public function testTags() + { + $storage = new RedisStorage($this->client, new Nette\Caching\Storages\FileJournal(TEMP_DIR)); + $cache = new Cache($storage); + + // Writing cache... + $cache->save('key1', 'value1', array( + Cache::TAGS => array('one', 'two'), + )); + $cache->save('key2', 'value2', array( + Cache::TAGS => array('one', 'three'), + )); + $cache->save('key3', 'value3', array( + Cache::TAGS => array('two', 'three'), + )); + $cache->save('key4', 'value4'); + + // Cleaning by tags... + $cache->clean(array( + Cache::TAGS => 'one', + )); + + Assert::false($cache->load('key1') !== null, 'Is cached key1?'); + Assert::false($cache->load('key2') !== null, 'Is cached key2?'); + Assert::true($cache->load('key3') !== null, 'Is cached key3?'); + Assert::true($cache->load('key4') !== null, 'Is cached key4?'); + } + + + public function testTags_Optimized() + { + $storage = new RedisStorage($this->client, new RedisLuaJournal($this->client)); + $cache = new Cache($storage); + + // Writing cache... + $cache->save('key1', 'value1', array( + Cache::TAGS => array('one', 'two'), + )); + $cache->save('key2', 'value2', array( + Cache::TAGS => array('one', 'three'), + )); + $cache->save('key3', 'value3', array( + Cache::TAGS => array('two', 'three'), + )); + $cache->save('key4', 'value4'); + + // Cleaning by tags... + $cache->clean(array( + Cache::TAGS => 'one', + )); + + Assert::false($cache->load('key1') !== null, 'Is cached key1?'); + Assert::false($cache->load('key2') !== null, 'Is cached key2?'); + Assert::true($cache->load('key3') !== null, 'Is cached key3?'); + Assert::true($cache->load('key4') !== null, 'Is cached key4?'); + } + + + public function testMultiRead() + { + $storage = $this->storage; + + $storage->write('A', 1, array()); + $storage->write('B', 2, array()); + $storage->write('C', false, array()); + $storage->write('E', null, array()); + + Assert::equal(array( + 'A' => 1, + 'B' => 2, + 'C' => false, + 'D' => null, + 'E' => null, + ), $storage->multiRead(array('A', 'B', 'C', 'D', 'E'))); + } +} + +\run(new RedisNamespaceStorageTest()); diff --git a/tests/KdybyTests/Redis/files/keyNamespace.config.neon b/tests/KdybyTests/Redis/files/keyNamespace.config.neon new file mode 100644 index 0000000..03d0a72 --- /dev/null +++ b/tests/KdybyTests/Redis/files/keyNamespace.config.neon @@ -0,0 +1,7 @@ +redis: + journal: + namespace: "instance1" + storage: + namespace: "instance2" + session: + namespace: "instance3" From fabf7fabc7a2f2d846de6ee9560462c3e5907fdc Mon Sep 17 00:00:00 2001 From: Vladimir Bosiak Date: Tue, 16 Feb 2016 13:35:58 +0100 Subject: [PATCH 6/7] fix build error [ERROR] Kdyby/Redis/DI/RedisExtension.php:54 Mixed tabs and spaces indentation --- src/Kdyby/Redis/DI/RedisExtension.php | 68 +++++++++++++-------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/Kdyby/Redis/DI/RedisExtension.php b/src/Kdyby/Redis/DI/RedisExtension.php index 42312a0..e513b3f 100644 --- a/src/Kdyby/Redis/DI/RedisExtension.php +++ b/src/Kdyby/Redis/DI/RedisExtension.php @@ -51,7 +51,7 @@ class RedisExtension extends Nette\DI\CompilerExtension 'lockAcquireTimeout' => FALSE, 'debugger' => '%debugMode%', 'versionCheck' => TRUE, - 'namespace' => NULL, + 'namespace' => NULL, ); /** @@ -155,14 +155,14 @@ protected function loadJournal(array $config) $builder = $this->getContainerBuilder(); - $journalConfig = Nette\DI\Config\Helpers::merge(is_array($config['journal']) ? $config['journal'] : array(), array( - 'namespace' => NULL, - )); + $journalConfig = Nette\DI\Config\Helpers::merge(is_array($config['journal']) ? $config['journal'] : array(), array( + 'namespace' => NULL, + )); - $constructParams = array( - $this->prefix('@client'), - $journalConfig['namespace'], - ); + $constructParams = array( + $this->prefix('@client'), + $journalConfig['namespace'], + ); $builder->addDefinition($this->prefix('cacheJournal')) ->setClass('Kdyby\Redis\RedisLuaJournal', $constructParams); @@ -185,17 +185,17 @@ protected function loadStorage(array $config) $storageConfig = Nette\DI\Config\Helpers::merge(is_array($config['storage']) ? $config['storage'] : array(), array( 'locks' => TRUE, - 'namespace' => NULL, + 'namespace' => NULL, )); - $constructParams = array( - $this->prefix('@client'), - $this->prefix('@cacheJournal'), - $storageConfig['namespace'], - ); + $constructParams = array( + $this->prefix('@client'), + $this->prefix('@cacheJournal'), + $storageConfig['namespace'], + ); - $cacheStorage = $builder->addDefinition($this->prefix('cacheStorage')) - ->setClass('Kdyby\Redis\RedisStorage', $constructParams); + $cacheStorage = $builder->addDefinition($this->prefix('cacheStorage')) + ->setClass('Kdyby\Redis\RedisStorage', $constructParams); if (!$storageConfig['locks']) { $cacheStorage->addSetup('disableLocking'); @@ -216,20 +216,20 @@ protected function loadSession(array $config) $builder = $this->getContainerBuilder(); - $sessionConfig = Nette\DI\Config\Helpers::merge(is_array($config['session']) ? $config['session'] : array(), array( - 'host' => $config['host'], - 'port' => $config['port'], - 'weight' => 1, - 'timeout' => $config['timeout'], - 'database' => $config['database'], - 'prefix' => isset($config['namespace']) ? $config['namespace'] : self::DEFAULT_SESSION_PREFIX, - 'auth' => $config['auth'], - 'native' => TRUE, - 'lockDuration' => $config['lockDuration'], - 'lockAcquireTimeout' => $config['lockAcquireTimeout'], - 'connectionAttempts' => $config['connectionAttempts'], - 'persistent' => $config['persistent'], - )); + $sessionConfig = Nette\DI\Config\Helpers::merge(is_array($config['session']) ? $config['session'] : array(), array( + 'host' => $config['host'], + 'port' => $config['port'], + 'weight' => 1, + 'timeout' => $config['timeout'], + 'database' => $config['database'], + 'prefix' => isset($config['namespace']) ? $config['namespace'] : self::DEFAULT_SESSION_PREFIX, + 'auth' => $config['auth'], + 'native' => TRUE, + 'lockDuration' => $config['lockDuration'], + 'lockAcquireTimeout' => $config['lockAcquireTimeout'], + 'connectionAttempts' => $config['connectionAttempts'], + 'persistent' => $config['persistent'], + )); $sessionConfig = self::fixClientConfig($sessionConfig); $this->buildClient('sessionHandler', array('debugger' => FALSE) + $sessionConfig); @@ -239,10 +239,10 @@ protected function loadSession(array $config) return; } - $constructParams = array( - $this->prefix('@sessionHandler_client'), - $sessionConfig['namespace'], - ); + $constructParams = array( + $this->prefix('@sessionHandler_client'), + $sessionConfig['namespace'], + ); $builder->addDefinition($this->prefix('sessionHandler')) ->setClass('Kdyby\Redis\RedisSessionHandler', $constructParams); From 8deb7c6113a9bfabdc6295e814bc3d9ed6204982 Mon Sep 17 00:00:00 2001 From: Vladimir Bosiak Date: Tue, 16 Feb 2016 14:08:50 +0100 Subject: [PATCH 7/7] fix build error [ERROR] KdybyTests/Redis/RedisNamespaceStorage.phpt:29 Mixed tabs and spaces indentation [ERROR] KdybyTests/Redis/RedisNamespaceJournal.phpt:28 Mixed tabs and spaces indentation --- .../Redis/RedisNamespaceJournal.phpt | 470 +++++------ .../Redis/RedisNamespaceStorage.phpt | 752 +++++++++--------- 2 files changed, 611 insertions(+), 611 deletions(-) diff --git a/tests/KdybyTests/Redis/RedisNamespaceJournal.phpt b/tests/KdybyTests/Redis/RedisNamespaceJournal.phpt index 28533ee..60b4d8e 100644 --- a/tests/KdybyTests/Redis/RedisNamespaceJournal.phpt +++ b/tests/KdybyTests/Redis/RedisNamespaceJournal.phpt @@ -25,277 +25,277 @@ require_once __DIR__ . '/../bootstrap.php'; class RedisNamespaceJournalTest extends AbstractRedisTestCase { - /** - * @var RedisLuaJournal|Nette\Caching\Storages\IJournal - */ - private $journal; + /** + * @var RedisLuaJournal|Nette\Caching\Storages\IJournal + */ + private $journal; - protected function setUp() - { - parent::setUp(); + protected function setUp() + { + parent::setUp(); - $this->journal = new RedisLuaJournal($this->getClient(), 'foo'); - } + $this->journal = new RedisLuaJournal($this->getClient(), 'foo'); + } - public function testRemoveByTag() - { - // Assert::same(0, count($this->getClient()->keys('*'))); - $this->assertKeysInDatabase(0); + public function testRemoveByTag() + { + // Assert::same(0, count($this->getClient()->keys('*'))); + $this->assertKeysInDatabase(0); - $this->journal->write('ok_test1', array( - Cache::TAGS => array('test:homepage'), - )); + $this->journal->write('ok_test1', array( + Cache::TAGS => array('test:homepage'), + )); - // Assert::same(2, count($this->getClient()->keys('*'))); - $this->assertKeysInDatabase(2); + // Assert::same(2, count($this->getClient()->keys('*'))); + $this->assertKeysInDatabase(2); - $result = $this->journal->clean(array(Cache::TAGS => array('test:homepage'))); - Assert::same(1, count($result)); - Assert::same('ok_test1', $result[0]); - } + $result = $this->journal->clean(array(Cache::TAGS => array('test:homepage'))); + Assert::same(1, count($result)); + Assert::same('ok_test1', $result[0]); + } - public function testRemovingByMultipleTags_OneIsNotDefined() - { - $this->journal->write('ok_test2', array( - Cache::TAGS => array('test:homepage', 'test:homepage2'), - )); + public function testRemovingByMultipleTags_OneIsNotDefined() + { + $this->journal->write('ok_test2', array( + Cache::TAGS => array('test:homepage', 'test:homepage2'), + )); - $result = $this->journal->clean(array(Cache::TAGS => array('test:homepage2'))); - Assert::same(1, count($result)); - Assert::same('ok_test2', $result[0]); - } + $result = $this->journal->clean(array(Cache::TAGS => array('test:homepage2'))); + Assert::same(1, count($result)); + Assert::same('ok_test2', $result[0]); + } - public function testRemovingByMultipleTags_BothAreOnOneEntry() - { - $this->journal->write('ok_test2b', array( - Cache::TAGS => array('test:homepage', 'test:homepage2'), - )); + public function testRemovingByMultipleTags_BothAreOnOneEntry() + { + $this->journal->write('ok_test2b', array( + Cache::TAGS => array('test:homepage', 'test:homepage2'), + )); - $result = $this->journal->clean(array(Cache::TAGS => array('test:homepage', 'test:homepage2'))); - Assert::same(1, count($result)); - Assert::same('ok_test2b', $result[0]); - } + $result = $this->journal->clean(array(Cache::TAGS => array('test:homepage', 'test:homepage2'))); + Assert::same(1, count($result)); + Assert::same('ok_test2b', $result[0]); + } - public function testRemoveByMultipleTags_TwoSameTags() - { - $this->journal->write('ok_test2c', array( - Cache::TAGS => array('test:homepage', 'test:homepage'), - )); + public function testRemoveByMultipleTags_TwoSameTags() + { + $this->journal->write('ok_test2c', array( + Cache::TAGS => array('test:homepage', 'test:homepage'), + )); - $result = $this->journal->clean(array(Cache::TAGS => array('test:homepage', 'test:homepage'))); - Assert::same(1, count($result)); - Assert::same('ok_test2c', $result[0]); - } + $result = $this->journal->clean(array(Cache::TAGS => array('test:homepage', 'test:homepage'))); + Assert::same(1, count($result)); + Assert::same('ok_test2c', $result[0]); + } - public function testRemoveByTagAndPriority() - { - $this->journal->write('ok_test2d', array( - Cache::TAGS => array('test:homepage'), - Cache::PRIORITY => 15, - )); + public function testRemoveByTagAndPriority() + { + $this->journal->write('ok_test2d', array( + Cache::TAGS => array('test:homepage'), + Cache::PRIORITY => 15, + )); - $result = $this->journal->clean(array(Cache::TAGS => array('test:homepage'), Cache::PRIORITY => 20)); - Assert::same(1, count($result)); - Assert::same('ok_test2d', $result[0]); - } + $result = $this->journal->clean(array(Cache::TAGS => array('test:homepage'), Cache::PRIORITY => 20)); + Assert::same(1, count($result)); + Assert::same('ok_test2d', $result[0]); + } - public function testRemoveByPriority() - { - $this->journal->write('ok_test3', array( - Cache::PRIORITY => 10, - )); + public function testRemoveByPriority() + { + $this->journal->write('ok_test3', array( + Cache::PRIORITY => 10, + )); - $result = $this->journal->clean(array(Cache::PRIORITY => 10)); - Assert::same(1, count($result)); - Assert::same('ok_test3', $result[0]); - } + $result = $this->journal->clean(array(Cache::PRIORITY => 10)); + Assert::same(1, count($result)); + Assert::same('ok_test3', $result[0]); + } - public function testPriorityAndTag_CleanByTag() - { - $this->journal->write('ok_test4', array( - Cache::TAGS => array('test:homepage'), - Cache::PRIORITY => 10, - )); + public function testPriorityAndTag_CleanByTag() + { + $this->journal->write('ok_test4', array( + Cache::TAGS => array('test:homepage'), + Cache::PRIORITY => 10, + )); - $result = $this->journal->clean(array(Cache::TAGS => array('test:homepage'))); - Assert::same(1, count($result)); - Assert::same('ok_test4', $result[0]); - } + $result = $this->journal->clean(array(Cache::TAGS => array('test:homepage'))); + Assert::same(1, count($result)); + Assert::same('ok_test4', $result[0]); + } - public function testPriorityAndTag_CleanByPriority() - { - $this->journal->write('ok_test5', array( - Cache::TAGS => array('test:homepage'), - Cache::PRIORITY => 10, - )); + public function testPriorityAndTag_CleanByPriority() + { + $this->journal->write('ok_test5', array( + Cache::TAGS => array('test:homepage'), + Cache::PRIORITY => 10, + )); - $result = $this->journal->clean(array(Cache::PRIORITY => 10)); - Assert::same(1, count($result)); - Assert::same('ok_test5', $result[0]); - } + $result = $this->journal->clean(array(Cache::PRIORITY => 10)); + Assert::same(1, count($result)); + Assert::same('ok_test5', $result[0]); + } - public function testMultipleWritesAndMultipleClean() - { - for ($i = 1; $i <= 10; $i++) { - $this->journal->write('ok_test6_' . $i, array( - Cache::TAGS => array('test:homepage', 'test:homepage/' . $i), - Cache::PRIORITY => $i, - )); - } + public function testMultipleWritesAndMultipleClean() + { + for ($i = 1; $i <= 10; $i++) { + $this->journal->write('ok_test6_' . $i, array( + Cache::TAGS => array('test:homepage', 'test:homepage/' . $i), + Cache::PRIORITY => $i, + )); + } - $result = $this->journal->clean(array(Cache::PRIORITY => 5)); - Assert::same(5, count($result), "clean priority lower then 5"); - Assert::same('ok_test6_1', $result[0], "clean priority lower then 5"); + $result = $this->journal->clean(array(Cache::PRIORITY => 5)); + Assert::same(5, count($result), "clean priority lower then 5"); + Assert::same('ok_test6_1', $result[0], "clean priority lower then 5"); - $result = $this->journal->clean(array(Cache::TAGS => array('test:homepage/7'))); - Assert::same(1, count($result), "clean tag homepage/7"); - Assert::same('ok_test6_7', $result[0], "clean tag homepage/7"); + $result = $this->journal->clean(array(Cache::TAGS => array('test:homepage/7'))); + Assert::same(1, count($result), "clean tag homepage/7"); + Assert::same('ok_test6_7', $result[0], "clean tag homepage/7"); - $result = $this->journal->clean(array(Cache::TAGS => array('test:homepage/4'))); - Assert::same(0, count($result), "clean non exists tag"); + $result = $this->journal->clean(array(Cache::TAGS => array('test:homepage/4'))); + Assert::same(0, count($result), "clean non exists tag"); - $result = $this->journal->clean(array(Cache::PRIORITY => 4)); - Assert::same(0, count($result), "clean non exists priority"); + $result = $this->journal->clean(array(Cache::PRIORITY => 4)); + Assert::same(0, count($result), "clean non exists priority"); - $result = $this->journal->clean(array(Cache::TAGS => array('test:homepage'))); - Assert::same(4, count($result), "clean other"); - sort($result); - Assert::same(array('ok_test6_10', 'ok_test6_6', 'ok_test6_8', 'ok_test6_9'), $result, "clean other"); - } + $result = $this->journal->clean(array(Cache::TAGS => array('test:homepage'))); + Assert::same(4, count($result), "clean other"); + sort($result); + Assert::same(array('ok_test6_10', 'ok_test6_6', 'ok_test6_8', 'ok_test6_9'), $result, "clean other"); + } - public function testSpecialChars() - { - $this->journal->write('ok_test7ščřžýáíé', array( - Cache::TAGS => array('čšřýýá', 'ýřžčýž/10') - )); + public function testSpecialChars() + { + $this->journal->write('ok_test7ščřžýáíé', array( + Cache::TAGS => array('čšřýýá', 'ýřžčýž/10') + )); - $result = $this->journal->clean(array(Cache::TAGS => array('čšřýýá'))); - Assert::same(1, count($result)); - Assert::same('ok_test7ščřžýáíé', $result[0]); - } + $result = $this->journal->clean(array(Cache::TAGS => array('čšřýýá'))); + Assert::same(1, count($result)); + Assert::same('ok_test7ščřžýáíé', $result[0]); + } - public function testDuplicates_SameTag() - { - $this->journal->write('ok_test_a', array( - Cache::TAGS => array('homepage') - )); + public function testDuplicates_SameTag() + { + $this->journal->write('ok_test_a', array( + Cache::TAGS => array('homepage') + )); - $this->journal->write('ok_test_a', array( - Cache::TAGS => array('homepage') - )); + $this->journal->write('ok_test_a', array( + Cache::TAGS => array('homepage') + )); - $result = $this->journal->clean(array(Cache::TAGS => array('homepage'))); - Assert::same(1, count($result)); - Assert::same('ok_test_a', $result[0]); - } + $result = $this->journal->clean(array(Cache::TAGS => array('homepage'))); + Assert::same(1, count($result)); + Assert::same('ok_test_a', $result[0]); + } - public function testDuplicates_SamePriority() - { - $this->journal->write('ok_test_b', array( - Cache::PRIORITY => 12 - )); + public function testDuplicates_SamePriority() + { + $this->journal->write('ok_test_b', array( + Cache::PRIORITY => 12 + )); - $this->journal->write('ok_test_b', array( - Cache::PRIORITY => 12 - )); + $this->journal->write('ok_test_b', array( + Cache::PRIORITY => 12 + )); - $result = $this->journal->clean(array(Cache::PRIORITY => 12)); - Assert::same(1, count($result)); - Assert::same('ok_test_b', $result[0]); - } + $result = $this->journal->clean(array(Cache::PRIORITY => 12)); + Assert::same(1, count($result)); + Assert::same('ok_test_b', $result[0]); + } - public function testDuplicates_DifferentTags() - { - $this->journal->write('ok_test_ba', array( - Cache::TAGS => array('homepage') - )); + public function testDuplicates_DifferentTags() + { + $this->journal->write('ok_test_ba', array( + Cache::TAGS => array('homepage') + )); - $this->journal->write('ok_test_ba', array( - Cache::TAGS => array('homepage2') - )); + $this->journal->write('ok_test_ba', array( + Cache::TAGS => array('homepage2') + )); - $result = $this->journal->clean(array(Cache::TAGS => array('homepage'))); - Assert::same(0, count($result)); + $result = $this->journal->clean(array(Cache::TAGS => array('homepage'))); + Assert::same(0, count($result)); - $result2 = $this->journal->clean(array(Cache::TAGS => array('homepage2'))); - Assert::same(1, count($result2)); - Assert::same('ok_test_ba', $result2[0]); - } + $result2 = $this->journal->clean(array(Cache::TAGS => array('homepage2'))); + Assert::same(1, count($result2)); + Assert::same('ok_test_ba', $result2[0]); + } - public function testDuplicates_DifferentPriorities() - { - $this->journal->write('ok_test_bb', array( - Cache::PRIORITY => 15 - )); + public function testDuplicates_DifferentPriorities() + { + $this->journal->write('ok_test_bb', array( + Cache::PRIORITY => 15 + )); - $this->journal->write('ok_test_bb', array( - Cache::PRIORITY => 20 - )); + $this->journal->write('ok_test_bb', array( + Cache::PRIORITY => 20 + )); - $result = $this->journal->clean(array(Cache::PRIORITY => 30)); - Assert::same(1, count($result)); - Assert::same('ok_test_bb', $result[0]); - } + $result = $this->journal->clean(array(Cache::PRIORITY => 30)); + Assert::same(1, count($result)); + Assert::same('ok_test_bb', $result[0]); + } - public function testCleanAll() - { - $this->journal->write('ok_test_all_tags', array( - Cache::TAGS => array('test:all', 'test:all') - )); + public function testCleanAll() + { + $this->journal->write('ok_test_all_tags', array( + Cache::TAGS => array('test:all', 'test:all') + )); - $this->journal->write('ok_test_all_priority', array( - Cache::PRIORITY => 5, - )); + $this->journal->write('ok_test_all_priority', array( + Cache::PRIORITY => 5, + )); - $result = $this->journal->clean(array(Cache::ALL => TRUE)); - Assert::null($result); + $result = $this->journal->clean(array(Cache::ALL => TRUE)); + Assert::null($result); - $result2 = $this->journal->clean(array(Cache::TAGS => 'test:all')); - Assert::true(empty($result2)); - } + $result2 = $this->journal->clean(array(Cache::TAGS => 'test:all')); + Assert::true(empty($result2)); + } - public function testBigCache() - { - if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { - Tester\Helpers::skip("Linux only"); - } + public function testBigCache() + { + if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { + Tester\Helpers::skip("Linux only"); + } - $script = $this->cacheGeneratorScripts(); - $script .= <<cacheGeneratorScripts(); + $script .= <<getClient()->evalScript($script)); - $this->assertKeysInDatabase(5100); + Assert::true($this->getClient()->evalScript($script)); + $this->assertKeysInDatabase(5100); - $this->journal->clean(array(Cache::TAGS => 'test.4356')); - $this->assertKeysInDatabase(0); - } + $this->journal->clean(array(Cache::TAGS => 'test.4356')); + $this->assertKeysInDatabase(0); + } - public function testBigCache_ShitloadOfEntries() - { - if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { - Tester\Helpers::skip("Linux only"); - } + public function testBigCache_ShitloadOfEntries() + { + if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { + Tester\Helpers::skip("Linux only"); + } - $script = $this->cacheGeneratorScripts(); - $script .= <<cacheGeneratorScripts(); + $script .= <<getClient()->evalScript($script)); - $this->assertKeysInDatabase(100001); + Assert::true($this->getClient()->evalScript($script)); + $this->assertKeysInDatabase(100001); - $this->journal->clean(array(Cache::TAGS => 'kdyby')); - $this->assertKeysInDatabase(0); - } + $this->journal->clean(array(Cache::TAGS => 'kdyby')); + $this->assertKeysInDatabase(0); + } - protected function assertKeysInDatabase($number) - { - $dbNum = $this->getClient()->getDriver()->getDBNum(); - $dbInfo = $this->getClient()->info('db' . $dbNum); + protected function assertKeysInDatabase($number) + { + $dbNum = $this->getClient()->getDriver()->getDBNum(); + $dbInfo = $this->getClient()->info('db' . $dbNum); - if ($number > 0 && !$dbInfo) { - Assert::fail("Number of keys in database couldn't be determined"); - } + if ($number > 0 && !$dbInfo) { + Assert::fail("Number of keys in database couldn't be determined"); + } - Assert::equal($number, $dbInfo ? (int) $dbInfo['keys'] : 0); - } + Assert::equal($number, $dbInfo ? (int) $dbInfo['keys'] : 0); + } - private function cacheGeneratorScripts() - { - $script = "local ARGV = {}\n"; - $script .= 'ARGV[2] = "foo:"'. "\n"; - $script .= file_get_contents(__DIR__ . '/../../../src/Kdyby/Redis/scripts/common.lua'); - $script .= <<journal->write($key, array( - Cache::TAGS => array("test:nullByte") - )); + public function testNullByte() + { + $key = "prefix\x00test:\\2"; + $this->journal->write($key, array( + Cache::TAGS => array("test:nullByte") + )); - $result = $this->journal->clean(array( - Cache::TAGS => array("test:nullByte") - )); - Assert::same(array($key), $result); - } + $result = $this->journal->clean(array( + Cache::TAGS => array("test:nullByte") + )); + Assert::same(array($key), $result); + } } diff --git a/tests/KdybyTests/Redis/RedisNamespaceStorage.phpt b/tests/KdybyTests/Redis/RedisNamespaceStorage.phpt index ed5d017..aec086d 100644 --- a/tests/KdybyTests/Redis/RedisNamespaceStorage.phpt +++ b/tests/KdybyTests/Redis/RedisNamespaceStorage.phpt @@ -26,382 +26,382 @@ require_once __DIR__ . '/../bootstrap.php'; class RedisNamespaceStorageTest extends AbstractRedisTestCase { - /** - * @var \Kdyby\Redis\RedisStorage - */ - private $storage; - - public function setUp() - { - parent::setUp(); - $this->storage = new RedisStorage($this->client, NULL, 'foo'); - } - - public function testBasics() - { - list($key, $value) = $this->basicData(); - - $cache = new Cache($this->storage); - Assert::null($cache->load($key), "Cache content"); - - // Writing cache... - $cache->save($key, $value); - Assert::same($value, $cache->load($key), "Is cache ok?"); - - // Removing from cache using unset()... - $cache->remove($key); - Assert::false($cache->load($key) !== null, "Is cached?"); - - // Removing from cache using set NULL... - $cache->save($key, $value); - $cache->save($key, null); - Assert::false($cache->load($key) !== null, "Is cached?"); - } - - /** - * key and data with special chars - * - * @return array - */ - public function basicData() - { - return array( - $key = array(1, true), - $value = range("\x00", "\xFF"), - ); - } - - /** - * @param mixed $val - * @return mixed - */ - public static function dependency($val) - { - return $val; - } - - - public function testCallbacks() - { - $key = 'nette'; - $value = 'rulez'; - - $cache = new Cache($this->storage); - $cb = get_called_class() . '::dependency'; - - // Writing cache... - $cache->save($key, $value, array( - Cache::CALLBACKS => array(array($cb, 1)), - )); - - Assert::true($cache->load($key) !== null, 'Is cached?'); - - // Writing cache... - $cache->save($key, $value, array( - Cache::CALLBACKS => array(array($cb, 0)), - )); - - Assert::false($cache->load($key) !== null, 'Is cached?'); - } - - - public function testCleanAll() - { - $cacheA = new Cache($this->storage); - $cacheB = new Cache($this->storage, 'B'); - - $cacheA->save('test1', 'David'); - $cacheA->save('test2', 'Grudl'); - $cacheB->save('test1', 'divaD'); - $cacheB->save('test2', 'ldurG'); - - Assert::same('David Grudl divaD ldurG', implode(' ', array( - $cacheA->load('test1'), - $cacheA->load('test2'), - $cacheB->load('test1'), - $cacheB->load('test2'), - ))); - - $this->storage->clean(array(Cache::ALL => true)); - - Assert::null($cacheA->load('test1')); - Assert::null($cacheA->load('test2')); - Assert::null($cacheB->load('test1')); - Assert::null($cacheB->load('test2')); - } - - - public function testExpiration() - { - $key = 'nette'; - $value = 'rulez'; - - $cache = new Cache($this->storage); - - // Writing cache... - $cache->save($key, $value, array( - Cache::EXPIRATION => time() + 3, - )); - - // Sleeping 1 second - sleep(1); - Assert::true($cache->load($key) !== null, 'Is cached?'); - - // Sleeping 3 seconds - sleep(3); - Assert::false($cache->load($key) !== null, 'Is cached?'); - } - - - public function testIntKeys() - { - // key and data with special chars - $key = 0; - $value = range("\x00", "\xFF"); - - $cache = new Cache($this->storage); - Assert::false($cache->load($key) !== null, 'Is cached?'); - Assert::null($cache->load($key), 'Cache content'); - - // Writing cache... - $cache->save($key, $value); - Assert::true($cache->load($key) !== null, 'Is cached?'); - Assert::same($value, $cache->load($key), 'Is cache ok?'); - - // Removing from cache using unset()... - $cache->remove($key); - Assert::false($cache->load($key) !== null, 'Is cached?'); - - // Removing from cache using set NULL... - $cache->save($key, $value); - $cache->save($key, null); - Assert::false($cache->load($key) !== null, 'Is cached?'); - } - - - public function testDependentItems() - { - $key = 'nette'; - $value = 'rulez'; - - $cache = new Cache($this->storage); - - // Writing cache... - $cache->save($key, $value, array( - Cache::ITEMS => array('dependent'), - )); - Assert::true($cache->load($key) !== null, 'Is cached?'); - - // Modifing dependent cached item - $cache->save('dependent', 'hello world'); - Assert::false($cache->load($key) !== null, 'Is cached?'); - - // Writing cache... - $cache->save($key, $value, array( - Cache::ITEMS => 'dependent', - )); - Assert::true($cache->load($key) !== null, 'Is cached?'); - - // Modifing dependent cached item - sleep(2); - $cache->save('dependent', 'hello europe'); - Assert::false($cache->load($key) !== null, 'Is cached?'); - - // Writing cache... - $cache->save($key, $value, array( - Cache::ITEMS => 'dependent', - )); - Assert::true($cache->load($key) !== null, 'Is cached?'); - - // Deleting dependent cached item - $cache->save('dependent', null); - Assert::false($cache->load($key) !== null, 'Is cached?'); - } - - - /** - */ - public function testLoadOrSave() - { - // key and data with special chars - $key = '../' . implode('', range("\x00", "\x1F")); - $value = range("\x00", "\xFF"); - - $cache = new Cache($this->storage); - Assert::false($cache->load($key) !== null, 'Is cached?'); - - // Writing cache using Closure... - $res = $cache->load($key, function (& $dp) use ($value) { - $dp = array( - Cache::EXPIRATION => time() + 2, - ); - - return $value; - }); - - Assert::same($value, $res, 'Is result ok?'); - Assert::same($value, $cache->load($key), 'Is cache ok?'); - - // Sleeping 3 seconds - sleep(3); - Assert::false($cache->load($key) !== null, 'Is cached?'); - } - - - public function testNamespace() - { - $cacheA = new Cache($this->storage, 'a'); - $cacheB = new Cache($this->storage, 'b'); - - // Writing cache... - $cacheA->save('key', 'hello'); - $cacheB->save('key', 'world'); - - Assert::true($cacheA->load('key') !== null, 'Is cached #1?'); - Assert::true($cacheB->load('key') !== null, 'Is cached #2?'); - Assert::same('hello', $cacheA->load('key'), 'Is cache ok #1?'); - Assert::same('world', $cacheB->load('key'), 'Is cache ok #2?'); - - // Removing from cache #2 using unset()... - $cacheB->remove('key'); - Assert::true($cacheA->load('key') !== null, 'Is cached #1?'); - Assert::false($cacheB->load('key') !== null, 'Is cached #2?'); - } - - - public function testPriority() - { - $storage = new RedisStorage($this->client, new Nette\Caching\Storages\FileJournal(TEMP_DIR)); - $cache = new Cache($storage); - - // Writing cache... - $cache->save('key1', 'value1', array( - Cache::PRIORITY => 100, - )); - $cache->save('key2', 'value2', array( - Cache::PRIORITY => 200, - )); - $cache->save('key3', 'value3', array( - Cache::PRIORITY => 300, - )); - $cache->save('key4', 'value4'); - - // Cleaning by priority... - $cache->clean(array( - Cache::PRIORITY => '200', - )); - - Assert::false($cache->load('key1') !== null, 'Is cached key1?'); - Assert::false($cache->load('key2') !== null, 'Is cached key2?'); - Assert::true($cache->load('key3') !== null, 'Is cached key3?'); - Assert::true($cache->load('key4') !== null, 'Is cached key4?'); - } - - - public function testPriority_Optimized() - { - $storage = new RedisStorage($this->client, new RedisLuaJournal($this->client)); - $cache = new Cache($storage); - - // Writing cache... - $cache->save('key1', 'value1', array( - Cache::PRIORITY => 100, - )); - $cache->save('key2', 'value2', array( - Cache::PRIORITY => 200, - )); - $cache->save('key3', 'value3', array( - Cache::PRIORITY => 300, - )); - $cache->save('key4', 'value4'); - - // Cleaning by priority... - $cache->clean(array( - Cache::PRIORITY => '200', - )); - - Assert::false($cache->load('key1') !== null, 'Is cached key1?'); - Assert::false($cache->load('key2') !== null, 'Is cached key2?'); - Assert::true($cache->load('key3') !== null, 'Is cached key3?'); - Assert::true($cache->load('key4') !== null, 'Is cached key4?'); - } - - - public function testTags() - { - $storage = new RedisStorage($this->client, new Nette\Caching\Storages\FileJournal(TEMP_DIR)); - $cache = new Cache($storage); - - // Writing cache... - $cache->save('key1', 'value1', array( - Cache::TAGS => array('one', 'two'), - )); - $cache->save('key2', 'value2', array( - Cache::TAGS => array('one', 'three'), - )); - $cache->save('key3', 'value3', array( - Cache::TAGS => array('two', 'three'), - )); - $cache->save('key4', 'value4'); - - // Cleaning by tags... - $cache->clean(array( - Cache::TAGS => 'one', - )); - - Assert::false($cache->load('key1') !== null, 'Is cached key1?'); - Assert::false($cache->load('key2') !== null, 'Is cached key2?'); - Assert::true($cache->load('key3') !== null, 'Is cached key3?'); - Assert::true($cache->load('key4') !== null, 'Is cached key4?'); - } - - - public function testTags_Optimized() - { - $storage = new RedisStorage($this->client, new RedisLuaJournal($this->client)); - $cache = new Cache($storage); - - // Writing cache... - $cache->save('key1', 'value1', array( - Cache::TAGS => array('one', 'two'), - )); - $cache->save('key2', 'value2', array( - Cache::TAGS => array('one', 'three'), - )); - $cache->save('key3', 'value3', array( - Cache::TAGS => array('two', 'three'), - )); - $cache->save('key4', 'value4'); - - // Cleaning by tags... - $cache->clean(array( - Cache::TAGS => 'one', - )); - - Assert::false($cache->load('key1') !== null, 'Is cached key1?'); - Assert::false($cache->load('key2') !== null, 'Is cached key2?'); - Assert::true($cache->load('key3') !== null, 'Is cached key3?'); - Assert::true($cache->load('key4') !== null, 'Is cached key4?'); - } - - - public function testMultiRead() - { - $storage = $this->storage; - - $storage->write('A', 1, array()); - $storage->write('B', 2, array()); - $storage->write('C', false, array()); - $storage->write('E', null, array()); - - Assert::equal(array( - 'A' => 1, - 'B' => 2, - 'C' => false, - 'D' => null, - 'E' => null, - ), $storage->multiRead(array('A', 'B', 'C', 'D', 'E'))); - } + /** + * @var \Kdyby\Redis\RedisStorage + */ + private $storage; + + public function setUp() + { + parent::setUp(); + $this->storage = new RedisStorage($this->client, NULL, 'foo'); + } + + public function testBasics() + { + list($key, $value) = $this->basicData(); + + $cache = new Cache($this->storage); + Assert::null($cache->load($key), "Cache content"); + + // Writing cache... + $cache->save($key, $value); + Assert::same($value, $cache->load($key), "Is cache ok?"); + + // Removing from cache using unset()... + $cache->remove($key); + Assert::false($cache->load($key) !== null, "Is cached?"); + + // Removing from cache using set NULL... + $cache->save($key, $value); + $cache->save($key, null); + Assert::false($cache->load($key) !== null, "Is cached?"); + } + + /** + * key and data with special chars + * + * @return array + */ + public function basicData() + { + return array( + $key = array(1, true), + $value = range("\x00", "\xFF"), + ); + } + + /** + * @param mixed $val + * @return mixed + */ + public static function dependency($val) + { + return $val; + } + + + public function testCallbacks() + { + $key = 'nette'; + $value = 'rulez'; + + $cache = new Cache($this->storage); + $cb = get_called_class() . '::dependency'; + + // Writing cache... + $cache->save($key, $value, array( + Cache::CALLBACKS => array(array($cb, 1)), + )); + + Assert::true($cache->load($key) !== null, 'Is cached?'); + + // Writing cache... + $cache->save($key, $value, array( + Cache::CALLBACKS => array(array($cb, 0)), + )); + + Assert::false($cache->load($key) !== null, 'Is cached?'); + } + + + public function testCleanAll() + { + $cacheA = new Cache($this->storage); + $cacheB = new Cache($this->storage, 'B'); + + $cacheA->save('test1', 'David'); + $cacheA->save('test2', 'Grudl'); + $cacheB->save('test1', 'divaD'); + $cacheB->save('test2', 'ldurG'); + + Assert::same('David Grudl divaD ldurG', implode(' ', array( + $cacheA->load('test1'), + $cacheA->load('test2'), + $cacheB->load('test1'), + $cacheB->load('test2'), + ))); + + $this->storage->clean(array(Cache::ALL => true)); + + Assert::null($cacheA->load('test1')); + Assert::null($cacheA->load('test2')); + Assert::null($cacheB->load('test1')); + Assert::null($cacheB->load('test2')); + } + + + public function testExpiration() + { + $key = 'nette'; + $value = 'rulez'; + + $cache = new Cache($this->storage); + + // Writing cache... + $cache->save($key, $value, array( + Cache::EXPIRATION => time() + 3, + )); + + // Sleeping 1 second + sleep(1); + Assert::true($cache->load($key) !== null, 'Is cached?'); + + // Sleeping 3 seconds + sleep(3); + Assert::false($cache->load($key) !== null, 'Is cached?'); + } + + + public function testIntKeys() + { + // key and data with special chars + $key = 0; + $value = range("\x00", "\xFF"); + + $cache = new Cache($this->storage); + Assert::false($cache->load($key) !== null, 'Is cached?'); + Assert::null($cache->load($key), 'Cache content'); + + // Writing cache... + $cache->save($key, $value); + Assert::true($cache->load($key) !== null, 'Is cached?'); + Assert::same($value, $cache->load($key), 'Is cache ok?'); + + // Removing from cache using unset()... + $cache->remove($key); + Assert::false($cache->load($key) !== null, 'Is cached?'); + + // Removing from cache using set NULL... + $cache->save($key, $value); + $cache->save($key, null); + Assert::false($cache->load($key) !== null, 'Is cached?'); + } + + + public function testDependentItems() + { + $key = 'nette'; + $value = 'rulez'; + + $cache = new Cache($this->storage); + + // Writing cache... + $cache->save($key, $value, array( + Cache::ITEMS => array('dependent'), + )); + Assert::true($cache->load($key) !== null, 'Is cached?'); + + // Modifing dependent cached item + $cache->save('dependent', 'hello world'); + Assert::false($cache->load($key) !== null, 'Is cached?'); + + // Writing cache... + $cache->save($key, $value, array( + Cache::ITEMS => 'dependent', + )); + Assert::true($cache->load($key) !== null, 'Is cached?'); + + // Modifing dependent cached item + sleep(2); + $cache->save('dependent', 'hello europe'); + Assert::false($cache->load($key) !== null, 'Is cached?'); + + // Writing cache... + $cache->save($key, $value, array( + Cache::ITEMS => 'dependent', + )); + Assert::true($cache->load($key) !== null, 'Is cached?'); + + // Deleting dependent cached item + $cache->save('dependent', null); + Assert::false($cache->load($key) !== null, 'Is cached?'); + } + + + /** + */ + public function testLoadOrSave() + { + // key and data with special chars + $key = '../' . implode('', range("\x00", "\x1F")); + $value = range("\x00", "\xFF"); + + $cache = new Cache($this->storage); + Assert::false($cache->load($key) !== null, 'Is cached?'); + + // Writing cache using Closure... + $res = $cache->load($key, function (& $dp) use ($value) { + $dp = array( + Cache::EXPIRATION => time() + 2, + ); + + return $value; + }); + + Assert::same($value, $res, 'Is result ok?'); + Assert::same($value, $cache->load($key), 'Is cache ok?'); + + // Sleeping 3 seconds + sleep(3); + Assert::false($cache->load($key) !== null, 'Is cached?'); + } + + + public function testNamespace() + { + $cacheA = new Cache($this->storage, 'a'); + $cacheB = new Cache($this->storage, 'b'); + + // Writing cache... + $cacheA->save('key', 'hello'); + $cacheB->save('key', 'world'); + + Assert::true($cacheA->load('key') !== null, 'Is cached #1?'); + Assert::true($cacheB->load('key') !== null, 'Is cached #2?'); + Assert::same('hello', $cacheA->load('key'), 'Is cache ok #1?'); + Assert::same('world', $cacheB->load('key'), 'Is cache ok #2?'); + + // Removing from cache #2 using unset()... + $cacheB->remove('key'); + Assert::true($cacheA->load('key') !== null, 'Is cached #1?'); + Assert::false($cacheB->load('key') !== null, 'Is cached #2?'); + } + + + public function testPriority() + { + $storage = new RedisStorage($this->client, new Nette\Caching\Storages\FileJournal(TEMP_DIR)); + $cache = new Cache($storage); + + // Writing cache... + $cache->save('key1', 'value1', array( + Cache::PRIORITY => 100, + )); + $cache->save('key2', 'value2', array( + Cache::PRIORITY => 200, + )); + $cache->save('key3', 'value3', array( + Cache::PRIORITY => 300, + )); + $cache->save('key4', 'value4'); + + // Cleaning by priority... + $cache->clean(array( + Cache::PRIORITY => '200', + )); + + Assert::false($cache->load('key1') !== null, 'Is cached key1?'); + Assert::false($cache->load('key2') !== null, 'Is cached key2?'); + Assert::true($cache->load('key3') !== null, 'Is cached key3?'); + Assert::true($cache->load('key4') !== null, 'Is cached key4?'); + } + + + public function testPriority_Optimized() + { + $storage = new RedisStorage($this->client, new RedisLuaJournal($this->client)); + $cache = new Cache($storage); + + // Writing cache... + $cache->save('key1', 'value1', array( + Cache::PRIORITY => 100, + )); + $cache->save('key2', 'value2', array( + Cache::PRIORITY => 200, + )); + $cache->save('key3', 'value3', array( + Cache::PRIORITY => 300, + )); + $cache->save('key4', 'value4'); + + // Cleaning by priority... + $cache->clean(array( + Cache::PRIORITY => '200', + )); + + Assert::false($cache->load('key1') !== null, 'Is cached key1?'); + Assert::false($cache->load('key2') !== null, 'Is cached key2?'); + Assert::true($cache->load('key3') !== null, 'Is cached key3?'); + Assert::true($cache->load('key4') !== null, 'Is cached key4?'); + } + + + public function testTags() + { + $storage = new RedisStorage($this->client, new Nette\Caching\Storages\FileJournal(TEMP_DIR)); + $cache = new Cache($storage); + + // Writing cache... + $cache->save('key1', 'value1', array( + Cache::TAGS => array('one', 'two'), + )); + $cache->save('key2', 'value2', array( + Cache::TAGS => array('one', 'three'), + )); + $cache->save('key3', 'value3', array( + Cache::TAGS => array('two', 'three'), + )); + $cache->save('key4', 'value4'); + + // Cleaning by tags... + $cache->clean(array( + Cache::TAGS => 'one', + )); + + Assert::false($cache->load('key1') !== null, 'Is cached key1?'); + Assert::false($cache->load('key2') !== null, 'Is cached key2?'); + Assert::true($cache->load('key3') !== null, 'Is cached key3?'); + Assert::true($cache->load('key4') !== null, 'Is cached key4?'); + } + + + public function testTags_Optimized() + { + $storage = new RedisStorage($this->client, new RedisLuaJournal($this->client)); + $cache = new Cache($storage); + + // Writing cache... + $cache->save('key1', 'value1', array( + Cache::TAGS => array('one', 'two'), + )); + $cache->save('key2', 'value2', array( + Cache::TAGS => array('one', 'three'), + )); + $cache->save('key3', 'value3', array( + Cache::TAGS => array('two', 'three'), + )); + $cache->save('key4', 'value4'); + + // Cleaning by tags... + $cache->clean(array( + Cache::TAGS => 'one', + )); + + Assert::false($cache->load('key1') !== null, 'Is cached key1?'); + Assert::false($cache->load('key2') !== null, 'Is cached key2?'); + Assert::true($cache->load('key3') !== null, 'Is cached key3?'); + Assert::true($cache->load('key4') !== null, 'Is cached key4?'); + } + + + public function testMultiRead() + { + $storage = $this->storage; + + $storage->write('A', 1, array()); + $storage->write('B', 2, array()); + $storage->write('C', false, array()); + $storage->write('E', null, array()); + + Assert::equal(array( + 'A' => 1, + 'B' => 2, + 'C' => false, + 'D' => null, + 'E' => null, + ), $storage->multiRead(array('A', 'B', 'C', 'D', 'E'))); + } } \run(new RedisNamespaceStorageTest());