- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
241 lines
7.5 KiB
PHP
241 lines
7.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Framework\Database\HealthCheck;
|
|
|
|
use App\Framework\Core\ValueObjects\Duration;
|
|
use App\Framework\Database\ConnectionInterface;
|
|
use App\Framework\DateTime\SystemTimer;
|
|
use App\Framework\DateTime\Timer;
|
|
|
|
final readonly class ConnectionHealthChecker
|
|
{
|
|
public function __construct(
|
|
private Timer $timer,
|
|
private int $timeoutSeconds = 5,
|
|
private array $customQueries = []
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* Führt einen einfachen Health Check aus
|
|
*/
|
|
public function checkHealth(ConnectionInterface $connection): HealthCheckResult
|
|
{
|
|
$startTime = microtime(true);
|
|
|
|
try {
|
|
// Einfacher SELECT zum Testen der Verbindung
|
|
$result = $connection->queryScalar('SELECT 1');
|
|
|
|
$responseTime = (microtime(true) - $startTime) * 1000; // in Millisekunden
|
|
|
|
if ($result != 1) {
|
|
return HealthCheckResult::unhealthy(
|
|
$responseTime,
|
|
'Unexpected result from health check query',
|
|
null,
|
|
['expected' => 1, 'actual' => $result]
|
|
);
|
|
}
|
|
|
|
return HealthCheckResult::healthy($responseTime, 'Connection is healthy');
|
|
|
|
} catch (\Throwable $e) {
|
|
$responseTime = (microtime(true) - $startTime) * 1000;
|
|
|
|
return HealthCheckResult::error($e, $responseTime);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Führt einen Health Check mit Timeout aus
|
|
*/
|
|
public function checkHealthWithTimeout(ConnectionInterface $connection): HealthCheckResult
|
|
{
|
|
$startTime = time();
|
|
|
|
while ((time() - $startTime) < $this->timeoutSeconds) {
|
|
$result = $this->checkHealth($connection);
|
|
|
|
if ($result->isHealthy) {
|
|
return $result;
|
|
}
|
|
|
|
// Kurz warten vor nächstem Versuch
|
|
$this->timer->sleep(Duration::fromMilliseconds(100));
|
|
}
|
|
|
|
return HealthCheckResult::timeout($this->timeoutSeconds * 1000);
|
|
}
|
|
|
|
/**
|
|
* Führt erweiterte Health Checks aus
|
|
*/
|
|
public function checkDetailedHealth(ConnectionInterface $connection): HealthCheckResult
|
|
{
|
|
$startTime = microtime(true);
|
|
$additionalData = [];
|
|
|
|
try {
|
|
// 1. Basis Health Check
|
|
$basicResult = $this->checkHealth($connection);
|
|
if (! $basicResult->isHealthy) {
|
|
return $basicResult;
|
|
}
|
|
|
|
$additionalData['basic_check'] = $basicResult->toArray();
|
|
|
|
// 2. PDO-Status prüfen
|
|
$pdoStatus = $this->checkPdoStatus($connection);
|
|
$additionalData['pdo_status'] = $pdoStatus;
|
|
|
|
// 3. Custom Queries ausführen
|
|
if (! empty($this->customQueries)) {
|
|
$customResults = $this->executeCustomQueries($connection);
|
|
$additionalData['custom_queries'] = $customResults;
|
|
}
|
|
|
|
// 4. Connection Attributes prüfen
|
|
$attributes = $this->getConnectionAttributes($connection);
|
|
$additionalData['connection_attributes'] = $attributes;
|
|
|
|
$responseTime = (microtime(true) - $startTime) * 1000;
|
|
|
|
return HealthCheckResult::healthy(
|
|
$responseTime,
|
|
'Detailed health check passed',
|
|
$additionalData
|
|
);
|
|
|
|
} catch (\Throwable $e) {
|
|
$responseTime = (microtime(true) - $startTime) * 1000;
|
|
|
|
return HealthCheckResult::error($e, $responseTime)->withAdditionalData('partial_data', $additionalData);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Prüft ob die Connection noch lebendig ist (schneller Check)
|
|
*/
|
|
public function checkConnectionAlive(ConnectionInterface $connection): bool
|
|
{
|
|
try {
|
|
$pdo = $connection->getPdo();
|
|
|
|
// Prüfe ob die PDO-Verbindung noch aktiv ist
|
|
$stmt = $pdo->query('SELECT 1');
|
|
|
|
return $stmt !== false;
|
|
|
|
} catch (\Throwable) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Prüft den PDO-Status
|
|
*/
|
|
private function checkPdoStatus(ConnectionInterface $connection): array
|
|
{
|
|
try {
|
|
$pdo = $connection->getPdo();
|
|
|
|
return [
|
|
'connection_status' => $pdo->getAttribute(\PDO::ATTR_CONNECTION_STATUS),
|
|
'server_info' => $pdo->getAttribute(\PDO::ATTR_SERVER_INFO),
|
|
'driver_name' => $pdo->getAttribute(\PDO::ATTR_DRIVER_NAME),
|
|
'client_version' => $pdo->getAttribute(\PDO::ATTR_CLIENT_VERSION),
|
|
'server_version' => $pdo->getAttribute(\PDO::ATTR_SERVER_VERSION),
|
|
'in_transaction' => $connection->inTransaction(),
|
|
];
|
|
} catch (\Throwable $e) {
|
|
return ['error' => $e->getMessage()];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Führt benutzerdefinierte Queries aus
|
|
*/
|
|
private function executeCustomQueries(ConnectionInterface $connection): array
|
|
{
|
|
$results = [];
|
|
|
|
foreach ($this->customQueries as $name => $query) {
|
|
try {
|
|
$startTime = microtime(true);
|
|
$result = $connection->queryScalar($query);
|
|
$responseTime = (microtime(true) - $startTime) * 1000;
|
|
|
|
$results[$name] = [
|
|
'success' => true,
|
|
'result' => $result,
|
|
'response_time_ms' => round($responseTime, 2),
|
|
];
|
|
} catch (\Throwable $e) {
|
|
$results[$name] = [
|
|
'success' => false,
|
|
'error' => $e->getMessage(),
|
|
'response_time_ms' => 0,
|
|
];
|
|
}
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Sammelt Connection-Attribute
|
|
*/
|
|
private function getConnectionAttributes(ConnectionInterface $connection): array
|
|
{
|
|
try {
|
|
$pdo = $connection->getPdo();
|
|
|
|
$attributes = [];
|
|
$attributesToCheck = [
|
|
'AUTOCOMMIT' => \PDO::ATTR_AUTOCOMMIT,
|
|
'ERRMODE' => \PDO::ATTR_ERRMODE,
|
|
'CASE' => \PDO::ATTR_CASE,
|
|
'NULL_TO_STRING' => \PDO::ATTR_NULL_TO_STRING,
|
|
'STRINGIFY_FETCHES' => \PDO::ATTR_STRINGIFY_FETCHES,
|
|
'STATEMENT_CLASS' => \PDO::ATTR_STATEMENT_CLASS,
|
|
'TIMEOUT' => \PDO::ATTR_TIMEOUT,
|
|
'EMULATE_PREPARES' => \PDO::ATTR_EMULATE_PREPARES,
|
|
'DEFAULT_FETCH_MODE' => \PDO::ATTR_DEFAULT_FETCH_MODE,
|
|
];
|
|
|
|
foreach ($attributesToCheck as $name => $constant) {
|
|
try {
|
|
$attributes[$name] = $pdo->getAttribute($constant);
|
|
} catch (\Throwable) {
|
|
$attributes[$name] = 'Not supported';
|
|
}
|
|
}
|
|
|
|
return $attributes;
|
|
} catch (\Throwable $e) {
|
|
return ['error' => $e->getMessage()];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Factory-Methoden für verschiedene Health Check-Konfigurationen
|
|
*/
|
|
public static function quick(?Timer $timer = null, int $timeoutSeconds = 2): self
|
|
{
|
|
return new self($timer ?? new SystemTimer(), $timeoutSeconds);
|
|
}
|
|
|
|
public static function detailed(?Timer $timer = null, int $timeoutSeconds = 10, array $customQueries = []): self
|
|
{
|
|
return new self($timer ?? new SystemTimer(), $timeoutSeconds, $customQueries);
|
|
}
|
|
|
|
public static function withCustomQueries(array $customQueries, ?Timer $timer = null, int $timeoutSeconds = 5): self
|
|
{
|
|
return new self($timer ?? new SystemTimer(), $timeoutSeconds, $customQueries);
|
|
}
|
|
}
|