Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5.4] Able to use multiple Redis cluster and non-cluster instances together #16696

Merged
merged 5 commits into from
Dec 12, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Illuminate/Redis/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ abstract class Database implements DatabaseContract
* Get a specific Redis connection instance.
*
* @param string $name
* @return \Predis\ClientInterface|null
* @return \Predis\ClientInterface|\RedisCluster|\Redis|null
*/
public function connection($name = 'default')
{
Expand Down
117 changes: 83 additions & 34 deletions src/Illuminate/Redis/PhpRedisDatabase.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class PhpRedisDatabase extends Database
public function __construct(array $servers = [])
{
$cluster = Arr::pull($servers, 'cluster');
$clusters = (array) Arr::pull($servers, 'clusters');

$options = (array) Arr::pull($servers, 'options');

Expand All @@ -33,26 +34,43 @@ public function __construct(array $servers = [])
} else {
$this->clients = $this->createSingleClients($servers, $options);
}

$this->createClusters($clusters, $options);
}

/**
* Create multiple clusters (aggregate clients).
*
* @param array $clusters
* @param array $options
* @return void
*/
protected function createClusters(array $clusters, array $options = [])
{
// Merge general options with general cluster options
$options = array_merge($options, (array) Arr::pull($clusters, 'options'));

foreach ($clusters as $connection => $servers) {
// Merge specific cluster options with general options
$options = array_merge($options, (array) Arr::pull($servers, 'options'));

$this->clients += $this->createAggregateClient($servers, $options, $connection);
}
}

/**
* Create a new aggregate client supporting sharding.
*
* @param array $servers
* @param array $options
* @param string $connection
* @return array
*/
protected function createAggregateClient(array $servers, array $options = [])
protected function createAggregateClient(array $servers, array $options = [], $connection = 'default')
{
$servers = array_map([$this, 'buildClusterSeed'], $servers);

return ['default' => new RedisCluster(
null,
array_values($servers),
Arr::get($options, 'timeout', 0),
Arr::get($options, 'read_timeout', 0),
isset($options['persistent']) && $options['persistent']
)];
return [$connection => $this->createRedisClusterInstance($servers, $options)];
}

/**
Expand All @@ -67,32 +85,7 @@ protected function createSingleClients(array $servers, array $options = [])
$clients = [];

foreach ($servers as $key => $server) {
$client = new Redis;

$timeout = empty($server['timeout']) ? 0 : $server['timeout'];

if (isset($server['persistent']) && $server['persistent']) {
$client->pconnect($server['host'], $server['port'], $timeout);
} else {
$client->connect($server['host'], $server['port'], $timeout);
}

if (! empty($server['prefix'])) {
$client->setOption(Redis::OPT_PREFIX, $server['prefix']);
}

if (! empty($server['read_timeout'])) {
$client->setOption(Redis::OPT_READ_TIMEOUT, $server['read_timeout']);
}

if (! empty($server['password'])) {
$client->auth($server['password']);
}

if (! empty($server['database'])) {
$client->select($server['database']);
}

$client = $this->createRedisInstance($server, $options);
$clients[$key] = $client;
}

Expand Down Expand Up @@ -140,4 +133,60 @@ public function psubscribe($channels, Closure $callback, $connection = null)
{
$this->subscribe($channels, $callback, $connection, __FUNCTION__);
}

/**
* Create a new redis cluster instance.
*
* @param array $servers
* @param array $options
* @return RedisCluster
*/
protected function createRedisClusterInstance(array $servers, array $options)
{
return new RedisCluster(
null,
array_values($servers),
Arr::get($options, 'timeout', 0),
Arr::get($options, 'read_timeout', 0),
isset($options['persistent']) && $options['persistent']
);
}

/**
* Create a new redis instance.
*
* @param array $server
* @param array $options
* @return Redis
*/
protected function createRedisInstance(array $server, array $options)
{
$client = new Redis();

$timeout = empty($server['timeout']) ? 0 : $server['timeout'];

if (isset($server['persistent']) && $server['persistent']) {
$client->pconnect($server['host'], $server['port'], $timeout);
} else {
$client->connect($server['host'], $server['port'], $timeout);
}

if (! empty($server['prefix'])) {
$client->setOption(Redis::OPT_PREFIX, $server['prefix']);
}

if (! empty($server['read_timeout'])) {
$client->setOption(Redis::OPT_READ_TIMEOUT, $server['read_timeout']);
}

if (! empty($server['password'])) {
$client->auth($server['password']);
}

if (! empty($server['database'])) {
$client->select($server['database']);
}

return $client;
}
}
30 changes: 27 additions & 3 deletions src/Illuminate/Redis/PredisDatabase.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ class PredisDatabase extends Database
*/
public function __construct(array $servers = [])
{
$cluster = Arr::pull($servers, 'cluster');
$cluster = (bool) Arr::pull($servers, 'cluster');
$clusters = (array) Arr::pull($servers, 'clusters');

$options = array_merge(['timeout' => 10.0], (array) Arr::pull($servers, 'options'));

Expand All @@ -32,18 +33,41 @@ public function __construct(array $servers = [])
} else {
$this->clients = $this->createSingleClients($servers, $options);
}

$this->createClusters($clusters, $options);
}

/**
* Create multiple clusters (aggregate clients).
*
* @param array $clusters
* @param array $options
* @return void
*/
protected function createClusters(array $clusters, array $options = [])
{
// Merge general options with general cluster options
$options = array_merge($options, (array) Arr::pull($clusters, 'options'));

foreach ($clusters as $connection => $servers) {
// Merge specific cluster options with general options
$options = array_merge($options, (array) Arr::pull($servers, 'options'));

$this->clients += $this->createAggregateClient($servers, $options, $connection);
}
}

/**
* Create a new aggregate client supporting sharding.
*
* @param array $servers
* @param array $options
* @param string $connection
* @return array
*/
protected function createAggregateClient(array $servers, array $options = [])
protected function createAggregateClient(array $servers, array $options = [], $connection = 'default')
{
return ['default' => new Client(array_values($servers), $options)];
return [$connection => new Client(array_values($servers), $options)];
}

/**
Expand Down
100 changes: 100 additions & 0 deletions tests/Redis/PhpRedisConnectionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?php

use Illuminate\Redis\PhpRedisDatabase;

class PhpRedisConnectionTest extends PHPUnit_Framework_TestCase
{
public function testPhpRedisNotCreateClusterAndOptionsAndClustersServer()
{
$redis = $this->getRedis(false);

$client = $redis->connection('cluster');
$this->assertNull($client, 'cluster parameter should not create as redis server');

$client = $redis->connection('options');
$this->assertNull($client, 'options parameter should not create as redis server');

$client = $redis->connection('clusters');
$this->assertNull($client, 'clusters parameter should not create as redis server');
}

public function testPhpRedisClusterNotCreateClusterAndOptionsServer()
{
$redis = $this->getRedis(true);

$client = $redis->connection();

$this->assertInstanceOf(RedisClusterStub::class, $client);
}

public function testPhpRedisClusterCreateMultipleClustersAndNotCreateOptionsServer()
{
$redis = $this->getRedis();

$clusterOne = $redis->connection('cluster-1');
$clusterTwo = $redis->connection('cluster-2');

$this->assertInstanceOf(RedisClusterStub::class, $clusterOne);
$this->assertInstanceOf(RedisClusterStub::class, $clusterTwo);

$client = $redis->connection('options');
$this->assertNull($client, 'options parameter should not create as redis server');
}

protected function getRedis($cluster = false)
{
$servers = [
'cluster' => $cluster,
'default' => [
'host' => '127.0.0.1',
'port' => 6379,
'database' => 0,
],
'options' => [
'prefix' => 'prefix:',
],
'clusters' => [
'options' => [
'prefix' => 'cluster:',
],
'cluster-1' => [
[
'host' => '127.0.0.1',
'port' => 6379,
'database' => 0,
],
],
'cluster-2' => [
[
'host' => '127.0.0.1',
'port' => 6379,
'database' => 0,
],
],
],
];

return new PhpRedisDatabaseStub($servers);
}
}

class PhpRedisDatabaseStub extends PhpRedisDatabase
{
protected function createRedisClusterInstance(array $servers, array $options)
{
return new RedisClusterStub();
}

protected function createRedisInstance(array $server, array $options)
{
return new RedisStub;
}
}

class RedisStub
{
}

class RedisClusterStub
{
}
37 changes: 36 additions & 1 deletion tests/Redis/RedisConnectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

class RedisConnectionTest extends PHPUnit_Framework_TestCase
{
public function testRedisNotCreateClusterAndOptionsServer()
public function testRedisNotCreateClusterAndOptionsAndClustersServer()
{
$redis = $this->getRedis(false);

Expand All @@ -11,6 +11,9 @@ public function testRedisNotCreateClusterAndOptionsServer()

$client = $redis->connection('options');
$this->assertNull($client, 'options parameter should not create as redis server');

$client = $redis->connection('clusters');
$this->assertNull($client, 'clusters parameter should not create as redis server');
}

public function testRedisClusterNotCreateClusterAndOptionsServer()
Expand All @@ -21,6 +24,19 @@ public function testRedisClusterNotCreateClusterAndOptionsServer()
$this->assertCount(1, $client->getConnection());
}

public function testRedisClusterCreateMultipleClustersAndNotCreateOptionsServer()
{
$redis = $this->getRedis();
$clusterOne = $redis->connection('cluster-1');
$clusterTwo = $redis->connection('cluster-2');

$this->assertCount(1, $clusterOne->getConnection());
$this->assertCount(1, $clusterTwo->getConnection());

$client = $redis->connection('options');
$this->assertNull($client, 'options parameter should not create as redis server');
}

protected function getRedis($cluster = false)
{
$servers = [
Expand All @@ -33,6 +49,25 @@ protected function getRedis($cluster = false)
'options' => [
'prefix' => 'prefix:',
],
'clusters' => [
'options' => [
'prefix' => 'cluster:',
],
'cluster-1' => [
[
'host' => '127.0.0.1',
'port' => 6379,
'database' => 0,
],
],
'cluster-2' => [
[
'host' => '127.0.0.1',
'port' => 6379,
'database' => 0,
],
],
],
];

return new Illuminate\Redis\PredisDatabase($servers);
Expand Down