Skip to content

Commit

Permalink
Specify node when sending scan to RedisCluster (#53837)
Browse files Browse the repository at this point in the history
* Specify node when sending scan to RedisCluster. Default to using the first master node (#53826)

* Update src/Illuminate/Redis/Connections/PhpRedisClusterConnection.php

Co-authored-by: Mior Muhammad Zaki <crynobone@gmail.com>

* formatting

---------

Co-authored-by: Taylor Otwell <taylor@laravel.com>
Co-authored-by: Mior Muhammad Zaki <crynobone@gmail.com>
  • Loading branch information
3 people authored Dec 11, 2024
1 parent c589439 commit c18b470
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 1 deletion.
59 changes: 58 additions & 1 deletion src/Illuminate/Redis/Connections/PhpRedisClusterConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,53 @@

namespace Illuminate\Redis\Connections;

use InvalidArgumentException;

class PhpRedisClusterConnection extends PhpRedisConnection
{
/**
* Flush the selected Redis database on all master nodes.
* The RedisCluster client.
*
* @var \RedisCluster
*/
protected $client;

/**
* The default node to use from the cluster.
*
* @var string|array
*/
protected $defaultNode;

/**
* Scan all keys based on the given options.
*
* @param mixed $cursor
* @param array $options
* @return mixed
*
* @throws \InvalidArgumentException
*/
#[\Override]
public function scan($cursor, $options = [])
{
$result = $this->client->scan($cursor,
$options['node'] ?? $this->defaultNode(),
$options['match'] ?? '*',
$options['count'] ?? 10
);

if ($result === false) {
$result = [];
}

return $cursor === 0 && empty($result) ? false : [$cursor, $result];
}

/**
* Flush the selected Redis database on all master nodes.
*
* @return void
*/
public function flushdb()
{
Expand All @@ -21,4 +62,20 @@ public function flushdb()
: $this->command('flushdb', [$master]);
}
}

/**
* Return default node to use for cluster.
*
* @return string|array
*
* @throws \InvalidArgumentException
*/
private function defaultNode()
{
if (! isset($this->defaultNode)) {
$this->defaultNode = $this->client->_masters()[0] ?? throw new InvalidArgumentException('Unable to determine default node. No master nodes found in the cluster.');
}

return $this->defaultNode;
}
}
65 changes: 65 additions & 0 deletions tests/Redis/Connections/PhpRedisClusterConnectionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

namespace Illuminate\Tests\Redis\Connections;

use Illuminate\Redis\Connections\PhpRedisClusterConnection;
use Mockery as m;
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
use PHPUnit\Framework\TestCase;

#[RequiresPhpExtension('redis')]
class PhpRedisClusterConnectionTest extends TestCase
{
protected function tearDown(): void
{
m::close();
}

public function testItScansUsingDefaultNode()
{
$client = m::mock(\RedisCluster::class);
$client->shouldReceive('_masters')->once()->andReturn([['127.0.0.1', '6379']]);
$client->shouldReceive('scan')
->once()
->with(0, ['127.0.0.1', '6379'], '*', 10)
->andReturn(['key']);

$connection = new PhpRedisClusterConnection($client);
$this->assertEquals([0, ['key']], $connection->scan(0));
}

public function testItOnlyFetchesDefaultNodeOnce()
{
$client = m::mock(\RedisCluster::class);
$client->shouldReceive('_masters')->once()->andReturn([['127.0.0.1', '6379']]);
$client->shouldReceive('scan')->twice();

$connection = new PhpRedisClusterConnection($client);
$connection->scan(0);
$connection->scan(0);
}

public function testItScansUsingOptionNode()
{
$client = m::mock(\RedisCluster::class);
$client->shouldReceive('scan')
->once()
->with(0, 'option-node', '*', 10)
->andReturn(['key']);

$connection = new PhpRedisClusterConnection($client);
$this->assertEquals([0, ['key']], $connection->scan(0, ['node' => 'option-node']));
}

public function testItThrowsExceptionWithoutNodes()
{
$client = m::mock(\RedisCluster::class);
$client->shouldReceive('_masters')->once()->andReturn([]);
$client->shouldReceive('scan');

$this->expectExceptionMessage('Unable to determine default node. No master nodes found in the cluster.');

$connection = new PhpRedisClusterConnection($client);
$connection->scan(0);
}
}

0 comments on commit c18b470

Please sign in to comment.