Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a41fc40

Browse files
[Cache] Fix Redis pipelining/multi-ops
1 parent 94d059d commit a41fc40

File tree

2 files changed

+78
-48
lines changed

2 files changed

+78
-48
lines changed

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

+51-48
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Predis\Connection\Factory;
1515
use Predis\Connection\Aggregate\PredisCluster;
1616
use Predis\Connection\Aggregate\RedisCluster;
17+
use Predis\Response\Status;
1718
use Symfony\Component\Cache\Exception\InvalidArgumentException;
1819

1920
/**
@@ -136,11 +137,14 @@ public static function createConnection($dsn, array $options = array())
136137
protected function doFetch(array $ids)
137138
{
138139
if ($ids) {
139-
$values = $this->redis->mGet($ids);
140-
$index = 0;
141-
foreach ($ids as $id) {
142-
if ($value = $values[$index++]) {
143-
yield $id => parent::unserialize($value);
140+
$values = $this->pipeline(function () use ($ids) {
141+
foreach ($ids as $id) {
142+
yield 'get' => array($id);
143+
}
144+
});
145+
foreach ($values as $id => $v) {
146+
if ($v) {
147+
yield $id => parent::unserialize($v);
144148
}
145149
}
146150
}
@@ -251,61 +255,60 @@ protected function doSave(array $values, $lifetime)
251255
return $failed;
252256
}
253257

254-
if (0 >= $lifetime) {
255-
$this->redis->mSet($serialized);
256-
257-
return $failed;
258-
}
259-
260-
$this->pipeline(function ($pipe) use (&$serialized, $lifetime) {
258+
$results = $this->pipeline(function () use ($serialized, $lifetime) {
261259
foreach ($serialized as $id => $value) {
262-
$pipe('setEx', $id, array($lifetime, $value));
260+
if (0 >= $lifetime) {
261+
yield 'set' => array($id, $value);
262+
} else {
263+
yield 'setEx' => array($id, $lifetime, $value);
264+
}
263265
}
264266
});
267+
foreach ($results as $id => $result) {
268+
if (true !== $result && (!$result instanceof Status || $result !== Status::get('OK'))) {
269+
$failed[] = $id;
270+
}
271+
}
265272

266273
return $failed;
267274
}
268275

269-
private function execute($command, $id, array $args, $redis = null)
276+
private function pipeline(\Closure $generator)
270277
{
271-
array_unshift($args, $id);
272-
call_user_func_array(array($redis ?: $this->redis, $command), $args);
273-
}
278+
$ids = array();
274279

275-
private function pipeline(\Closure $callback)
276-
{
277-
$redis = $this->redis;
278-
279-
try {
280-
if ($redis instanceof \Predis\Client) {
281-
$redis->pipeline(function ($pipe) use ($callback) {
282-
$this->redis = $pipe;
283-
$callback(array($this, 'execute'));
284-
});
285-
} elseif ($redis instanceof \RedisArray) {
286-
$connections = array();
287-
$callback(function ($command, $id, $args) use (&$connections) {
288-
if (!isset($connections[$h = $this->redis->_target($id)])) {
289-
$connections[$h] = $this->redis->_instance($h);
290-
$connections[$h]->multi(\Redis::PIPELINE);
291-
}
292-
$this->execute($command, $id, $args, $connections[$h]);
293-
});
294-
foreach ($connections as $c) {
295-
$c->exec();
280+
if ($this->redis instanceof \Predis\Client) {
281+
$results = $this->redis->pipeline(function ($redis) use ($generator, &$ids) {
282+
foreach ($generator() as $command => $args) {
283+
call_user_func_array(array($redis, $command), $args);
284+
$ids[] = $args[0];
296285
}
297-
} else {
298-
$pipe = $redis->multi(\Redis::PIPELINE);
299-
try {
300-
$callback(array($this, 'execute'));
301-
} finally {
302-
if ($pipe) {
303-
$redis->exec();
304-
}
286+
});
287+
} elseif ($this->redis instanceof \RedisArray) {
288+
$connections = $results = $ids = array();
289+
foreach ($generator() as $command => $args) {
290+
if (!isset($connections[$h = $this->redis->_target($args[0])])) {
291+
$connections[$h] = array($this->redis->_instance($h), array());
292+
$connections[$h][0]->multi(\Redis::PIPELINE);
305293
}
294+
call_user_func_array(array($connections[$h][0], $command), $args);
295+
$connections[$h][1][] = $args[0];
296+
}
297+
foreach ($connections as $c) {
298+
$results = array_merge($results, $c[0]->exec());
299+
$ids = array_merge($ids, $c[1]);
306300
}
307-
} finally {
308-
$this->redis = $redis;
301+
} else {
302+
$this->redis->multi(\Redis::PIPELINE);
303+
foreach ($generator() as $command => $args) {
304+
call_user_func_array(array($this->redis, $command), $args);
305+
$ids[] = $args[0];
306+
}
307+
$results = $this->redis->exec();
308+
}
309+
310+
foreach ($ids as $k => $id) {
311+
yield $id => $results[$k];
309312
}
310313
}
311314
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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 PredisClusterAdapterTest extends AbstractRedisAdapterTest
15+
{
16+
public static function setupBeforeClass()
17+
{
18+
parent::setupBeforeClass();
19+
self::$redis = new \Predis\Client(array(getenv('REDIS_HOST')));
20+
}
21+
22+
public static function tearDownAfterClass()
23+
{
24+
self::$redis->getConnection()->getConnectionByKey('foo')->executeCommand(self::$redis->createCommand('FLUSHDB'));
25+
self::$redis = null;
26+
}
27+
}

0 commit comments

Comments
 (0)