Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 5155f48

Browse filesBrowse files
[Cache] improve perf when using RedisCluster by reducing roundtrips to the servers
1 parent ff1727e commit 5155f48
Copy full SHA for 5155f48

File tree

2 files changed

+74
-28
lines changed
Filter options

2 files changed

+74
-28
lines changed
+28Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Cache\Tests\Adapter;
13+
14+
class PredisRedisClusterAdapterTest extends AbstractRedisAdapterTest
15+
{
16+
public static function setupBeforeClass()
17+
{
18+
if (!$hosts = getenv('REDIS_CLUSTER_HOSTS')) {
19+
self::markTestSkipped('REDIS_CLUSTER_HOSTS env var is not defined.');
20+
}
21+
self::$redis = new \Predis\Client(explode(' ', $hosts), array('cluster' => 'redis'));
22+
}
23+
24+
public static function tearDownAfterClass()
25+
{
26+
self::$redis = null;
27+
}
28+
}

‎src/Symfony/Component/Cache/Traits/RedisTrait.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Traits/RedisTrait.php
+46-28Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
namespace Symfony\Component\Cache\Traits;
1313

1414
use Predis\Connection\Aggregate\ClusterInterface;
15-
use Predis\Connection\Aggregate\PredisCluster;
1615
use Predis\Connection\Aggregate\RedisCluster;
1716
use Predis\Connection\Factory;
1817
use Predis\Response\Status;
@@ -53,9 +52,7 @@ private function init($redisClient, $namespace, $defaultLifetime, ?MarshallerInt
5352
if (preg_match('#[^-+_.A-Za-z0-9]#', $namespace, $match)) {
5453
throw new InvalidArgumentException(sprintf('RedisAdapter namespace contains "%s" but only characters in [-+_.A-Za-z0-9] are allowed.', $match[0]));
5554
}
56-
if ($redisClient instanceof \RedisCluster) {
57-
$this->enableVersioning();
58-
} elseif (!$redisClient instanceof \Redis && !$redisClient instanceof \RedisArray && !$redisClient instanceof \Predis\Client && !$redisClient instanceof RedisProxy) {
55+
if (!$redisClient instanceof \Redis && !$redisClient instanceof \RedisArray && !$redisClient instanceof \RedisCluster && !$redisClient instanceof \Predis\Client && !$redisClient instanceof RedisProxy) {
5956
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)));
6057
}
6158
$this->redis = $redisClient;
@@ -182,18 +179,30 @@ public static function createConnection($dsn, array $options = array())
182179
*/
183180
protected function doFetch(array $ids)
184181
{
185-
if ($ids) {
182+
if (!$ids) {
183+
return array();
184+
}
185+
186+
$i = -1;
187+
$result = array();
188+
189+
if ($this->redis instanceof \Predis\Client) {
186190
$values = $this->pipeline(function () use ($ids) {
187191
foreach ($ids as $id) {
188192
yield 'get' => array($id);
189193
}
190194
});
191-
foreach ($values as $id => $v) {
192-
if ($v) {
193-
yield $id => $this->marshaller->unmarshall($v);
194-
}
195+
} else {
196+
$values = array_combine($ids, $this->redis->mget($ids));
197+
}
198+
199+
foreach ($values as $id => $v) {
200+
if ($v) {
201+
$result[$id] = $this->marshaller->unmarshall($v);
195202
}
196203
}
204+
205+
return $result;
197206
}
198207

199208
/**
@@ -209,9 +218,6 @@ protected function doHave($id)
209218
*/
210219
protected function doClear($namespace)
211220
{
212-
// When using a native Redis cluster, clearing the cache is done by versioning in AbstractTrait::clear().
213-
// This means old keys are not really removed until they expire and may need garbage collection.
214-
215221
$cleared = true;
216222
$hosts = array($this->redis);
217223
$evalArgs = array(array($namespace), 0);
@@ -220,21 +226,23 @@ protected function doClear($namespace)
220226
$evalArgs = array(0, $namespace);
221227

222228
$connection = $this->redis->getConnection();
223-
if ($connection instanceof PredisCluster) {
229+
if ($connection instanceof ClusterInterface && $connection instanceof \Traversable) {
224230
$hosts = array();
225231
foreach ($connection as $c) {
226232
$hosts[] = new \Predis\Client($c);
227233
}
228-
} elseif ($connection instanceof RedisCluster) {
229-
return false;
230234
}
231235
} elseif ($this->redis instanceof \RedisArray) {
232236
$hosts = array();
233237
foreach ($this->redis->_hosts() as $host) {
234238
$hosts[] = $this->redis->_instance($host);
235239
}
236240
} elseif ($this->redis instanceof \RedisCluster) {
237-
return false;
241+
$hosts = array();
242+
foreach ($this->redis->_masters() as $host) {
243+
$hosts[] = $h = new \Redis();
244+
$h->connect($host[0], $host[1]);
245+
}
238246
}
239247
foreach ($hosts as $host) {
240248
if (!isset($namespace[0])) {
@@ -261,7 +269,7 @@ protected function doClear($namespace)
261269
$keys = $keys[1];
262270
}
263271
if ($keys) {
264-
$host->del($keys);
272+
$this->doDelete($keys);
265273
}
266274
} while ($cursor = (int) $cursor);
267275
}
@@ -274,7 +282,17 @@ protected function doClear($namespace)
274282
*/
275283
protected function doDelete(array $ids)
276284
{
277-
if ($ids) {
285+
if (!$ids) {
286+
return true;
287+
}
288+
289+
if ($this->redis instanceof \Predis\Client) {
290+
$this->pipeline(function () use ($ids) {
291+
foreach ($ids as $id) {
292+
yield 'del' => array($id);
293+
}
294+
})->rewind();
295+
} else {
278296
$this->redis->del($ids);
279297
}
280298

@@ -312,7 +330,16 @@ private function pipeline(\Closure $generator)
312330
{
313331
$ids = array();
314332

315-
if ($this->redis instanceof \Predis\Client && !$this->redis->getConnection() instanceof ClusterInterface) {
333+
if ($this->redis instanceof \RedisCluster || ($this->redis instanceof \Predis\Client && $this->redis->getConnection() instanceof RedisCluster)) {
334+
// phpredis & predis don't support pipelining with RedisCluster
335+
// see https://github.com/phpredis/phpredis/blob/develop/cluster.markdown#pipelining
336+
// see https://github.com/nrk/predis/issues/267#issuecomment-123781423
337+
$results = array();
338+
foreach ($generator() as $command => $args) {
339+
$results[] = \call_user_func_array(array($this->redis, $command), $args);
340+
$ids[] = $args[0];
341+
}
342+
} elseif ($this->redis instanceof \Predis\Client) {
316343
$results = $this->redis->pipeline(function ($redis) use ($generator, &$ids) {
317344
foreach ($generator() as $command => $args) {
318345
\call_user_func_array(array($redis, $command), $args);
@@ -336,15 +363,6 @@ private function pipeline(\Closure $generator)
336363
foreach ($results as $k => list($h, $c)) {
337364
$results[$k] = $connections[$h][$c];
338365
}
339-
} elseif ($this->redis instanceof \RedisCluster || ($this->redis instanceof \Predis\Client && $this->redis->getConnection() instanceof ClusterInterface)) {
340-
// phpredis & predis don't support pipelining with RedisCluster
341-
// see https://github.com/phpredis/phpredis/blob/develop/cluster.markdown#pipelining
342-
// see https://github.com/nrk/predis/issues/267#issuecomment-123781423
343-
$results = array();
344-
foreach ($generator() as $command => $args) {
345-
$results[] = \call_user_func_array(array($this->redis, $command), $args);
346-
$ids[] = $args[0];
347-
}
348366
} else {
349367
$this->redis->multi(\Redis::PIPELINE);
350368
foreach ($generator() as $command => $args) {

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.