Skip to content

Commit db011b1

Browse files
committed
feat: implement health check functionality for database and ClickHouse adapters
1 parent 5236502 commit db011b1

6 files changed

Lines changed: 180 additions & 2 deletions

File tree

docker-compose.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ services:
3131
networks:
3232
- usage
3333
ports:
34-
- "8123:8123"
35-
- "9000:9000"
34+
- "8124:8123"
35+
- "9001:9000"
3636
healthcheck:
3737
test: ["CMD", "clickhouse-client", "--host=localhost", "--port=9000", "-q", "SELECT 1"]
3838
interval: 5s

src/Usage/Adapter.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ abstract class Adapter
99
*/
1010
abstract public function getName(): string;
1111

12+
/**
13+
* Check adapter health and connection status
14+
*
15+
* @return array<string, mixed> Health check result with 'healthy' bool and additional adapter-specific information
16+
*/
17+
abstract public function healthCheck(): array;
18+
1219
/**
1320
* Setup database structure
1421
*/

src/Usage/Adapter/Database.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,46 @@ public function getName(): string
2727
return 'Database';
2828
}
2929

30+
/**
31+
* Check database connection health and collection existence.
32+
*
33+
* @return array{healthy: bool, database?: string, collection?: string, error?: string}
34+
*/
35+
public function healthCheck(): array
36+
{
37+
try {
38+
// Check if database exists
39+
$databaseName = $this->db->getDatabase();
40+
if (!$this->db->exists($databaseName)) {
41+
return [
42+
'healthy' => false,
43+
'error' => "Database '{$databaseName}' does not exist"
44+
];
45+
}
46+
47+
// Check if collection exists
48+
$collectionName = $this->collection ?? 'usage';
49+
if (!$this->db->getCollection($collectionName)->isEmpty()) {
50+
return [
51+
'healthy' => true,
52+
'database' => $databaseName,
53+
'collection' => $collectionName
54+
];
55+
}
56+
57+
return [
58+
'healthy' => true,
59+
'database' => $databaseName,
60+
'collection' => $collectionName
61+
];
62+
} catch (\Exception $e) {
63+
return [
64+
'healthy' => false,
65+
'error' => $e->getMessage()
66+
];
67+
}
68+
}
69+
3070
public function setup(): void
3171
{
3272
$this->collection = 'usage';

src/Usage/Usage.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,16 @@ public function getAdapter(): Adapter
4040
return $this->adapter;
4141
}
4242

43+
/**
44+
* Check adapter health and connection status.
45+
*
46+
* @return array<string, mixed> Health check result with 'healthy' bool and additional adapter-specific information
47+
*/
48+
public function healthCheck(): array
49+
{
50+
return $this->adapter->healthCheck();
51+
}
52+
4353
/**
4454
* Setup the usage metrics storage.
4555
*

tests/Usage/Adapter/ClickHouseTest.php

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,4 +387,66 @@ public function testFind(): void
387387
// First should be A (10), Second B (20)
388388
$this->assertTrue($results[0]->getValue() <= $results[1]->getValue());
389389
}
390+
391+
/**
392+
* Test healthCheck() method
393+
*/
394+
public function testHealthCheck(): void
395+
{
396+
$adapter = $this->usage->getAdapter();
397+
398+
$health = $adapter->healthCheck();
399+
400+
// Assert basic structure
401+
$this->assertIsArray($health);
402+
$this->assertArrayHasKey('healthy', $health);
403+
$this->assertArrayHasKey('host', $health);
404+
$this->assertArrayHasKey('port', $health);
405+
$this->assertArrayHasKey('database', $health);
406+
$this->assertArrayHasKey('secure', $health);
407+
408+
// Assert connection is healthy
409+
$this->assertTrue($health['healthy'], 'ClickHouse should be healthy');
410+
411+
// Assert additional fields are present when healthy
412+
$this->assertArrayHasKey('version', $health);
413+
$this->assertArrayHasKey('uptime', $health);
414+
$this->assertArrayHasKey('response_time', $health);
415+
$this->assertIsString($health['version']);
416+
$this->assertIsInt($health['uptime']);
417+
$this->assertIsFloat($health['response_time']);
418+
$this->assertGreaterThan(0, $health['response_time']);
419+
}
420+
421+
/**
422+
* Test healthCheck() with invalid connection
423+
*/
424+
public function testHealthCheckFailure(): void
425+
{
426+
// Create adapter with invalid host
427+
$adapter = new ClickHouseAdapter('invalid-host-that-does-not-exist', 'default', '', 8123, false);
428+
429+
$health = $adapter->healthCheck();
430+
431+
// Assert basic structure
432+
$this->assertIsArray($health);
433+
$this->assertArrayHasKey('healthy', $health);
434+
$this->assertArrayHasKey('host', $health);
435+
436+
// Assert connection failed
437+
$this->assertFalse($health['healthy'], 'ClickHouse should be unhealthy with invalid host');
438+
439+
// Assert error message is present
440+
$this->assertArrayHasKey('error', $health);
441+
if (isset($health['error'])) {
442+
$this->assertIsString($health['error']);
443+
$this->assertNotEmpty($health['error']);
444+
}
445+
446+
// Assert response time is still recorded
447+
$this->assertArrayHasKey('response_time', $health);
448+
if (isset($health['response_time'])) {
449+
$this->assertIsFloat($health['response_time']);
450+
}
451+
}
390452
}

tests/Usage/Adapter/DatabaseTest.php

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,63 @@ protected function initializeUsage(): void
4949
// ignore duplicate exception
5050
}
5151
}
52+
53+
/**
54+
* Test healthCheck() method
55+
*/
56+
public function testHealthCheck(): void
57+
{
58+
$adapter = $this->usage->getAdapter();
59+
60+
$health = $adapter->healthCheck();
61+
62+
// Assert basic structure
63+
$this->assertIsArray($health);
64+
$this->assertArrayHasKey('healthy', $health);
65+
66+
// Assert connection is healthy
67+
$this->assertTrue($health['healthy'], 'Database should be healthy');
68+
69+
// Assert additional fields are present when healthy
70+
$this->assertArrayHasKey('database', $health);
71+
$this->assertArrayHasKey('collection', $health);
72+
$this->assertIsString($health['database']);
73+
$this->assertIsString($health['collection']);
74+
}
75+
76+
/**
77+
* Test healthCheck() with database that doesn't exist
78+
*/
79+
public function testHealthCheckWithNonExistentDatabase(): void
80+
{
81+
// Create a new database instance pointing to a non-existent database
82+
$dbHost = 'mariadb';
83+
$dbPort = '3306';
84+
$dbUser = 'root';
85+
$dbPass = 'password';
86+
87+
$pdo = new PDO("mysql:host={$dbHost};port={$dbPort};charset=utf8mb4", $dbUser, $dbPass, MariaDB::getPdoAttributes());
88+
$cache = new Cache(new NoCache());
89+
$database = new Database(new MariaDB($pdo), $cache);
90+
$database->setDatabase('nonexistent_database_xyz');
91+
$database->setNamespace('test');
92+
93+
$adapter = new AdapterDatabase($database);
94+
95+
$health = $adapter->healthCheck();
96+
97+
// Assert basic structure
98+
$this->assertIsArray($health);
99+
$this->assertArrayHasKey('healthy', $health);
100+
101+
// Assert connection failed
102+
$this->assertFalse($health['healthy'], 'Database should be unhealthy with non-existent database');
103+
104+
// Assert error message is present
105+
$this->assertArrayHasKey('error', $health);
106+
if (isset($health['error'])) {
107+
$this->assertIsString($health['error']);
108+
$this->assertNotEmpty($health['error']);
109+
}
110+
}
52111
}

0 commit comments

Comments
 (0)