Skip to content

Commit

Permalink
[1.x] Improve redis adapter (#127)
Browse files Browse the repository at this point in the history
* Remove unused redis adapter methods

* Fix code styling

* Further improvements

* Fix code styling

---------

Co-authored-by: timacdonald <[email protected]>
  • Loading branch information
timacdonald and timacdonald authored Dec 4, 2023
1 parent 5d3abe6 commit fb370f2
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 233 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"pestphp/pest": "^2.0",
"pestphp/pest-plugin-laravel": "^2.2",
"phpstan/phpstan": "^1.11",
"predis/predis": "^2.2"
"predis/predis": "^1.0 || ^2.0"
},
"conflict": {
"nunomaduro/collision": "<7.7.0"
Expand Down
159 changes: 62 additions & 97 deletions src/Support/RedisAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

namespace Laravel\Pulse\Support;

use Carbon\CarbonInterval;
use Illuminate\Config\Repository;
use Illuminate\Redis\Connections\Connection;
use Illuminate\Support\Collection;
use Predis\Client as Predis;
use Predis\Command\RawCommand;
use Predis\Pipeline\Pipeline;
use Predis\Response\ServerException as PredisServerException;
use Redis as PhpRedis;
use RuntimeException;

/**
* @internal
Expand All @@ -27,96 +27,19 @@ public function __construct(
//
}

/**
* Get a range from a sorted set.
*
* @return list<string>
*/
public function zrange(string $key, int $start, int $stop, bool $reversed = false, bool $withScores = false): array|PhpRedis|Pipeline
{
return match (true) {
$this->client() instanceof PhpRedis => $this->client()->rawCommand('ZRANGE', $this->config->get('database.redis.options.prefix').$key, $start, $stop, ...array_filter([
$reversed ? 'REV' : null,
$withScores ? 'WITHSCORES' : null,
])),
$this->client() instanceof Predis ||
$this->client() instanceof Pipeline => $this->client()->zrange($key, $start, $stop, ...array_filter([
$reversed ? 'REV' : null,
$withScores ? 'WITHSCORES' : null,
])),
};
}

/**
* Remove sorted set range by score.
*/
public function zremrangebyscore(string $key, int|string $start, int|string $stop): int|PhpRedis|Pipeline
{
return match (true) {
$this->client() instanceof PhpRedis => $this->client()->rawCommand('ZREMRANGEBYSCORE', $this->config->get('database.redis.options.prefix').$key, $start, $stop),
$this->client() instanceof Predis ||
$this->client() instanceof Pipeline => $this->client()->zremrangebyscore($key, $start, $stop),
};
}

/**
* Get the key.
*/
public function get(string $key): null|string|Pipeline|PhpRedis
{
return $this->client()->get($key); // @phpstan-ignore return.type
}

/**
* Set the value.
*/
public function set(string $key, string $value, CarbonInterval $ttl): null|string|Pipeline|PhpRedis
{
return match (true) {
$this->client() instanceof PhpRedis => $this->client()->rawCommand('SET', $this->config->get('database.redis.options.prefix').$key, $value, 'PX', (int) $ttl->totalMilliseconds),
$this->client() instanceof Predis ||
$this->client() instanceof Pipeline => $this->client()->set($key, $value, 'PX', (int) $ttl->totalMilliseconds),
};
}

/**
* Expire the key at the given time.
*/
public function expire(string $key, CarbonInterval $interval): int|Pipeline|PhpRedis
{
return $this->client()->expire($key, (int) $interval->totalSeconds); // @phpstan-ignore return.type
}

/**
* Delete the key.
*
* @param list<string> $keys
*/
public function del(array $keys): int|Pipeline|PhpRedis
{
return $this->client()->del(...$keys); // @phpstan-ignore return.type
}

/**
* Increment the given members value.
*/
public function zincrby(string $key, int $increment, string $member): string|float|Pipeline|PhpRedis
{
return $this->client()->zincrby($key, $increment, $member); // @phpstan-ignore return.type
}

/**
* Add an entry to the stream.
*
* @param array<string, string> $dictionary
*/
public function xadd(string $key, array $dictionary): string|Pipeline|PhpRedis
{
return match (true) {
$this->client() instanceof PhpRedis => $this->client()->xadd($key, '*', $dictionary),
$this->client() instanceof Predis ||
$this->client() instanceof Pipeline => $this->client()->xadd($key, $dictionary), // @phpstan-ignore method.notFound
};
return $this->handle([
'XADD',
$this->config->get('database.redis.options.prefix').$key,
'*',
...collect($dictionary)->keys()->zip($dictionary)->flatten()->all(),
]);
}

/**
Expand All @@ -126,19 +49,33 @@ public function xadd(string $key, array $dictionary): string|Pipeline|PhpRedis
*/
public function xrange(string $key, string $start, string $end, int $count = null): array
{
return $this->client()->xrange(...array_filter(func_get_args())); // @phpstan-ignore method.notFound
return collect($this->handle([

Check failure on line 52 in src/Support/RedisAdapter.php

View workflow job for this annotation

GitHub Actions / Static Analysis

Method Laravel\Pulse\Support\RedisAdapter::xrange() should return array<string, array<string, string>> but returns array<int|string, array>.

Check failure on line 52 in src/Support/RedisAdapter.php

View workflow job for this annotation

GitHub Actions / Static Analysis

Unable to resolve the template type TKey in call to function collect

Check failure on line 52 in src/Support/RedisAdapter.php

View workflow job for this annotation

GitHub Actions / Static Analysis

Unable to resolve the template type TValue in call to function collect
'XRANGE',
$this->config->get('database.redis.options.prefix').$key,
$start,
$end,
...$count !== null ? ['COUNT', "$count"] : [],
]))->mapWithKeys(fn ($value, $key) => [
$value[0] => collect($value[1])

Check failure on line 59 in src/Support/RedisAdapter.php

View workflow job for this annotation

GitHub Actions / Static Analysis

Unable to resolve the template type TKey in call to function collect

Check failure on line 59 in src/Support/RedisAdapter.php

View workflow job for this annotation

GitHub Actions / Static Analysis

Unable to resolve the template type TValue in call to function collect
->chunk(2)
->map->values()
->mapWithKeys(fn ($value, $key) => [$value[0] => $value[1]])
->all(),
])->all();
}

/**
* Trim the stream.
*/
public function xtrim(string $key, string $strategy, string $strategyModifier, string|int $threshold): int
{
return match (true) {
$this->client() instanceof PhpRedis => $this->client()->rawCommand('XTRIM', $this->config->get('database.redis.options.prefix').$key, $strategy, $strategyModifier, (string) $threshold),
$this->client() instanceof Predis ||
$this->client() instanceof Pipeline => $this->client()->xtrim($key, [$strategy, $strategyModifier], (string) $threshold), // @phpstan-ignore method.notFound
};
return $this->handle([
'XTRIM',
$this->config->get('database.redis.options.prefix').$key,
$strategy,
$strategyModifier,
$threshold,
]);
}

/**
Expand All @@ -148,7 +85,11 @@ public function xtrim(string $key, string $strategy, string $strategyModifier, s
*/
public function xdel(string $stream, Collection|array $keys): int
{
return $this->client()->xdel($stream, Collection::unwrap($keys)); // @phpstan-ignore method.notFound
return $this->handle([
'XDEL',
$this->config->get('database.redis.options.prefix').$stream,
...$keys,
]);
}

/**
Expand All @@ -159,16 +100,40 @@ public function xdel(string $stream, Collection|array $keys): int
*/
public function pipeline(callable $closure): array
{
if ($this->client() instanceof Pipeline) {
throw new RuntimeException('Pipelines are not able to be nested.');
}

// Create a pipeline and wrap the Redis client in an instance of this class to ensure our wrapper methods are used within the pipeline...
return $this->connection->pipeline(fn (Pipeline|PhpRedis $client) => $closure(new self($this->connection, $this->config, $client))); // @phpstan-ignore method.notFound
}

/**
* Get the Redis client instance.
* Run the given command.
*/
protected function handle($args): mixed

Check failure on line 110 in src/Support/RedisAdapter.php

View workflow job for this annotation

GitHub Actions / Static Analysis

Method Laravel\Pulse\Support\RedisAdapter::handle() has parameter $args with no type specified.
{
try {
return tap($this->run($args), function ($result) {
if ($result === false && $this->client() instanceof PhpRedis) {
throw new RedisClientException($this->client()->getLastError());

Check failure on line 115 in src/Support/RedisAdapter.php

View workflow job for this annotation

GitHub Actions / Static Analysis

Parameter #1 $message of class Laravel\Pulse\Support\RedisClientException constructor expects string, string|null given.
}
});
} catch (PredisServerException $e) {
throw new RedisClientException($e->getMessage(), previous: $e);
}
}

/**
* Run the given command.
*/
protected function run($args): mixed

Check failure on line 126 in src/Support/RedisAdapter.php

View workflow job for this annotation

GitHub Actions / Static Analysis

Method Laravel\Pulse\Support\RedisAdapter::run() has parameter $args with no type specified.
{
return match (true) {
$this->client() instanceof PhpRedis => $this->client()->rawCommand(...$args),
$this->client() instanceof Predis,
$this->client() instanceof Pipeline => $this->client()->executeCommand(new RawCommand($args)),
};
}

/**
* Retrieve the Redis client.
*/
protected function client(): PhpRedis|Predis|Pipeline
{
Expand Down
10 changes: 10 additions & 0 deletions src/Support/RedisClientException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Laravel\Pulse\Support;

use RuntimeException;

class RedisClientException extends RuntimeException
{
//
}
Loading

0 comments on commit fb370f2

Please sign in to comment.