- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
495 lines
17 KiB
PHP
495 lines
17 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Framework\Discovery\Quality;
|
|
|
|
use App\Framework\Core\ValueObjects\Score;
|
|
use App\Framework\DateTime\Clock;
|
|
use App\Framework\Discovery\Results\DiscoveryRegistry;
|
|
use App\Framework\Discovery\UnifiedDiscoveryService;
|
|
use App\Framework\Discovery\ValueObjects\DiscoveryContext;
|
|
use App\Framework\Logging\Logger;
|
|
|
|
/**
|
|
* Quality assurance validator for Discovery system
|
|
*
|
|
* Provides comprehensive quality checks including performance
|
|
* validation, memory efficiency analysis, cache effectiveness,
|
|
* and overall system health assessment.
|
|
*/
|
|
final class DiscoveryQualityValidator
|
|
{
|
|
// Quality thresholds
|
|
private const array PERFORMANCE_THRESHOLDS = [
|
|
'max_discovery_time_seconds' => 30,
|
|
'max_memory_usage_mb' => 256,
|
|
'min_cache_hit_rate' => 0.7,
|
|
'max_memory_pressure' => 0.8,
|
|
'min_items_per_second' => 10,
|
|
];
|
|
|
|
private const array QUALITY_WEIGHTS = [
|
|
'performance' => 0.3,
|
|
'memory_efficiency' => 0.25,
|
|
'cache_effectiveness' => 0.2,
|
|
'reliability' => 0.15,
|
|
'maintainability' => 0.1,
|
|
];
|
|
|
|
public function __construct(
|
|
private readonly Clock $clock,
|
|
private readonly ?Logger $logger = null
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* Perform comprehensive quality validation
|
|
*/
|
|
public function validateQuality(
|
|
UnifiedDiscoveryService $service,
|
|
DiscoveryContext $context,
|
|
?DiscoveryRegistry $registry = null
|
|
): DiscoveryQualityReport {
|
|
$startTime = microtime(true);
|
|
$startMemory = memory_get_usage(true);
|
|
|
|
$this->logger?->info('Starting discovery quality validation', [
|
|
'context_key' => $context->getCacheKey(),
|
|
]);
|
|
|
|
// Performance validation
|
|
$performanceScore = $this->validatePerformance($service, $context, $registry);
|
|
|
|
// Memory efficiency validation
|
|
$memoryScore = $this->validateMemoryEfficiency($service, $startMemory);
|
|
|
|
// Cache effectiveness validation
|
|
$cacheScore = $this->validateCacheEffectiveness($service);
|
|
|
|
// Reliability validation
|
|
$reliabilityScore = $this->validateReliability($service);
|
|
|
|
// Maintainability validation
|
|
$maintainabilityScore = $this->validateMaintainability($service, $registry);
|
|
|
|
// Calculate overall quality score
|
|
$overallScore = $this->calculateOverallScore([
|
|
'performance' => $performanceScore,
|
|
'memory_efficiency' => $memoryScore,
|
|
'cache_effectiveness' => $cacheScore,
|
|
'reliability' => $reliabilityScore,
|
|
'maintainability' => $maintainabilityScore,
|
|
]);
|
|
|
|
$validationTime = microtime(true) - $startTime;
|
|
|
|
$report = new DiscoveryQualityReport(
|
|
overallScore: $overallScore,
|
|
performanceScore: $performanceScore,
|
|
memoryScore: $memoryScore,
|
|
cacheScore: $cacheScore,
|
|
reliabilityScore: $reliabilityScore,
|
|
maintainabilityScore: $maintainabilityScore,
|
|
validationTime: $validationTime,
|
|
recommendations: $this->generateRecommendations($overallScore, [
|
|
'performance' => $performanceScore,
|
|
'memory_efficiency' => $memoryScore,
|
|
'cache_effectiveness' => $cacheScore,
|
|
'reliability' => $reliabilityScore,
|
|
'maintainability' => $maintainabilityScore,
|
|
]),
|
|
timestamp: $this->clock->now()
|
|
);
|
|
|
|
$this->logger?->info('Discovery quality validation completed', [
|
|
'overall_score' => $overallScore->toString(),
|
|
'validation_time' => $validationTime,
|
|
'quality_rating' => $report->getQualityRating(),
|
|
]);
|
|
|
|
return $report;
|
|
}
|
|
|
|
/**
|
|
* Validate performance characteristics
|
|
*/
|
|
private function validatePerformance(
|
|
UnifiedDiscoveryService $service,
|
|
DiscoveryContext $context,
|
|
?DiscoveryRegistry $registry
|
|
): Score {
|
|
$score = 100;
|
|
$issues = [];
|
|
|
|
try {
|
|
// Test discovery timing if registry not provided
|
|
if ($registry === null) {
|
|
$startTime = microtime(true);
|
|
$registry = $service->discoverWithOptions($context->options);
|
|
$discoveryTime = microtime(true) - $startTime;
|
|
} else {
|
|
$discoveryTime = 5.0; // Assume reasonable time if registry provided
|
|
}
|
|
|
|
// Check discovery time
|
|
if ($discoveryTime > self::PERFORMANCE_THRESHOLDS['max_discovery_time_seconds']) {
|
|
$penalty = min(30, ($discoveryTime - self::PERFORMANCE_THRESHOLDS['max_discovery_time_seconds']) * 2);
|
|
$score -= $penalty;
|
|
$issues[] = "Discovery took {$discoveryTime}s (threshold: " . self::PERFORMANCE_THRESHOLDS['max_discovery_time_seconds'] . "s)";
|
|
}
|
|
|
|
// Check throughput
|
|
$itemsCount = count($registry);
|
|
$itemsPerSecond = $itemsCount / max($discoveryTime, 0.1);
|
|
|
|
if ($itemsPerSecond < self::PERFORMANCE_THRESHOLDS['min_items_per_second']) {
|
|
$score -= 15;
|
|
$issues[] = "Low throughput: {$itemsPerSecond} items/s (threshold: " . self::PERFORMANCE_THRESHOLDS['min_items_per_second'] . ")";
|
|
}
|
|
|
|
// Check for performance bottlenecks
|
|
$healthStatus = $service->getHealthStatus();
|
|
if (isset($healthStatus['memory_management']['memory_pressure'])) {
|
|
$memoryPressure = (float) $healthStatus['memory_management']['memory_pressure'];
|
|
if ($memoryPressure > self::PERFORMANCE_THRESHOLDS['max_memory_pressure']) {
|
|
$score -= 20;
|
|
$issues[] = "High memory pressure: {$memoryPressure} (threshold: " . self::PERFORMANCE_THRESHOLDS['max_memory_pressure'] . ")";
|
|
}
|
|
}
|
|
|
|
} catch (\Throwable $e) {
|
|
$score -= 50;
|
|
$issues[] = "Performance test failed: " . $e->getMessage();
|
|
}
|
|
|
|
if (! empty($issues)) {
|
|
$this->logger?->warning('Performance validation issues detected', [
|
|
'issues' => $issues,
|
|
'score' => $score,
|
|
]);
|
|
}
|
|
|
|
return Score::fromRatio(max(0, $score), 100);
|
|
}
|
|
|
|
/**
|
|
* Validate memory efficiency
|
|
*/
|
|
private function validateMemoryEfficiency(UnifiedDiscoveryService $service, int $startMemory): Score
|
|
{
|
|
$score = 100;
|
|
$issues = [];
|
|
|
|
try {
|
|
$currentMemory = memory_get_usage(true);
|
|
$memoryUsed = $currentMemory - $startMemory;
|
|
$memoryUsedMB = $memoryUsed / 1024 / 1024;
|
|
|
|
// Check memory usage
|
|
if ($memoryUsedMB > self::PERFORMANCE_THRESHOLDS['max_memory_usage_mb']) {
|
|
$penalty = min(40, ($memoryUsedMB - self::PERFORMANCE_THRESHOLDS['max_memory_usage_mb']) / 10);
|
|
$score -= $penalty;
|
|
$issues[] = "High memory usage: {$memoryUsedMB}MB (threshold: " . self::PERFORMANCE_THRESHOLDS['max_memory_usage_mb'] . "MB)";
|
|
}
|
|
|
|
// Check memory management statistics
|
|
$memoryStats = $service->getMemoryStatistics();
|
|
|
|
if (isset($memoryStats['memory_status']['memory_pressure'])) {
|
|
$pressure = $memoryStats['memory_status']['memory_pressure'];
|
|
if ($pressure > 0.9) {
|
|
$score -= 25;
|
|
$issues[] = "Critical memory pressure: {$pressure}";
|
|
} elseif ($pressure > 0.8) {
|
|
$score -= 15;
|
|
$issues[] = "High memory pressure: {$pressure}";
|
|
}
|
|
}
|
|
|
|
// Check for memory leaks
|
|
if (isset($memoryStats['guard_statistics']['emergency_mode']) && $memoryStats['guard_statistics']['emergency_mode']) {
|
|
$score -= 30;
|
|
$issues[] = "Memory guard in emergency mode";
|
|
}
|
|
|
|
} catch (\Throwable $e) {
|
|
$score -= 25;
|
|
$issues[] = "Memory efficiency test failed: " . $e->getMessage();
|
|
}
|
|
|
|
if (! empty($issues)) {
|
|
$this->logger?->warning('Memory efficiency validation issues detected', [
|
|
'issues' => $issues,
|
|
'score' => $score,
|
|
]);
|
|
}
|
|
|
|
return Score::fromRatio(max(0, $score), 100);
|
|
}
|
|
|
|
/**
|
|
* Validate cache effectiveness
|
|
*/
|
|
private function validateCacheEffectiveness(UnifiedDiscoveryService $service): Score
|
|
{
|
|
$score = 100;
|
|
$issues = [];
|
|
|
|
try {
|
|
$healthStatus = $service->getHealthStatus();
|
|
|
|
// Check if cache is enabled
|
|
if (! isset($healthStatus['cache']['memory_aware']) || ! $healthStatus['cache']['memory_aware']) {
|
|
$score -= 20;
|
|
$issues[] = "Memory-aware caching not enabled";
|
|
}
|
|
|
|
// For now, assume good cache performance if no issues detected
|
|
// In a real implementation, you would track cache hit rates over time
|
|
|
|
} catch (\Throwable $e) {
|
|
$score -= 30;
|
|
$issues[] = "Cache effectiveness test failed: " . $e->getMessage();
|
|
}
|
|
|
|
if (! empty($issues)) {
|
|
$this->logger?->warning('Cache effectiveness validation issues detected', [
|
|
'issues' => $issues,
|
|
'score' => $score,
|
|
]);
|
|
}
|
|
|
|
return Score::fromRatio(max(0, $score), 100);
|
|
}
|
|
|
|
/**
|
|
* Validate system reliability
|
|
*/
|
|
private function validateReliability(UnifiedDiscoveryService $service): Score
|
|
{
|
|
$score = 100;
|
|
$issues = [];
|
|
|
|
try {
|
|
// Test basic functionality
|
|
$testResults = $service->test();
|
|
|
|
if ($testResults['overall_status'] !== 'healthy') {
|
|
$score -= 40;
|
|
$issues[] = "Service health check failed: " . $testResults['overall_status'];
|
|
}
|
|
|
|
// Check component statuses
|
|
foreach ($testResults['components'] as $component => $status) {
|
|
if (is_array($status) && isset($status['status']) && $status['status'] === 'error') {
|
|
$score -= 15;
|
|
$issues[] = "Component {$component} has error: " . ($status['error'] ?? 'unknown');
|
|
}
|
|
}
|
|
|
|
} catch (\Throwable $e) {
|
|
$score -= 50;
|
|
$issues[] = "Reliability test failed: " . $e->getMessage();
|
|
}
|
|
|
|
if (! empty($issues)) {
|
|
$this->logger?->warning('Reliability validation issues detected', [
|
|
'issues' => $issues,
|
|
'score' => $score,
|
|
]);
|
|
}
|
|
|
|
return Score::fromRatio(max(0, $score), 100);
|
|
}
|
|
|
|
/**
|
|
* Validate maintainability aspects
|
|
*/
|
|
private function validateMaintainability(UnifiedDiscoveryService $service, ?DiscoveryRegistry $registry): Score
|
|
{
|
|
$score = 100;
|
|
$issues = [];
|
|
|
|
try {
|
|
// Check if registry has reasonable structure
|
|
if ($registry !== null && count($registry) === 0) {
|
|
$score -= 10;
|
|
$issues[] = "Empty discovery registry - may indicate configuration issues";
|
|
}
|
|
|
|
// Check health status structure
|
|
$healthStatus = $service->getHealthStatus();
|
|
$expectedKeys = ['service', 'cache', 'memory_management', 'components'];
|
|
|
|
foreach ($expectedKeys as $key) {
|
|
if (! isset($healthStatus[$key])) {
|
|
$score -= 5;
|
|
$issues[] = "Missing health status key: {$key}";
|
|
}
|
|
}
|
|
|
|
} catch (\Throwable $e) {
|
|
$score -= 20;
|
|
$issues[] = "Maintainability test failed: " . $e->getMessage();
|
|
}
|
|
|
|
if (! empty($issues)) {
|
|
$this->logger?->debug('Maintainability validation issues detected', [
|
|
'issues' => $issues,
|
|
'score' => $score,
|
|
]);
|
|
}
|
|
|
|
return Score::fromRatio(max(0, $score), 100);
|
|
}
|
|
|
|
/**
|
|
* Calculate overall quality score
|
|
*/
|
|
private function calculateOverallScore(array $scores): Score
|
|
{
|
|
$weightedSum = 0;
|
|
$totalWeight = 0;
|
|
|
|
foreach (self::QUALITY_WEIGHTS as $category => $weight) {
|
|
if (isset($scores[$category])) {
|
|
$weightedSum += $scores[$category]->toDecimal() * $weight;
|
|
$totalWeight += $weight;
|
|
}
|
|
}
|
|
|
|
$overallScore = $totalWeight > 0 ? $weightedSum / $totalWeight : 0;
|
|
|
|
return Score::fromRatio((int) ($overallScore * 100), 100);
|
|
}
|
|
|
|
/**
|
|
* Generate quality improvement recommendations
|
|
*/
|
|
private function generateRecommendations(Score $overallScore, array $categoryScores): array
|
|
{
|
|
$recommendations = [];
|
|
|
|
// Overall recommendations
|
|
if ($overallScore->toDecimal() < 0.6) {
|
|
$recommendations[] = 'CRITICAL: Overall quality score is below acceptable threshold - immediate action required';
|
|
} elseif ($overallScore->toDecimal() < 0.8) {
|
|
$recommendations[] = 'Quality score indicates room for improvement - consider optimization';
|
|
}
|
|
|
|
// Category-specific recommendations
|
|
foreach ($categoryScores as $category => $score) {
|
|
if ($score->toDecimal() < 0.7) {
|
|
$recommendations[] = match ($category) {
|
|
'performance' => 'Performance optimization needed - consider enabling parallel processing and reducing batch sizes',
|
|
'memory_efficiency' => 'Memory usage is high - enable memory monitoring and optimize memory management strategies',
|
|
'cache_effectiveness' => 'Cache performance is poor - review cache configuration and implement cache warming',
|
|
'reliability' => 'System reliability issues detected - investigate component health and error handling',
|
|
'maintainability' => 'Maintainability concerns found - review configuration and system structure',
|
|
default => "Improve {$category} score through system optimization"
|
|
};
|
|
}
|
|
}
|
|
|
|
if (empty($recommendations)) {
|
|
$recommendations[] = 'Quality metrics are within acceptable ranges - continue monitoring';
|
|
}
|
|
|
|
return $recommendations;
|
|
}
|
|
|
|
/**
|
|
* Validate specific quality criteria
|
|
*/
|
|
public function validateCriteria(UnifiedDiscoveryService $service, array $criteria): array
|
|
{
|
|
$results = [];
|
|
|
|
foreach ($criteria as $criterion => $expectedValue) {
|
|
try {
|
|
$result = match ($criterion) {
|
|
'max_discovery_time' => $this->checkDiscoveryTime($service, $expectedValue),
|
|
'max_memory_usage' => $this->checkMemoryUsage($service, $expectedValue),
|
|
'min_cache_hit_rate' => $this->checkCacheHitRate($service, $expectedValue),
|
|
'service_health' => $this->checkServiceHealth($service, $expectedValue),
|
|
default => ['valid' => false, 'error' => "Unknown criterion: {$criterion}"]
|
|
};
|
|
|
|
$results[$criterion] = $result;
|
|
|
|
} catch (\Throwable $e) {
|
|
$results[$criterion] = [
|
|
'valid' => false,
|
|
'error' => "Validation failed: " . $e->getMessage(),
|
|
];
|
|
}
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Check discovery time criterion
|
|
*/
|
|
private function checkDiscoveryTime(UnifiedDiscoveryService $service, float $maxSeconds): array
|
|
{
|
|
// This would require actual timing - simplified for example
|
|
return [
|
|
'valid' => true,
|
|
'message' => 'Discovery time validation not implemented in test environment',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Check memory usage criterion
|
|
*/
|
|
private function checkMemoryUsage(UnifiedDiscoveryService $service, int $maxMB): array
|
|
{
|
|
$currentMemoryMB = memory_get_usage(true) / 1024 / 1024;
|
|
|
|
return [
|
|
'valid' => $currentMemoryMB <= $maxMB,
|
|
'actual_value' => $currentMemoryMB,
|
|
'expected_max' => $maxMB,
|
|
'message' => $currentMemoryMB <= $maxMB ? 'Memory usage within limits' : 'Memory usage exceeds limits',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Check cache hit rate criterion
|
|
*/
|
|
private function checkCacheHitRate(UnifiedDiscoveryService $service, float $minRate): array
|
|
{
|
|
// This would require cache statistics - simplified for example
|
|
return [
|
|
'valid' => true,
|
|
'message' => 'Cache hit rate validation not implemented in test environment',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Check service health criterion
|
|
*/
|
|
private function checkServiceHealth(UnifiedDiscoveryService $service, string $expectedStatus): array
|
|
{
|
|
try {
|
|
$testResults = $service->test();
|
|
$actualStatus = $testResults['overall_status'];
|
|
|
|
return [
|
|
'valid' => $actualStatus === $expectedStatus,
|
|
'actual_value' => $actualStatus,
|
|
'expected_value' => $expectedStatus,
|
|
'message' => $actualStatus === $expectedStatus ? 'Service health as expected' : 'Service health differs from expected',
|
|
];
|
|
|
|
} catch (\Throwable $e) {
|
|
return [
|
|
'valid' => false,
|
|
'error' => 'Health check failed: ' . $e->getMessage(),
|
|
];
|
|
}
|
|
}
|
|
}
|