mirror of
https://github.com/predis/predis.git
synced 2026-09-09 09:56:55 +00:00
afc8035491
* Revert GCRA command support * Updated test image * Fixed flacky test
335 lines
12 KiB
PHP
335 lines
12 KiB
PHP
<?php
|
|
|
|
/*
|
|
* This file is part of the Predis package.
|
|
*
|
|
* (c) 2009-2020 Daniele Alessandri
|
|
* (c) 2021-2026 Till Krüss
|
|
*
|
|
* For the full copyright and license information, please view the LICENSE
|
|
* file that was distributed with this source code.
|
|
*/
|
|
|
|
namespace Predis\Command\Redis;
|
|
|
|
use Predis\Command\Container\HOTKEYS as Container;
|
|
use Predis\NotSupportedException;
|
|
use ValueError;
|
|
|
|
/**
|
|
* @group commands
|
|
* @group relay-incompatible
|
|
* @group realm-generic
|
|
*/
|
|
class HOTKEYS_Test extends PredisCommandTestCase
|
|
{
|
|
/**
|
|
* {@inheritdoc}
|
|
*/
|
|
protected function getExpectedCommand(): string
|
|
{
|
|
return HOTKEYS::class;
|
|
}
|
|
|
|
/**
|
|
* {@inheritdoc}
|
|
*/
|
|
protected function getExpectedId(): string
|
|
{
|
|
return 'HOTKEYS';
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
* @dataProvider argumentsProvider
|
|
*/
|
|
public function testFilterArguments(array $actualArguments, array $expectedArguments): void
|
|
{
|
|
$command = $this->getCommand();
|
|
$command->setArguments($actualArguments);
|
|
|
|
$this->assertSame($expectedArguments, $command->getArguments());
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testParseResponse(): void
|
|
{
|
|
$this->assertSame(1, $this->getCommand()->parseResponse(1));
|
|
}
|
|
|
|
/**
|
|
* @group connected
|
|
* @requiresRedisVersion >= 8.5.0
|
|
* @return void
|
|
*/
|
|
public function testRetrieveHotKeys()
|
|
{
|
|
$redis = $this->getClient();
|
|
|
|
// Starts hotkeys tracking (CPU only)
|
|
$this->assertEquals('OK', $redis->hotkeys->start([Container::CPU]));
|
|
$this->assertEquals('OK', $redis->set('key', 'value'));
|
|
$this->assertEquals('OK', $redis->hotkeys->stop());
|
|
|
|
$hotkeysInfo = $redis->hotkeys->get()[0];
|
|
$this->assertContains('key', $hotkeysInfo['by-cpu-time-us']);
|
|
|
|
// Starts hotkeys tracking (CPU and NET)
|
|
$this->assertEquals('OK', $redis->hotkeys->start([Container::CPU, Container::NET]));
|
|
$this->assertEquals('OK', $redis->set('key', 'value'));
|
|
$this->assertEquals('OK', $redis->set('key1', 'value1'));
|
|
$this->assertEquals('OK', $redis->hotkeys->stop());
|
|
|
|
$hotkeysInfo = $redis->hotkeys->get()[0];
|
|
|
|
$this->assertContains('key', $hotkeysInfo['by-cpu-time-us']);
|
|
$this->assertContains('key1', $hotkeysInfo['by-cpu-time-us']);
|
|
$this->assertContains('key', $hotkeysInfo['by-net-bytes']);
|
|
$this->assertContains('key1', $hotkeysInfo['by-net-bytes']);
|
|
|
|
// Starts hotkeys tracking (limited COUNT)
|
|
$this->assertEquals('OK', $redis->hotkeys->start([Container::CPU, Container::NET], 12));
|
|
|
|
for ($i = 0; $i < 13; $i++) {
|
|
$redis->set("key:$i", "value:$i");
|
|
}
|
|
$this->assertEquals('OK', $redis->hotkeys->stop());
|
|
|
|
$hotkeysInfo = $redis->hotkeys->get()[0];
|
|
$tracked = count($hotkeysInfo['by-cpu-time-us']);
|
|
// COUNT 12 is an upper bound on tracked keys (24 = 12 key/score pairs);
|
|
// sampling may return fewer entries, but always in even-sized key/score pairs.
|
|
$this->assertNotEmpty($hotkeysInfo['by-cpu-time-us']);
|
|
$this->assertLessThanOrEqual(24, $tracked);
|
|
$this->assertSame(0, $tracked % 2);
|
|
|
|
// Starts hotkeys tracking (with DURATION, SAMPLE)
|
|
$this->assertEquals(
|
|
'OK',
|
|
$redis->hotkeys->start([Container::CPU, Container::NET], null, 1, 10)
|
|
);
|
|
$this->sleep(1.2);
|
|
|
|
$hotkeysInfo = $redis->hotkeys->get()[0];
|
|
$this->assertEquals(0, $hotkeysInfo['tracking-active']);
|
|
$this->assertEquals(10, $hotkeysInfo['sample-ratio']);
|
|
|
|
$this->assertEquals('OK', $redis->hotkeys->reset());
|
|
|
|
$hotkeysInfo = $redis->hotkeys->get()[0];
|
|
$this->assertEquals(0, $hotkeysInfo['tracking-active']);
|
|
$this->assertNull($hotkeysInfo['sample-ratio']);
|
|
$this->assertEmpty($hotkeysInfo['selected-slots']);
|
|
$this->assertEmpty($hotkeysInfo['by-cpu-time-us']);
|
|
$this->assertEmpty($hotkeysInfo['by-net-bytes']);
|
|
}
|
|
|
|
/**
|
|
* @group connected
|
|
* @requiresRedisVersion >= 8.5.0
|
|
* @return void
|
|
*/
|
|
public function testRetrieveHotKeysResp3()
|
|
{
|
|
$redis = $this->getResp3Client();
|
|
|
|
// Starts hotkeys tracking (CPU only)
|
|
$this->assertEquals('OK', $redis->hotkeys->start([Container::CPU]));
|
|
$this->assertEquals('OK', $redis->set('key', 'value'));
|
|
$this->assertEquals('OK', $redis->hotkeys->stop());
|
|
|
|
$hotkeysInfo = $redis->hotkeys->get()[0];
|
|
$this->assertContains('key', $hotkeysInfo['by-cpu-time-us']);
|
|
|
|
// Starts hotkeys tracking (CPU and NET)
|
|
$this->assertEquals('OK', $redis->hotkeys->start([Container::CPU, Container::NET]));
|
|
$this->assertEquals('OK', $redis->set('key', 'value'));
|
|
$this->assertEquals('OK', $redis->set('key1', 'value1'));
|
|
$this->assertEquals('OK', $redis->hotkeys->stop());
|
|
|
|
$hotkeysInfo = $redis->hotkeys->get()[0];
|
|
|
|
$this->assertContains('key', $hotkeysInfo['by-cpu-time-us']);
|
|
$this->assertContains('key1', $hotkeysInfo['by-cpu-time-us']);
|
|
$this->assertContains('key', $hotkeysInfo['by-net-bytes']);
|
|
$this->assertContains('key1', $hotkeysInfo['by-net-bytes']);
|
|
|
|
// Starts hotkeys tracking (limited COUNT)
|
|
$this->assertEquals('OK', $redis->hotkeys->start([Container::CPU, Container::NET], 12));
|
|
|
|
for ($i = 0; $i < 13; $i++) {
|
|
$redis->set("key:$i", "value:$i");
|
|
}
|
|
$this->assertEquals('OK', $redis->hotkeys->stop());
|
|
|
|
$hotkeysInfo = $redis->hotkeys->get()[0];
|
|
$tracked = count($hotkeysInfo['by-cpu-time-us']);
|
|
// COUNT 12 is an upper bound on tracked keys (24 = 12 key/score pairs);
|
|
// sampling may return fewer entries, but always in even-sized key/score pairs.
|
|
$this->assertNotEmpty($hotkeysInfo['by-cpu-time-us']);
|
|
$this->assertLessThanOrEqual(24, $tracked);
|
|
$this->assertSame(0, $tracked % 2);
|
|
|
|
// Starts hotkeys tracking (with DURATION, SAMPLE)
|
|
$this->assertEquals(
|
|
'OK',
|
|
$redis->hotkeys->start([Container::CPU, Container::NET], null, 1, 10)
|
|
);
|
|
$this->sleep(1.2);
|
|
|
|
$hotkeysInfo = $redis->hotkeys->get()[0];
|
|
$this->assertEquals(0, $hotkeysInfo['tracking-active']);
|
|
$this->assertEquals(10, $hotkeysInfo['sample-ratio']);
|
|
|
|
$this->assertEquals('OK', $redis->hotkeys->reset());
|
|
|
|
$hotkeysInfo = $redis->hotkeys->get()[0];
|
|
$this->assertEquals(0, $hotkeysInfo['tracking-active']);
|
|
$this->assertNull($hotkeysInfo['sample-ratio']);
|
|
$this->assertEmpty($hotkeysInfo['selected-slots']);
|
|
$this->assertEmpty($hotkeysInfo['by-cpu-time-us']);
|
|
$this->assertEmpty($hotkeysInfo['by-net-bytes']);
|
|
}
|
|
|
|
/**
|
|
* @group connected
|
|
* @group cluster
|
|
* @requiresRedisVersion >= 8.5.0
|
|
* @return void
|
|
*/
|
|
public function testHotkeysStartDisabledInClusterClient()
|
|
{
|
|
$redis = $this->getClient();
|
|
|
|
$this->expectException(NotSupportedException::class);
|
|
$this->expectExceptionMessage("Cannot use 'HOTKEYS' with redis-cluster");
|
|
|
|
$redis->hotkeys->start([Container::CPU]);
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testThrowsExceptionOnInvalidSampleValue(): void
|
|
{
|
|
$command = $this->getCommand();
|
|
|
|
$this->expectException(ValueError::class);
|
|
$this->expectExceptionMessage('Sample value should be greater than 0');
|
|
|
|
$command->setArguments(['START', ['metric1', 'metric2'], null, null, 0]);
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testThrowsExceptionOnNegativeSampleValue(): void
|
|
{
|
|
$command = $this->getCommand();
|
|
|
|
$this->expectException(ValueError::class);
|
|
$this->expectExceptionMessage('Sample value should be greater than 0');
|
|
|
|
$command->setArguments(['START', ['metric1', 'metric2'], null, null, -1]);
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testThrowsExceptionOnCountValueTooLow(): void
|
|
{
|
|
$command = $this->getCommand();
|
|
|
|
$this->expectException(ValueError::class);
|
|
$this->expectExceptionMessage('Count value should be between 10 and 64');
|
|
|
|
$command->setArguments(['START', ['metric1', 'metric2'], 9]);
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testThrowsExceptionOnCountValueTooHigh(): void
|
|
{
|
|
$command = $this->getCommand();
|
|
|
|
$this->expectException(ValueError::class);
|
|
$this->expectExceptionMessage('Count value should be between 10 and 64');
|
|
|
|
$command->setArguments(['START', ['metric1', 'metric2'], 65]);
|
|
}
|
|
|
|
/**
|
|
* @group disconnected
|
|
*/
|
|
public function testThrowsExceptionOnCountValueZero(): void
|
|
{
|
|
$command = $this->getCommand();
|
|
|
|
$this->expectException(ValueError::class);
|
|
$this->expectExceptionMessage('Count value should be between 10 and 64');
|
|
|
|
$command->setArguments(['START', ['metric1', 'metric2'], 0]);
|
|
}
|
|
|
|
public function argumentsProvider(): array
|
|
{
|
|
return [
|
|
'with non-START subcommand' => [
|
|
['STOP'],
|
|
['STOP'],
|
|
],
|
|
'with START and metrics only' => [
|
|
['START', ['metric1', 'metric2']],
|
|
['START', 'METRICS', 2, 'metric1', 'metric2'],
|
|
],
|
|
'with START, metrics and COUNT' => [
|
|
['START', ['metric1', 'metric2'], 50],
|
|
['START', 'METRICS', 2, 'metric1', 'metric2', 'COUNT', 50],
|
|
],
|
|
'with START, metrics, COUNT and DURATION' => [
|
|
['START', ['metric1', 'metric2'], 50, 60],
|
|
['START', 'METRICS', 2, 'metric1', 'metric2', 'COUNT', 50, 'DURATION', 60],
|
|
],
|
|
'with START, metrics, COUNT, DURATION and SAMPLE' => [
|
|
['START', ['metric1', 'metric2'], 50, 60, 10],
|
|
['START', 'METRICS', 2, 'metric1', 'metric2', 'COUNT', 50, 'DURATION', 60, 'SAMPLE', 10],
|
|
],
|
|
'with START, metrics, COUNT, DURATION, SAMPLE and SLOTS' => [
|
|
['START', ['metric1', 'metric2'], 50, 60, 10, [1, 2, 3]],
|
|
['START', 'METRICS', 2, 'metric1', 'metric2', 'COUNT', 50, 'DURATION', 60, 'SAMPLE', 10, 'SLOTS', 3, 1, 2, 3],
|
|
],
|
|
'with START, metrics and SLOTS (no COUNT, DURATION, SAMPLE)' => [
|
|
['START', ['metric1'], null, null, null, [5, 10]],
|
|
['START', 'METRICS', 1, 'metric1', 'SLOTS', 2, 5, 10],
|
|
],
|
|
'with START, metrics, COUNT and SLOTS (no DURATION, SAMPLE)' => [
|
|
['START', ['metric1'], 50, null, null, [1, 2]],
|
|
['START', 'METRICS', 1, 'metric1', 'COUNT', 50, 'SLOTS', 2, 1, 2],
|
|
],
|
|
'with START, metrics, COUNT, DURATION and SLOTS (no SAMPLE)' => [
|
|
['START', ['metric1'], 50, 30, null, [1]],
|
|
['START', 'METRICS', 1, 'metric1', 'COUNT', 50, 'DURATION', 30, 'SLOTS', 1, 1],
|
|
],
|
|
'with START and single metric' => [
|
|
['START', ['metric1']],
|
|
['START', 'METRICS', 1, 'metric1'],
|
|
],
|
|
'with START and multiple metrics' => [
|
|
['START', ['metric1', 'metric2', 'metric3']],
|
|
['START', 'METRICS', 3, 'metric1', 'metric2', 'metric3'],
|
|
],
|
|
'with START, metrics and minimum valid COUNT (10)' => [
|
|
['START', ['metric1'], 10],
|
|
['START', 'METRICS', 1, 'metric1', 'COUNT', 10],
|
|
],
|
|
'with START, metrics and maximum valid COUNT (64)' => [
|
|
['START', ['metric1'], 64],
|
|
['START', 'METRICS', 1, 'metric1', 'COUNT', 64],
|
|
],
|
|
];
|
|
}
|
|
}
|