8000 [Cache] improve perf when using RedisCluster by reducing roundtrips t… · symfony/symfony@5155f48 · GitHub
[go: up one dir, main page]

Skip to content

Commit 5155f48

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

File tree

2 files changed

+74
-28
lines changed

2 files changed

+74
-28
lines changed
Lines 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

Lines 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