8000 [Cache] Tests for Redis Replication with cache · symfonyaml/symfony@159e6b9 · GitHub
[go: up one dir, main page]

Skip to content

Commit 159e6b9

Browse files
DemigodCodenicolas-grekas
authored andcommitted
[Cache] Tests for Redis Replication with cache
1 parent e1cf65c commit 159e6b9

File tree

6 files changed

+178
-3
lines changed

6 files changed

+178
-3
lines changed

.github/workflows/integration-tests.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,17 @@ jobs:
8181
REDIS_MASTER_HOST: redis
8282
REDIS_MASTER_SET: redis_sentinel
8383
REDIS_SENTINEL_QUORUM: 1
84+
redis-primary:
85+
image: redis:latest
86+
hostname: redis-primary
87+
ports:
88+
- 16381:6379
89+
90+
redis-replica:
91+
image: redis:latest
92+
ports:
93+
- 16382:6379
94+
command: redis-server --slaveof redis-primary 6379
8495
memcached:
8596
image: memcached:1.6.5
8697
ports:
@@ -239,6 +250,7 @@ jobs:
239250
REDIS_CLUSTER_HOSTS: 'localhost:7000 localhost:7001 localhost:7002 localhost:7003 localhost:7004 localhost:7005'
240251
REDIS_SENTINEL_HOSTS: 'unreachable-host:26379 localhost:26379 localhost:26379'
241252
REDIS_SENTINEL_SERVICE: redis_sentinel
253+
REDIS_REPLICATION_HOSTS: 'localhost:16381 localhost:16382'
242254
MESSENGER_REDIS_DSN: redis://127.0.0.1:7006/messages
243255
MESSENGER_AMQP_DSN: amqp://localhost/%2f/messages
244256
MESSENGER_SQS_DSN: "sqs://localhost:4566/messages?sslmode=disable&poll_timeout=0.01"
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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+
use Symfony\Component\Cache\Adapter\RedisAdapter;
15+
16+
/**
17+
* @group integration
18+
*/
19+
class PredisRedisReplicationAdapterTest extends AbstractRedisAdapterTestCase
20+
{
21+
public static function setUpBeforeClass(): void
22+
{
23+
if (!$hosts = getenv('REDIS_REPLICATION_HOSTS')) {
24+
self::markTestSkipped('REDIS_REPLICATION_HOSTS env var is not defined.');
25+
}
26+
27+
self::$redis = RedisAdapter::createConnection('redis:?host['.str_replace(' ', ']&host[', $hosts).'][alias]=master', ['class' => \Predis\Client::class, 'prefix' => 'prefix_']);
28+
}
29+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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+
/**
15+
* @group integration
16+
*/
17+
class PredisReplicationAdapterTest extends AbstractRedisAdapterTestCase
18+
{
19+
public static function setUpBeforeClass(): void
20+
{
21+
parent::setUpBeforeClass();
22+
self::$redis = new \Predis\Client(array_combine(['host', 'port'], explode(':', getenv('REDIS_HOST')) + [1 => 6379]), ['prefix' => 'prefix_']);
23+
}
24+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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+
use Psr\Cache\CacheItemPoolInterface;
15+
use Symfony\Component\Cache\Adapter\RedisTagAwareAdapter;
16+
17+
/**
18+
* @group integration
19+
*/
20+
class PredisTagAwareReplicationAdapterTest extends PredisReplicationAdapterTest
21+
{
22+
use TagAwareTestTrait;
23+
24+
protected function setUp(): void
25+
{
26+
parent::setUp();
27+
$this->skippedTests['testTagItemExpiry'] = 'Testing expiration slows down the test suite';
28+
}
29+
30+
public function createCachePool(int $defaultLifetime = 0, ?string $testMethod = null): CacheItemPoolInterface
31+
{
32+
$this->assertInstanceOf(\Predis\Client::class, self::$redis);
33+
$adapter = new RedisTagAwareAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime);
34+
35+
return $adapter;
36+
}
37+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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+
use Psr\Cache\CacheItemPoolInterface;
15+
use Symfony\Component\Cache\Adapter\AbstractAdapter;
16+
use Symfony\Component\Cache\Adapter\RedisAdapter;
17+
use Symfony\Component\Cache\Exception\InvalidArgumentException;
18+
use Symfony\Component\Cache\Traits\RedisClusterProxy;
19+
20+
/**
21+
* @group integration
22+
*/
23+
class RedisReplicationAdapterTest extends AbstractRedisAdapterTestCase
24+
{
25+
public static function setUpBeforeClass(): void
26+
{
27+
if (!$hosts = getenv('REDIS_REPLICATION_HOSTS')) {
28+
self::markTestSkipped('REDIS_REPLICATION_HOSTS env var is not defined.');
29+
}
30+
31+
self::$redis = AbstractAdapter::createConnection('redis:?host['.str_replace(' ', ']&host[', $hosts).'][alias]=master', ['lazy' => true]);
32+
self::$redis->setOption(\Redis::OPT_PREFIX, 'prefix_');
33+
}
34+
35+
public function createCachePool(int $defaultLifetime = 0, ?string $testMethod = null): CacheItemPoolInterface
36+
{
37+
if ('testClearWithPrefix' === $testMethod && \defined('Redis::SCAN_PREFIX')) {
38+
self::$redis->setOption(\Redis::OPT_SCAN, \Redis::SCAN_PREFIX);
39+
}
40+
41+
$this->assertInstanceOf(RedisClusterProxy::class, self::$redis);
42+
$adapter = new RedisAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime);
43+
44+
return $adapter;
45+
}
46+
47+
/**
48+
* @dataProvider provideFailedCreateConnection
49+
*/
50+
public function testFailedCreateConnection(string $dsn)
51+
{
52+
$this->expectException(InvalidArgumentException::class);
53+
$this->expectExceptionMessage('Redis connection ');
54+
RedisAdapter::createConnection($dsn);
55+
}
56+
57+
public static function provideFailedCreateConnection(): array
58+
{
59+
return [
60+
['redis://localhost:1234'],
61+
['redis://foo@localhost?role=master'],
62+
['redis://localhost/123?role=master'],
63+
];
64+
}
65+
}

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Predis\Connection\Aggregate\ReplicationInterface;
1818
use Predis\Connection\Cluster\ClusterInterface as Predis2ClusterInterface;
1919
use Predis\Connection\Cluster\RedisCluster as Predis2RedisCluster;
20+
use Predis\Connection\Replication\ReplicationInterface as Predis2ReplicationInterface;
2021
use Predis\Response\ErrorInterface;
2122
use Predis\Response\Status;
2223
use Relay\Relay;
@@ -473,9 +474,16 @@ protected function doClear(string $namespace): bool
473474
$cleared = true;
474475
$hosts = $this->getHosts();
475476
$host = reset($hosts);
476-
if ($host instanceof \Predis\Client && $host->getConnection() instanceof ReplicationInterface) {
477-
// Predis supports info command only on the master in replication environments
478-
$hosts = [$host->getClientFor('master')];
477+
if ($host instanceof \Predis\Client) {
478+
$connection = $host->getConnection();
479+
480+
if ($connection instanceof ReplicationInterface) {
481+
$hosts = [$host->getClientFor('master')];
482+
} elseif ($connection instanceof Predis2ReplicationInterface) {
483+
$connection->switchToMaster();
484+
485+
$hosts = [$host];
486+
}
479487
}
480488

481489
foreach ($hosts as $host) {

0 commit comments

Comments
 (0)
0