From 43ecf3f4fb6beb77a5cabb81aa4465ce62d07ff4 Mon Sep 17 00:00:00 2001 From: Vladyslav Vildanov <117659936+vladvildanov@users.noreply.github.com> Date: Fri, 3 Feb 2023 12:13:42 +0200 Subject: [PATCH] Added support for TDigest sketch, added TDIGEST.CREATE, TDIGEST.INFO commands (#1093) Co-authored-by: Vladyslav Vildanov --- src/ClientConfiguration.php | 1 + src/ClientContextInterface.php | 2 + src/ClientInterface.php | 2 + src/Command/Redis/TDigest/TDIGESTCREATE.php | 40 ++++++ src/Command/Redis/TDigest/TDIGESTINFO.php | 41 ++++++ .../Redis/TDigest/TDIGESTCREATE_Test.php | 130 ++++++++++++++++++ .../Redis/TDigest/TDIGESTINFO_Test.php | 113 +++++++++++++++ 7 files changed, 329 insertions(+) create mode 100644 src/Command/Redis/TDigest/TDIGESTCREATE.php create mode 100644 src/Command/Redis/TDigest/TDIGESTINFO.php create mode 100644 tests/Predis/Command/Redis/TDigest/TDIGESTCREATE_Test.php create mode 100644 tests/Predis/Command/Redis/TDigest/TDIGESTINFO_Test.php diff --git a/src/ClientConfiguration.php b/src/ClientConfiguration.php index 7f0087f4..a2d3d2ae 100644 --- a/src/ClientConfiguration.php +++ b/src/ClientConfiguration.php @@ -23,6 +23,7 @@ class ClientConfiguration ['name' => 'BloomFilter', 'commandPrefix' => 'BF'], ['name' => 'CuckooFilter', 'commandPrefix' => 'CF'], ['name' => 'CountMinSketch', 'commandPrefix' => 'CMS'], + ['name' => 'TDigest', 'commandPrefix' => 'TDIGEST'], ], ]; diff --git a/src/ClientContextInterface.php b/src/ClientContextInterface.php index bc77d2cb..f3dd2ba6 100644 --- a/src/ClientContextInterface.php +++ b/src/ClientContextInterface.php @@ -173,6 +173,8 @@ use Predis\Command\CommandInterface; * @method $this sscan($key, $cursor, array $options = null) * @method $this sunion(array|string $keys) * @method $this sunionstore($destination, array|string $keys) + * @method $this tdigestcreate(string $key, int $compression = 0) + * @method $this tdigestinfo(string $key) * @method $this zadd($key, array $membersAndScoresDictionary) * @method $this zcard($key) * @method $this zcount($key, $min, $max) diff --git a/src/ClientInterface.php b/src/ClientInterface.php index 1748c345..52881117 100644 --- a/src/ClientInterface.php +++ b/src/ClientInterface.php @@ -183,6 +183,8 @@ use Predis\Response\Status; * @method string[] sunion(array|string $keys) * @method int sunionstore(string $destination, array|string $keys) * @method int touch(string[]|string $keyOrKeys, string ...$keys = null) + * @method Status tdigestcreate(string $key, int $compression = 0) + * @method array tdigestinfo(string $key) * @method string xadd(string $key, array $dictionary, string $id = '*', array $options = null) * @method int xdel(string $key, string ...$id) * @method int xlen(string $key) diff --git a/src/Command/Redis/TDigest/TDIGESTCREATE.php b/src/Command/Redis/TDigest/TDIGESTCREATE.php new file mode 100644 index 00000000..68db397e --- /dev/null +++ b/src/Command/Redis/TDigest/TDIGESTCREATE.php @@ -0,0 +1,40 @@ +getCommand(); + $command->setArguments($actualArguments); + + $this->assertSameValues($expectedArguments, $command->getArguments()); + } + + /** + * @group disconnected + */ + public function testParseResponse(): void + { + $this->assertSame(1, $this->getCommand()->parseResponse(1)); + } + + /** + * @group connected + * @dataProvider sketchesProvider + * @param array $createArguments + * @param string $key + * @param int $expectedCompression + * @return void + * @requiresRedisBfVersion >= 2.4.0 + */ + public function testCreateTDigestSketchWithGivenConfiguration( + array $createArguments, + string $key, + int $expectedCompression + ): void { + $redis = $this->getClient(); + + $actualResponse = $redis->tdigestcreate(...$createArguments); + $info = $redis->tdigestinfo($key); + + $this->assertEquals('OK', $actualResponse); + $this->assertSame($expectedCompression, $info['Compression']); + } + + /** + * @group connected + * @return void + * @requiresRedisBfVersion >= 2.4.0 + */ + public function testThrowsExceptionOnAlreadyCreatedKey(): void + { + $redis = $this->getClient(); + + $actualResponse = $redis->tdigestcreate('key'); + $this->assertEquals('OK', $actualResponse); + + $this->expectException(ServerException::class); + $this->expectExceptionMessage('ERR T-Digest: key already exists'); + + $redis->tdigestcreate('key'); + } + + public function argumentsProvider(): array + { + return [ + 'with default arguments' => [ + ['key'], + ['key'], + ], + 'with 0 compression' => [ + ['key', 0], + ['key'], + ], + 'with COMPRESSION modifier' => [ + ['key', 100], + ['key', 'COMPRESSION', 100], + ], + ]; + } + + public function sketchesProvider(): array + { + return [ + 'with default arguments' => [ + ['key'], + 'key', + 100, + ], + 'with modified COMPRESSION' => [ + ['key', 120], + 'key', + 120, + ], + ]; + } +} diff --git a/tests/Predis/Command/Redis/TDigest/TDIGESTINFO_Test.php b/tests/Predis/Command/Redis/TDigest/TDIGESTINFO_Test.php new file mode 100644 index 00000000..ac94f15c --- /dev/null +++ b/tests/Predis/Command/Redis/TDigest/TDIGESTINFO_Test.php @@ -0,0 +1,113 @@ +getCommand(); + $command->setArguments($actualArguments); + + $this->assertSameValues($expectedArguments, $command->getArguments()); + } + + /** + * @group disconnected + */ + public function testParseResponse(): void + { + $actualResponse = [ + 'Compression', 100, 'Capacity', 610, 'Merged nodes', 0, 'Unmerged nodes', 5, 'Merged weight', 0, + 'Unmerged weight', 5, 'Observations', 5, 'Total compressions', 0, 'Memory usage', 9768, + ]; + $expectedResponse = [ + 'Compression' => 100, + 'Capacity' => 610, + 'Merged nodes' => 0, + 'Unmerged nodes' => 5, + 'Merged weight' => 0, + 'Unmerged weight' => 5, + 'Observations' => 5, + 'Total compressions' => 0, + 'Memory usage' => 9768, + ]; + + $this->assertSame($expectedResponse, $this->getCommand()->parseResponse($actualResponse)); + } + + /** + * @group connected + * @return void + * @requiresRedisBfVersion >= 2.4.0 + */ + public function testInfoReturnsInformationAboutGivenTDigestSketch(): void + { + $redis = $this->getClient(); + $expectedResponse = [ + 'Compression' => 100, + 'Capacity' => 610, + 'Merged nodes' => 0, + 'Unmerged nodes' => 0, + 'Merged weight' => 0, + 'Unmerged weight' => 0, + 'Observations' => 0, + 'Total compressions' => 0, + 'Memory usage' => 9768, + ]; + + $redis->tdigestcreate('key'); + + $this->assertSame($expectedResponse, $redis->tdigestinfo('key')); + } + + /** + * @group connected + * @return void + * @requiresRedisBfVersion >= 2.4.0 + */ + public function testThrowsExceptionOnNonExistingTDigestSketch(): void + { + $redis = $this->getClient(); + + $this->expectException(ServerException::class); + $this->expectExceptionMessage('ERR T-Digest: key does not exist'); + + $redis->tdigestinfo('key'); + } +}