Files
michaelschiemer/src/Framework/Queue/ValueObjects/QueueMetrics.php
Michael Schiemer 5050c7d73a docs: consolidate documentation into organized structure
- Move 12 markdown files from root to docs/ subdirectories
- Organize documentation by category:
  • docs/troubleshooting/ (1 file)  - Technical troubleshooting guides
  • docs/deployment/      (4 files) - Deployment and security documentation
  • docs/guides/          (3 files) - Feature-specific guides
  • docs/planning/        (4 files) - Planning and improvement proposals

Root directory cleanup:
- Reduced from 16 to 4 markdown files in root
- Only essential project files remain:
  • CLAUDE.md (AI instructions)
  • README.md (Main project readme)
  • CLEANUP_PLAN.md (Current cleanup plan)
  • SRC_STRUCTURE_IMPROVEMENTS.md (Structure improvements)

This improves:
 Documentation discoverability
 Logical organization by purpose
 Clean root directory
 Better maintainability
2025-10-05 11:05:04 +02:00

218 lines
7.3 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Framework\Queue\ValueObjects;
use App\Framework\Core\ValueObjects\Percentage;
final readonly class QueueMetrics
{
public function __construct(
public string $queueName,
public int $totalJobs,
public int $pendingJobs,
public int $runningJobs,
public int $completedJobs,
public int $failedJobs,
public int $deadLetterJobs,
public float $averageExecutionTimeMs,
public float $averageMemoryUsageMB,
public float $throughputJobsPerHour,
public Percentage $successRate,
public string $measuredAt,
public array $additionalMetrics = []
) {}
public static function calculate(
string $queueName,
array $jobMetrics,
string $timeWindow = '1 hour'
): self {
$total = count($jobMetrics);
$pending = 0;
$running = 0;
$completed = 0;
$failed = 0;
$deadLetter = 0;
$totalExecutionTime = 0.0;
$totalMemoryUsage = 0.0;
$executionCount = 0;
$windowStart = strtotime("-{$timeWindow}");
$jobsInWindow = 0;
foreach ($jobMetrics as $metrics) {
match($metrics->status) {
'pending' => $pending++,
'running' => $running++,
'completed' => $completed++,
'failed' => $failed++,
'dead_letter' => $deadLetter++,
default => null
};
if ($metrics->executionTimeMs > 0) {
$totalExecutionTime += $metrics->executionTimeMs;
$totalMemoryUsage += $metrics->getMemoryUsageMB();
$executionCount++;
}
if (strtotime($metrics->createdAt) >= $windowStart) {
$jobsInWindow++;
}
}
$avgExecutionTime = $executionCount > 0 ? $totalExecutionTime / $executionCount : 0.0;
$avgMemoryUsage = $executionCount > 0 ? $totalMemoryUsage / $executionCount : 0.0;
$throughput = $jobsInWindow / (strtotime($timeWindow) / 3600);
$successRate = $total > 0 ?
Percentage::from(($completed / $total) * 100) :
Percentage::from(100.0);
return new self(
queueName: $queueName,
totalJobs: $total,
pendingJobs: $pending,
runningJobs: $running,
completedJobs: $completed,
failedJobs: $failed,
deadLetterJobs: $deadLetter,
averageExecutionTimeMs: $avgExecutionTime,
averageMemoryUsageMB: $avgMemoryUsage,
throughputJobsPerHour: $throughput,
successRate: $successRate,
measuredAt: date('Y-m-d H:i:s'),
additionalMetrics: []
);
}
public function withAdditionalMetrics(array $metrics): self
{
return new self(
queueName: $this->queueName,
totalJobs: $this->totalJobs,
pendingJobs: $this->pendingJobs,
runningJobs: $this->runningJobs,
completedJobs: $this->completedJobs,
failedJobs: $this->failedJobs,
deadLetterJobs: $this->deadLetterJobs,
averageExecutionTimeMs: $this->averageExecutionTimeMs,
averageMemoryUsageMB: $this->averageMemoryUsageMB,
throughputJobsPerHour: $this->throughputJobsPerHour,
successRate: $this->successRate,
measuredAt: $this->measuredAt,
additionalMetrics: array_merge($this->additionalMetrics, $metrics)
);
}
public function getFailureRate(): Percentage
{
if ($this->totalJobs === 0) {
return Percentage::from(0.0);
}
return Percentage::from(($this->failedJobs / $this->totalJobs) * 100);
}
public function getAverageExecutionTimeSeconds(): float
{
return $this->averageExecutionTimeMs / 1000.0;
}
public function getHealthScore(): Percentage
{
// Composite health score based on multiple factors
$successWeight = 40; // Success rate weight
$throughputWeight = 30; // Throughput weight
$performanceWeight = 20; // Performance weight
$stabilityWeight = 10; // Stability weight
// Success rate score (0-100)
$successScore = $this->successRate->getValue();
// Throughput score (normalized, assuming 100 jobs/hour is excellent)
$throughputScore = min(100, ($this->throughputJobsPerHour / 100) * 100);
// Performance score (inverse of execution time, assuming 1000ms is baseline)
$performanceScore = $this->averageExecutionTimeMs > 0 ?
max(0, 100 - (($this->averageExecutionTimeMs / 1000) * 10)) : 100;
// Stability score (low pending/running job ratio)
$activeJobs = $this->pendingJobs + $this->runningJobs;
$stabilityScore = $this->totalJobs > 0 ?
max(0, 100 - (($activeJobs / $this->totalJobs) * 100)) : 100;
$weightedScore = (
($successScore * $successWeight) +
($throughputScore * $throughputWeight) +
($performanceScore * $performanceWeight) +
($stabilityScore * $stabilityWeight)
) / 100;
return Percentage::from($weightedScore);
}
public function isHealthy(): bool
{
return $this->getHealthScore()->getValue() >= 70.0;
}
public function getBottleneckIndicators(): array
{
$indicators = [];
// High pending jobs
if ($this->pendingJobs > ($this->totalJobs * 0.3)) {
$indicators[] = 'high_pending_jobs';
}
// High failure rate
if ($this->getFailureRate()->getValue() > 10.0) {
$indicators[] = 'high_failure_rate';
}
// Slow execution
if ($this->averageExecutionTimeMs > 5000) {
$indicators[] = 'slow_execution';
}
// High memory usage
if ($this->averageMemoryUsageMB > 100) {
$indicators[] = 'high_memory_usage';
}
// Low throughput
if ($this->throughputJobsPerHour < 10) {
$indicators[] = 'low_throughput';
}
return $indicators;
}
public function toArray(): array
{
return [
'queue_name' => $this->queueName,
'total_jobs' => $this->totalJobs,
'pending_jobs' => $this->pendingJobs,
'running_jobs' => $this->runningJobs,
'completed_jobs' => $this->completedJobs,
'failed_jobs' => $this->failedJobs,
'dead_letter_jobs' => $this->deadLetterJobs,
'average_execution_time_ms' => $this->averageExecutionTimeMs,
'average_execution_time_seconds' => $this->getAverageExecutionTimeSeconds(),
'average_memory_usage_mb' => $this->averageMemoryUsageMB,
'throughput_jobs_per_hour' => $this->throughputJobsPerHour,
'success_rate' => $this->successRate->getValue(),
'failure_rate' => $this->getFailureRate()->getValue(),
'health_score' => $this->getHealthScore()->getValue(),
'is_healthy' => $this->isHealthy(),
'bottleneck_indicators' => $this->getBottleneckIndicators(),
'measured_at' => $this->measuredAt,
'additional_metrics' => $this->additionalMetrics
];
}
}