Skip to content

Commit 563f988

Browse files
LassombriaAnasatasiia Sh
andauthored
Add PING/PONG strategy for redis health check (#18)
* Add PING/PONG strategy for redis health check --------- Co-authored-by: Anasatasiia Sh <anastasiya.sh@mserverone.com>
1 parent 78b02c2 commit 563f988

7 files changed

Lines changed: 302 additions & 4 deletions

File tree

.github/workflows/tests.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ jobs:
161161
id: composer-cache
162162
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
163163

164-
- uses: actions/cache@v2
164+
- uses: actions/cache@v4
165165
with:
166166
path: ${{ steps.composer-cache.outputs.dir }}
167167
key: ${{ runner.os }}-composer-${{ matrix.php }}

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ Integrate with applications
103103

104104
### Dependency Injection
105105

106-
You can easy integrate this library to Symfony application, or application with DependencyInjection support.
106+
You can easily integrate this library to Symfony application, or application with DependencyInjection support.
107107

108108
For integrate, you can add the compiler pass to your container builder:
109109

@@ -121,7 +121,7 @@ $containerBuilder->addCompilerPass(new AddDiagnosticToBuilderCheckPass());
121121
$containerBuilder->compile();
122122
```
123123

124-
Add add check services:
124+
And add check services:
125125

126126
```yaml
127127
services:
@@ -176,7 +176,9 @@ Before create the PR or merge into develop, please run next commands for validat
176176
./bin/phpunit
177177

178178
./bin/phpcs --config-set show_warnings 0
179-
./bin/phpcs --standard=vendor/escapestudios/symfony2-coding-standard/Symfony/ src/
179+
./bin/phpcs --standard=src/phpcs-ruleset.xml src/
180180
./bin/phpcs --standard=tests/phpcs-ruleset.xml tests/
181181

182+
php -d memory_limit=-1 ./bin/phpstan
183+
182184
```
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the FiveLab Diagnostic package.
5+
*
6+
* (c) FiveLab <mail@fivelab.org>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types = 1);
13+
14+
namespace FiveLab\Component\Diagnostic\Check\Redis\Predis;
15+
16+
use FiveLab\Component\Diagnostic\Check\CheckInterface;
17+
use FiveLab\Component\Diagnostic\Result\Failure;
18+
use FiveLab\Component\Diagnostic\Result\Result;
19+
use FiveLab\Component\Diagnostic\Result\Success;
20+
use Predis\Client;
21+
22+
readonly class PredisPingPongCheck implements CheckInterface
23+
{
24+
public function __construct(private string $host, private int $port, private ?string $password = null)
25+
{
26+
}
27+
28+
public function check(): Result
29+
{
30+
if (!\class_exists(Client::class)) {
31+
return new Failure('The predis/predis not installed.');
32+
}
33+
34+
$parameters = [
35+
'host' => $this->host,
36+
'port' => $this->port,
37+
];
38+
39+
if ($this->password) {
40+
$parameters['password'] = $this->password;
41+
}
42+
43+
try {
44+
$client = new Client($parameters);
45+
$client->ping();
46+
} catch (\Throwable $e) {
47+
return new Failure(\sprintf(
48+
'Cannot connect to Redis: %s.',
49+
\rtrim($e->getMessage(), '.')
50+
));
51+
}
52+
53+
return new Success('Success connect to Redis.');
54+
}
55+
56+
public function getExtraParameters(): array
57+
{
58+
// By security we not return password (because many redis instances work in internal network).
59+
return [
60+
'host' => $this->host,
61+
'port' => $this->port,
62+
];
63+
}
64+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the FiveLab Diagnostic package.
5+
*
6+
* (c) FiveLab <mail@fivelab.org>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types = 1);
13+
14+
namespace FiveLab\Component\Diagnostic\Check\Redis\RedisExt;
15+
16+
use FiveLab\Component\Diagnostic\Check\CheckInterface;
17+
use FiveLab\Component\Diagnostic\Result\Failure;
18+
use FiveLab\Component\Diagnostic\Result\Result;
19+
use FiveLab\Component\Diagnostic\Result\Success;
20+
21+
readonly class RedisPingPongCheck implements CheckInterface
22+
{
23+
public function __construct(private string $host, private int $port, private ?string $password = null)
24+
{
25+
}
26+
27+
public function check(): Result
28+
{
29+
if (!\class_exists(\Redis::class)) {
30+
return new Failure('The ext-redis not installed.');
31+
}
32+
33+
$redis = new \Redis();
34+
35+
\set_error_handler(static function (int $errno, string $errstr) {
36+
throw new \Exception($errstr);
37+
});
38+
39+
try {
40+
$this->connect($redis);
41+
$redis->ping();
42+
} catch (\Throwable $e) {
43+
return new Failure(\sprintf(
44+
'Cannot connect to Redis: %s.',
45+
\rtrim($e->getMessage(), '.')
46+
));
47+
} finally {
48+
\restore_error_handler();
49+
}
50+
51+
return new Success('Success connect to Redis.');
52+
}
53+
54+
public function getExtraParameters(): array
55+
{
56+
// By security we not return password (because many redis instances work in internal network).
57+
return [
58+
'host' => $this->host,
59+
'port' => $this->port,
60+
];
61+
}
62+
63+
private function connect(\Redis $redis): void
64+
{
65+
$redis->connect($this->host, $this->port);
66+
67+
if ($this->password) {
68+
$redis->auth($this->password);
69+
}
70+
}
71+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the FiveLab Diagnostic package.
5+
*
6+
* (c) FiveLab <mail@fivelab.org>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types = 1);
13+
14+
namespace FiveLab\Component\Diagnostic\Tests\Check\Redis\Predis;
15+
16+
use FiveLab\Component\Diagnostic\Check\Redis\Predis\PredisPingPongCheck;
17+
use FiveLab\Component\Diagnostic\Result\Failure;
18+
use FiveLab\Component\Diagnostic\Result\Success;
19+
use FiveLab\Component\Diagnostic\Tests\Check\AbstractRedisTestCase;
20+
use PHPUnit\Framework\Attributes\Test;
21+
22+
class PredisPingPongCheckTest extends AbstractRedisTestCase
23+
{
24+
protected function setUp(): void
25+
{
26+
if (!$this->canTestingWithRedis()) {
27+
self::markTestSkipped('The Redis is not configured.');
28+
}
29+
30+
if (!\class_exists(\Redis::class)) {
31+
self::markTestSkipped('The ext-redis not installed.');
32+
}
33+
}
34+
35+
#[Test]
36+
public function shouldSuccessCheck(): void
37+
{
38+
$check = new PredisPingPongCheck(
39+
$this->getRedisHost(),
40+
$this->getRedisPort(),
41+
$this->getRedisPassword()
42+
);
43+
44+
$result = $check->check();
45+
46+
self::assertEquals(new Success('Success connect to Redis.'), $result);
47+
}
48+
49+
#[Test]
50+
public function shouldSuccessGetExtraParameters(): void
51+
{
52+
$check = new PredisPingPongCheck(
53+
$this->getRedisHost(),
54+
$this->getRedisPort(),
55+
$this->getRedisPassword()
56+
);
57+
58+
self::assertEquals([
59+
'host' => $this->getRedisHost(),
60+
'port' => $this->getRedisPort(),
61+
], $check->getExtraParameters());
62+
}
63+
64+
#[Test]
65+
public function shouldFailIfHostIsWrong(): void
66+
{
67+
$check = new PredisPingPongCheck(
68+
$this->getRedisHost().'some',
69+
$this->getRedisPort(),
70+
$this->getRedisPassword()
71+
);
72+
73+
$result = $check->check();
74+
75+
self::assertInstanceOf(Failure::class, $result);
76+
self::assertStringStartsWith('Cannot connect to Redis: php_network_getaddresses:', $result->message);
77+
}
78+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the FiveLab Diagnostic package.
5+
*
6+
* (c) FiveLab <mail@fivelab.org>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types = 1);
13+
14+
namespace FiveLab\Component\Diagnostic\Tests\Check\Redis\RedisExt;
15+
16+
use FiveLab\Component\Diagnostic\Check\Redis\RedisExt\RedisPingPongCheck;
17+
use FiveLab\Component\Diagnostic\Result\Failure;
18+
use FiveLab\Component\Diagnostic\Result\Success;
19+
use FiveLab\Component\Diagnostic\Tests\Check\AbstractRedisTestCase;
20+
use PHPUnit\Framework\Attributes\Test;
21+
22+
class RedisPingPongCheckTest extends AbstractRedisTestCase
23+
{
24+
protected function setUp(): void
25+
{
26+
if (!$this->canTestingWithRedis()) {
27+
self::markTestSkipped('The Redis is not configured.');
28+
}
29+
30+
if (!\class_exists(\Redis::class)) {
31+
self::markTestSkipped('The ext-redis not installed.');
32+
}
33+
}
34+
35+
#[Test]
36+
public function shouldSuccessCheck(): void
37+
{
38+
$check = new RedisPingPongCheck(
39+
$this->getRedisHost(),
40+
$this->getRedisPort(),
41+
$this->getRedisPassword()
42+
);
43+
44+
$result = $check->check();
45+
46+
self::assertEquals(new Success('Success connect to Redis.'), $result);
47+
}
48+
49+
#[Test]
50+
public function shouldSuccessGetExtraParameters(): void
51+
{
52+
$check = new RedisPingPongCheck(
53+
$this->getRedisHost(),
54+
$this->getRedisPort(),
55+
$this->getRedisPassword()
56+
);
57+
58+
self::assertEquals([
59+
'host' => $this->getRedisHost(),
60+
'port' => $this->getRedisPort(),
61+
], $check->getExtraParameters());
62+
}
63+
64+
#[Test]
65+
public function shouldFailIfHostIsWrong(): void
66+
{
67+
$check = new RedisPingPongCheck(
68+
$this->getRedisHost().'some',
69+
$this->getRedisPort(),
70+
$this->getRedisPassword()
71+
);
72+
73+
$result = $check->check();
74+
75+
self::assertInstanceOf(Failure::class, $result);
76+
self::assertStringStartsWith('Cannot connect to Redis: php_network_getaddresses:', $result->message);
77+
}
78+
}

tests/DependencyInjection/AddDiagnosticToBuilderCheckPassTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,13 @@ public function shouldSuccessProcessWithGroups(): void
9999
$containerBuilder = $this->createContainerBuilderWithDefinitionsBuilder();
100100

101101
$checkContainerDefinition1 = new Definition(StubCheck::class);
102+
102103
$checkContainerDefinition1->addTag('diagnostic.check', [
103104
'group' => 'foo',
104105
]);
105106

106107
$checkContainerDefinition2 = new Definition(StubCheck::class);
108+
107109
$checkContainerDefinition2->addTag('diagnostic.check', [
108110
'group' => 'bar',
109111
]);
@@ -147,12 +149,14 @@ public function shouldSuccessProcessWithCustomKeys(): void
147149
$containerBuilder = $this->createContainerBuilderWithDefinitionsBuilder();
148150

149151
$checkContainerDefinition1 = new Definition(StubCheck::class);
152+
150153
$checkContainerDefinition1->addTag('diagnostic.check', [
151154
'key' => 'foo',
152155
'error_on_failure' => true,
153156
]);
154157

155158
$checkContainerDefinition2 = new Definition(StubCheck::class);
159+
156160
$checkContainerDefinition2->addTag('diagnostic.check', [
157161
'key' => 'bar',
158162
'error_on_failure' => false,
@@ -197,6 +201,7 @@ public function shouldSuccessWithMultipleTagsForOneService(): void
197201
$containerBuilder = $this->createContainerBuilderWithDefinitionsBuilder();
198202

199203
$checkContainerDefinition = new Definition(StubCheck::class);
204+
200205
$checkContainerDefinition->addTag('diagnostic.check', [
201206
'group' => 'foo',
202207
]);

0 commit comments

Comments
 (0)