Skip to content

Commit 90c0a03

Browse files
[Cache] Add generic TagAwareAdapter wrapper (replaces TagAwareRedisAdapter)
1 parent 64ace10 commit 90c0a03

File tree

9 files changed

+427
-545
lines changed

9 files changed

+427
-545
lines changed

src/Symfony/Component/Cache/Adapter/AbstractTagAwareAdapter.php

Lines changed: 0 additions & 217 deletions
This file was deleted.

src/Symfony/Component/Cache/Adapter/RedisAdapter.php

Lines changed: 99 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
namespace Symfony\Component\Cache\Adapter;
1313

1414
use Predis\Connection\Factory;
15+
use Predis\Connection\Aggregate\PredisCluster;
16+
use Predis\Connection\Aggregate\RedisCluster;
1517
use Symfony\Component\Cache\Exception\InvalidArgumentException;
1618

1719
/**
@@ -20,23 +22,29 @@
2022
*/
2123
class RedisAdapter extends AbstractAdapter
2224
{
23-
use RedisAdapterTrait;
24-
2525
private static $defaultConnectionOptions = array(
2626
'class' => null,
2727
'persistent' => 0,
2828
'timeout' => 0,
2929
'read_timeout' => 0,
3030
'retry_interval' => 0,
3131
);
32+
private $redis;
3233

3334
/**
3435
* @param \Redis|\RedisArray|\RedisCluster|\Predis\Client $redisClient
3536
*/
3637
public function __construct($redisClient, $namespace = '', $defaultLifetime = 0)
3738
{
3839
parent::__construct($namespace, $defaultLifetime);
39-
$this->setRedis($redisClient, $namespace);
40+
41+
if (preg_match('#[^-+_.A-Za-z0-9]#', $namespace, $match)) {
42+
throw new InvalidArgumentException(sprintf('RedisAdapter namespace contains "%s" but only characters in [-+_.A-Za-z0-9] are allowed.', $match[0]));
43+
}
44+
if (!$redisClient instanceof \Redis && !$redisClient instanceof \RedisArray && !$redisClient instanceof \RedisCluster && !$redisClient instanceof \Predis\Client) {
45+
throw new InvalidArgumentException(sprintf('%s() expects parameter 1 to be Redis, RedisArray, RedisCluster or Predis\Client, %s given', __METHOD__, is_object($redisClient) ? get_class($redisClient) : gettype($redisClient)));
46+
}
47+
$this->redis = $redisClient;
4048
}
4149

4250
/**
@@ -149,6 +157,51 @@ protected function doHave($id)
149157
return (bool) $this->redis->exists($id);
150158
}
151159

160+
/**
161+
* {@inheritdoc}
162+
*/
163+
protected function doClear($namespace)
164+
{
165+
// When using a native Redis cluster, clearing the cache cannot work and always returns false.
166+
// Clearing the cache should then be done by any other means (e.g. by restarting the cluster).
167+
168+
$hosts = array($this->redis);
169+
$evalArgs = array(array($namespace), 0);
170+
171+
if ($this->redis instanceof \Predis\Client) {
172+
$evalArgs = array(0, $namespace);
173+
174+
$connection = $this->redis->getConnection();
175+
if ($connection instanceof PredisCluster) {
176+
$hosts = array();
177+
foreach ($connection as $c) {
178+
$hosts[] = new \Predis\Client($c);
179+
}
180+
} elseif ($connection instanceof RedisCluster) {
181+
return false;
182+
}
183+
} elseif ($this->redis instanceof \RedisArray) {
184+
foreach ($this->redis->_hosts() as $host) {
185+
$hosts[] = $this->redis->_instance($host);
186+
}
187+
} elseif ($this->redis instanceof \RedisCluster) {
188+
return false;
189+
}
190+
foreach ($hosts as $host) {
191+
if (!isset($namespace[0])) {
192+
$host->flushDb();
193+
} else {
194+
// As documented in Redis documentation (http://redis.io/commands/keys) using KEYS
195+
// can hang your server when it is executed against large databases (millions of items).
196+
// Whenever you hit this scale, it is advised to deploy one Redis database per cache pool
197+
// instead of using namespaces, so that FLUSHDB is used instead.
198+
$host->eval("local keys=redis.call('KEYS',ARGV[1]..'*') for i=1,#keys,5000 do redis.call('DEL',unpack(keys,i,math.min(i+4999,#keys))) end", $evalArgs[0], $evalArgs[1]);
199+
}
200+
}
201+
202+
return true;
203+
}
204+
152205
/**
153206
* {@inheritdoc}
154207
*/
@@ -195,4 +248,47 @@ protected function doSave(array $values, $lifetime)
195248

196249
return $failed;
197250
}
251+
252+
private function execute($command, $id, array $args, $redis = null)
253+
{
254+
array_unshift($args, $id);
255+
call_user_func_array(array($redis ?: $this->redis, $command), $args);
256+
}
257+
258+
private function pipeline(\Closure $callback)
259+
{
260+
$redis = $this->redis;
261+
262+
try {
263+
if ($redis instanceof \Predis\Client) {
264+
$redis->pipeline(function ($pipe) use ($callback) {
265+
$this->redis = $pipe;
266+
$callback(array($this, 'execute'));
267+
});
268+
} elseif ($redis instanceof \RedisArray) {
269+
$connections = array();
270+
$callback(function ($command, $id, $args) use (&$connections) {
271+
if (!isset($connections[$h = $this->redis->_target($id)])) {
272+
$connections[$h] = $this->redis->_instance($h);
273+
$connections[$h]->multi(\Redis::PIPELINE);
274+
}
275+
$this->execute($command, $id, $args, $connections[$h]);
276+
});
277+
foreach ($connections as $c) {
278+
$c->exec();
279+
}
280+
} else {
281+
$pipe = $redis->multi(\Redis::PIPELINE);
282+
try {
283+
$callback(array($this, 'execute'));
284+
} finally {
285+
if ($pipe) {
286+
$redis->exec();
287+
}
288+
}
289+
}
290+
} finally {
291+
$this->redis = $redis;
292+
}
293+
}
198294
}

0 commit comments

Comments
 (0)