Skip to content

Commit

Permalink
Add AsyncRPCInterface in AsyncCache, improve unit tests (#41)
Browse files Browse the repository at this point in the history
  • Loading branch information
msmakouz authored Jul 25, 2024
1 parent c7ea69c commit bc6e142
Show file tree
Hide file tree
Showing 10 changed files with 718 additions and 1,383 deletions.
52 changes: 17 additions & 35 deletions src/AsyncCache.php
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
<?php

declare(strict_types=1);

namespace Spiral\RoadRunner\KeyValue;

use DateInterval;
use RoadRunner\KV\DTO\V1\Response;
use Spiral\Goridge\RPC\AsyncRPCInterface;
use Spiral\Goridge\RPC\Exception\RPCException;
use Spiral\Goridge\RPC\Exception\ServiceException;
use Spiral\Goridge\RPC\RPCInterface;
use Spiral\RoadRunner\KeyValue\Exception\KeyValueException;
use Spiral\RoadRunner\KeyValue\Exception\StorageException;
use Spiral\RoadRunner\KeyValue\Serializer\DefaultSerializer;
use Spiral\RoadRunner\KeyValue\Serializer\SerializerInterface;
use function sprintf;
use function str_contains;
use function str_replace;

/**
* @psalm-suppress PropertyNotSetInConstructor
* @property AsyncRPCInterface $rpc
*/
class AsyncCache extends Cache implements AsyncStorageInterface
{
Expand All @@ -27,26 +25,20 @@ class AsyncCache extends Cache implements AsyncStorageInterface
protected array $callsInFlight = [];

/**
* @param AsyncRPCInterface $rpc
* @param non-empty-string $name
*/
public function __construct(
RPCInterface $rpc,
AsyncRPCInterface $rpc,
string $name,
SerializerInterface $serializer = new DefaultSerializer()
) {
parent::__construct($rpc, $name, $serializer);

// This should result in things like the Symfony ContainerBuilder throwing during build instead of runtime.
assert($this->rpc instanceof AsyncRPCInterface);
}

/**
* Note: The current PSR-16 implementation always returns true or
* exception on error.
*
* {@inheritDoc}
*
* @throws KeyValueException
* @throws RPCException
*/
Expand All @@ -59,19 +51,15 @@ public function deleteAsync(string $key): bool
* Note: The current PSR-16 implementation always returns true or
* exception on error.
*
* {@inheritDoc}
*
* @psalm-param iterable<string> $keys
* @param iterable<string> $keys
*
* @throws KeyValueException
* @throws RPCException
*/
public function deleteMultipleAsync(iterable $keys): bool
{
assert($this->rpc instanceof AsyncRPCInterface);

// Handle someone never calling commitAsync()
if (count($this->callsInFlight) > 1000) {
if (\count($this->callsInFlight) > 1000) {
$this->commitAsync();
}

Expand All @@ -81,33 +69,29 @@ public function deleteMultipleAsync(iterable $keys): bool
}

/**
* {@inheritDoc}
*
* @psalm-param positive-int|\DateInterval|null $ttl
* @param positive-int|\DateInterval|null $ttl
* @psalm-suppress MoreSpecificImplementedParamType
*
* @throws KeyValueException
* @throws RPCException
*/
public function setAsync(string $key, mixed $value, null|int|DateInterval $ttl = null): bool
public function setAsync(string $key, mixed $value, null|int|\DateInterval $ttl = null): bool
{
return $this->setMultipleAsync([$key => $value], $ttl);
}

/**
* {@inheritDoc}
*
* @psalm-param iterable<string, mixed> $values
* @psalm-param positive-int|\DateInterval|null $ttl
* @param iterable<string, mixed> $values
* @param positive-int|\DateInterval|null $ttl
* @psalm-suppress MoreSpecificImplementedParamType
*
* @throws KeyValueException
* @throws RPCException
*/
public function setMultipleAsync(iterable $values, null|int|DateInterval $ttl = null): bool
public function setMultipleAsync(iterable $values, null|int|\DateInterval $ttl = null): bool
{
assert($this->rpc instanceof AsyncRPCInterface);

// Handle someone never calling commitAsync()
if (count($this->callsInFlight) > 1000) {
if (\count($this->callsInFlight) > 1000) {
$this->commitAsync();
}

Expand All @@ -125,15 +109,13 @@ public function setMultipleAsync(iterable $values, null|int|DateInterval $ttl =
*/
public function commitAsync(): bool
{
assert($this->rpc instanceof AsyncRPCInterface);

try {
$this->rpc->getResponses($this->callsInFlight, Response::class);
} catch (ServiceException $e) {
$message = str_replace(["\t", "\n"], ' ', $e->getMessage());
$message = \str_replace(["\t", "\n"], ' ', $e->getMessage());

if (str_contains($message, 'no such storage')) {
throw new StorageException(sprintf(self::ERROR_INVALID_STORAGE, $this->name));
if (\str_contains($message, 'no such storage')) {
throw new StorageException(\sprintf(self::ERROR_INVALID_STORAGE, $this->name));
}

throw new KeyValueException($message, $e->getCode(), $e);
Expand Down
7 changes: 4 additions & 3 deletions src/AsyncStorageInterface.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<?php

declare(strict_types=1);

namespace Spiral\RoadRunner\KeyValue;

use DateInterval;
use Spiral\RoadRunner\KeyValue\Exception\KeyValueException;

interface AsyncStorageInterface extends StorageInterface
Expand All @@ -28,7 +29,7 @@ public function commitAsync(): bool;
* MUST be thrown if $values is neither an array nor a Traversable,
* or if any of the $values are not a legal value.
*/
public function setMultipleAsync(iterable $values, null|int|DateInterval $ttl = null): bool;
public function setMultipleAsync(iterable $values, null|int|\DateInterval $ttl = null): bool;

/**
* Persists data in the cache, uniquely referenced by a key with an optional expiration TTL time.
Expand All @@ -44,7 +45,7 @@ public function setMultipleAsync(iterable $values, null|int|DateInterval $ttl =
* @throws \Psr\SimpleCache\InvalidArgumentException
* MUST be thrown if the $key string is not a legal value.
*/
public function setAsync(string $key, mixed $value, null|int|DateInterval $ttl = null): bool;
public function setAsync(string $key, mixed $value, null|int|\DateInterval $ttl = null): bool;

/**
* Delete an item from the cache by its unique key.
Expand Down
4 changes: 2 additions & 2 deletions src/Cache.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ class Cache implements StorageInterface
* @param non-empty-string $name
*/
public function __construct(
RPCInterface $rpc,
RPCInterface $rpc,
protected readonly string $name,
SerializerInterface $serializer = new DefaultSerializer()
SerializerInterface $serializer = new DefaultSerializer()
) {
$this->rpc = $rpc->withCodec(new ProtobufCodec());
$this->zone = new \DateTimeZone('UTC');
Expand Down
4 changes: 2 additions & 2 deletions src/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ public function select(string $name): StorageInterface
{
if ($this->rpc instanceof AsyncRPCInterface) {
return new AsyncCache($this->rpc, $name, $this->getSerializer());
} else {
return new Cache($this->rpc, $name, $this->getSerializer());
}

return new Cache($this->rpc, $name, $this->getSerializer());
}
}
Loading

0 comments on commit bc6e142

Please sign in to comment.