metricsManager->getPerformanceStats( queueName: $currentMetrics->queueName, timeWindow: '24 hours' ); return new JobFeatures( executionTimeVariance: $this->calculateExecutionTimeVariance($currentMetrics, $historicalStats), memoryUsagePattern: $this->calculateMemoryUsagePattern($currentMetrics, $historicalStats), retryFrequency: $this->calculateRetryFrequency($currentMetrics), failureRate: $this->calculateFailureRate($historicalStats), queueDepthCorrelation: $this->calculateQueueDepthCorrelation($queueDepth), dependencyChainComplexity: $this->calculateDependencyComplexity($metadata), payloadSizeAnomaly: $this->calculatePayloadSizeAnomaly($metadata, $historicalStats), executionTimingRegularity: $this->calculateExecutionTimingRegularity($currentMetrics) ); } /** * Calculate execution time variance (0.0-1.0) * * Measures how much the current execution time deviates from the average. * High variance indicates unstable performance. */ private function calculateExecutionTimeVariance( JobMetrics $metrics, array $historicalStats ): float { $avgExecutionTime = $historicalStats['average_execution_time_ms'] ?? 0; if ($avgExecutionTime <= 0) { return 0.0; // No historical data yet } $currentExecutionTime = $metrics->executionTimeMs; $deviation = abs($currentExecutionTime - $avgExecutionTime) / $avgExecutionTime; // Normalize: 0 = exactly average, 1.0 = 10x or more deviation return min(1.0, $deviation / 10.0); } /** * Calculate memory usage pattern (0.0-1.0) * * Measures memory usage anomaly compared to historical average. */ private function calculateMemoryUsagePattern( JobMetrics $metrics, array $historicalStats ): float { $avgMemoryUsage = $historicalStats['average_memory_usage_bytes'] ?? 0; if ($avgMemoryUsage <= 0) { return 0.0; // No historical data yet } $currentMemoryUsage = $metrics->memoryUsageBytes; $deviation = abs($currentMemoryUsage - $avgMemoryUsage) / $avgMemoryUsage; // Normalize: 0 = average usage, 1.0 = 5x or more deviation return min(1.0, $deviation / 5.0); } /** * Calculate retry frequency (0.0-1.0) * * Normalized retry count: 0 = no retries, 1.0 = max attempts exhausted */ private function calculateRetryFrequency(JobMetrics $metrics): float { if ($metrics->maxAttempts <= 1) { return 0.0; // No retry configuration } return min(1.0, $metrics->attempts / $metrics->maxAttempts); } /** * Calculate failure rate (0.0-1.0) * * Percentage of failed jobs for this queue over time window. */ private function calculateFailureRate(array $historicalStats): float { $totalJobs = $historicalStats['total_jobs'] ?? 0; if ($totalJobs === 0) { return 0.0; } $failedJobs = $historicalStats['failed_jobs'] ?? 0; return min(1.0, $failedJobs / $totalJobs); } /** * Calculate queue depth correlation (0.0-1.0) * * Impact of queue depth on performance. * High values indicate system is overloaded. */ private function calculateQueueDepthCorrelation(int $queueDepth): float { // Normalize queue depth: 0 = empty, 1.0 = 1000+ jobs queued return min(1.0, $queueDepth / 1000.0); } /** * Calculate dependency chain complexity (0.0-1.0) * * Currently a placeholder - would analyze job dependency graph. * For now, use job tags to estimate complexity. */ private function calculateDependencyComplexity(JobMetadata $metadata): float { $tagCount = count($metadata->tags); // Simple heuristic: more tags = more complex job return min(1.0, $tagCount / 10.0); } /** * Calculate payload size anomaly (0.0-1.0) * * Deviation from typical payload size for this job type. * Currently estimates from metadata extra fields. */ private function calculatePayloadSizeAnomaly( JobMetadata $metadata, array $historicalStats ): float { $extraFieldCount = count($metadata->extra); // Simple heuristic: more extra fields = larger payload // Normalize: 0 = typical, 1.0 = 50+ extra fields return min(1.0, $extraFieldCount / 50.0); } /** * Calculate execution timing regularity (0.0-1.0) * * Measures consistency of execution intervals. * High regularity (near 1.0) can indicate bot-like behavior. */ private function calculateExecutionTimingRegularity(JobMetrics $metrics): float { // For now, use job type consistency as proxy // In a full implementation, would analyze inter-arrival times // If job has metadata indicating scheduled execution, mark as regular $metadata = $metrics->metadata ?? []; if (isset($metadata['scheduled']) && $metadata['scheduled']) { return 0.9; // Scheduled jobs are highly regular (expected) } // Default: moderate regularity for queue jobs return 0.3; } /** * Extract features from job metrics history for batch analysis * * @param string $jobId Job ID to analyze * @return JobFeatures[] Array of feature vectors over time */ public function extractHistoricalFeatures(string $jobId): array { $metricsHistory = $this->metricsManager->getJobMetricsHistory($jobId); $features = []; foreach ($metricsHistory as $metrics) { // Create minimal metadata from metrics $metadata = new JobMetadata( id: new \App\Framework\Ulid\Ulid(new \App\Framework\DateTime\SystemClock()), class: \App\Framework\Core\ValueObjects\ClassName::create($metrics->queueName), type: 'job', queuedAt: \App\Framework\Core\ValueObjects\Timestamp::now(), tags: [], extra: $metrics->metadata ?? [] ); $features[] = $this->extractFeatures($metrics, $metadata, 0); } return $features; } /** * Extract features for all recent jobs in a queue * * @param string $queueName Queue to analyze * @param int $limit Maximum number of jobs to analyze * @return array Array of [JobMetrics, JobFeatures] tuples */ public function extractQueueFeatures(string $queueName, int $limit = 100): array { // Get recent job metrics for this queue $historicalStats = $this->metricsManager->getPerformanceStats($queueName, '1 hour'); // This would need a method to get recent jobs - placeholder for now // In a full implementation, would query job_metrics table for recent jobs return []; } }