- Create AnsibleDeployStage using framework's Process module for secure command execution - Integrate AnsibleDeployStage into DeploymentPipelineCommands for production deployments - Add force_deploy flag support in Ansible playbook to override stale locks - Use PHP deployment module as orchestrator (php console.php deploy:production) - Fix ErrorAggregationInitializer to use Environment class instead of $_ENV superglobal Architecture: - BuildStage → AnsibleDeployStage → HealthCheckStage for production - Process module provides timeout, error handling, and output capture - Ansible playbook supports rollback via rollback-git-based.yml - Zero-downtime deployments with health checks
262 lines
10 KiB
PHP
262 lines
10 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* Integration Test: Queue Anomaly Detection System
|
|
*
|
|
* Tests the full integration of:
|
|
* - QueueJobFeatureExtractor
|
|
* - JobAnomalyDetector
|
|
* - QueueAnomalyMonitor
|
|
* - Event dispatching
|
|
*/
|
|
|
|
require_once __DIR__ . '/../../vendor/autoload.php';
|
|
|
|
use App\Framework\Core\AppBootstrapper;
|
|
use App\Framework\Queue\MachineLearning\JobAnomalyDetector;
|
|
use App\Framework\Queue\MachineLearning\QueueJobFeatureExtractor;
|
|
use App\Framework\Queue\MachineLearning\QueueAnomalyMonitor;
|
|
use App\Framework\Queue\Services\JobMetricsManager;
|
|
use App\Framework\Queue\ValueObjects\JobMetrics;
|
|
use App\Framework\Queue\ValueObjects\JobMetadata;
|
|
use App\Framework\Core\ValueObjects\ClassName;
|
|
use App\Framework\Core\ValueObjects\Timestamp;
|
|
use App\Framework\Core\ValueObjects\Score;
|
|
use App\Framework\Ulid\Ulid;
|
|
use App\Framework\DateTime\SystemClock;
|
|
|
|
echo "=== Queue Anomaly Detection Integration Test ===\n\n";
|
|
|
|
try {
|
|
// Bootstrap framework
|
|
echo "1. Bootstrapping framework...\n";
|
|
$basePath = dirname(__DIR__, 2);
|
|
|
|
$clock = new \App\Framework\DateTime\SystemClock();
|
|
$highResClock = new \App\Framework\DateTime\SystemHighResolutionClock();
|
|
$memoryMonitor = new \App\Framework\Performance\MemoryMonitor();
|
|
$collector = new \App\Framework\Performance\EnhancedPerformanceCollector(
|
|
$clock,
|
|
$highResClock,
|
|
$memoryMonitor,
|
|
enabled: false
|
|
);
|
|
|
|
$bootstrapper = new AppBootstrapper($basePath, $collector, $memoryMonitor);
|
|
$container = $bootstrapper->bootstrapWorker();
|
|
echo " ✓ Framework bootstrapped\n\n";
|
|
|
|
// Initialize components
|
|
echo "2. Initializing Queue Anomaly Detection Components...\n";
|
|
|
|
// Create detector with lower threshold for testing
|
|
$detector = new JobAnomalyDetector(
|
|
anomalyThreshold: new Score(0.4), // 40% threshold for testing
|
|
zScoreThreshold: 3.0,
|
|
iqrMultiplier: 1.5
|
|
);
|
|
echo " ✓ JobAnomalyDetector created (threshold: 40%)\n";
|
|
|
|
// Get JobMetricsManager from container
|
|
$metricsManager = $container->get(JobMetricsManager::class);
|
|
echo " ✓ JobMetricsManager retrieved\n";
|
|
|
|
// Create feature extractor
|
|
$featureExtractor = new QueueJobFeatureExtractor($metricsManager);
|
|
echo " ✓ QueueJobFeatureExtractor created\n";
|
|
|
|
// Create anomaly monitor
|
|
$logger = $container->get(\App\Framework\Logging\Logger::class);
|
|
$anomalyMonitor = new QueueAnomalyMonitor(
|
|
$detector,
|
|
$featureExtractor,
|
|
$metricsManager,
|
|
$logger
|
|
);
|
|
echo " ✓ QueueAnomalyMonitor created\n\n";
|
|
|
|
// Test Case 1: Normal Job Execution
|
|
echo "3. Test Case 1: Normal Job Execution\n";
|
|
$normalMetrics = new JobMetrics(
|
|
jobId: 'job-normal-001',
|
|
queueName: 'default',
|
|
status: 'completed',
|
|
attempts: 1,
|
|
maxAttempts: 3,
|
|
executionTimeMs: 150.0,
|
|
memoryUsageBytes: 10 * 1024 * 1024, // 10MB
|
|
errorMessage: null,
|
|
createdAt: date('Y-m-d H:i:s'),
|
|
startedAt: date('Y-m-d H:i:s'),
|
|
completedAt: date('Y-m-d H:i:s'),
|
|
failedAt: null,
|
|
metadata: ['scheduled' => false]
|
|
);
|
|
|
|
$normalMetadata = new JobMetadata(
|
|
id: new Ulid(new SystemClock()),
|
|
class: ClassName::create('NormalProcessingJob'),
|
|
type: 'job',
|
|
queuedAt: Timestamp::now(),
|
|
tags: ['normal'],
|
|
extra: []
|
|
);
|
|
|
|
$result1 = $anomalyMonitor->analyzeJobExecution($normalMetrics, $normalMetadata, 10);
|
|
echo " Result: " . ($result1->isAnomalous ? "🚨 ANOMALOUS" : "✓ NORMAL") . "\n";
|
|
echo " Confidence: " . sprintf("%.2f%%", $result1->anomalyScore->value() * 100) . "\n";
|
|
echo " Severity: {$result1->getSeverity()}\n\n";
|
|
|
|
// Test Case 2: High Failure Job
|
|
echo "4. Test Case 2: High Failure Job (Multiple Retries)\n";
|
|
$highFailureMetrics = new JobMetrics(
|
|
jobId: 'job-failure-002',
|
|
queueName: 'default',
|
|
status: 'failed',
|
|
attempts: 3,
|
|
maxAttempts: 3,
|
|
executionTimeMs: 500.0,
|
|
memoryUsageBytes: 15 * 1024 * 1024, // 15MB
|
|
errorMessage: 'Database connection timeout',
|
|
createdAt: date('Y-m-d H:i:s'),
|
|
startedAt: date('Y-m-d H:i:s'),
|
|
completedAt: null,
|
|
failedAt: date('Y-m-d H:i:s'),
|
|
metadata: ['retry_reason' => 'timeout']
|
|
);
|
|
|
|
$highFailureMetadata = new JobMetadata(
|
|
id: new Ulid(new SystemClock()),
|
|
class: ClassName::create('DatabaseProcessingJob'),
|
|
type: 'job',
|
|
queuedAt: Timestamp::now(),
|
|
tags: ['database', 'critical'],
|
|
extra: ['retry_count' => 3]
|
|
);
|
|
|
|
$result2 = $anomalyMonitor->analyzeJobExecution($highFailureMetrics, $highFailureMetadata, 150);
|
|
echo " Result: " . ($result2->isAnomalous ? "🚨 ANOMALOUS" : "✓ NORMAL") . "\n";
|
|
echo " Confidence: " . sprintf("%.2f%%", $result2->anomalyScore->value() * 100) . "\n";
|
|
echo " Severity: {$result2->getSeverity()}\n";
|
|
if ($result2->isAnomalous) {
|
|
echo " Primary Indicator: {$result2->primaryIndicator}\n";
|
|
echo " Detected Patterns:\n";
|
|
foreach ($result2->detectedPatterns as $pattern) {
|
|
echo " - {$pattern['type']}: " . sprintf("%.2f%% confidence", $pattern['confidence']->value() * 100) . "\n";
|
|
}
|
|
echo " Recommended Action: {$result2->getRecommendedAction()}\n";
|
|
}
|
|
echo "\n";
|
|
|
|
// Test Case 3: Performance Degradation
|
|
echo "5. Test Case 3: Performance Degradation (Slow Execution + High Memory)\n";
|
|
$slowMetrics = new JobMetrics(
|
|
jobId: 'job-slow-003',
|
|
queueName: 'default',
|
|
status: 'completed',
|
|
attempts: 1,
|
|
maxAttempts: 3,
|
|
executionTimeMs: 15000.0, // 15 seconds (very slow)
|
|
memoryUsageBytes: 200 * 1024 * 1024, // 200MB (high memory)
|
|
errorMessage: null,
|
|
createdAt: date('Y-m-d H:i:s'),
|
|
startedAt: date('Y-m-d H:i:s'),
|
|
completedAt: date('Y-m-d H:i:s'),
|
|
failedAt: null,
|
|
metadata: []
|
|
);
|
|
|
|
$slowMetadata = new JobMetadata(
|
|
id: new Ulid(new SystemClock()),
|
|
class: ClassName::create('ReportGenerationJob'),
|
|
type: 'job',
|
|
queuedAt: Timestamp::now(),
|
|
tags: ['report', 'heavy'],
|
|
extra: ['report_type' => 'annual']
|
|
);
|
|
|
|
$result3 = $anomalyMonitor->analyzeJobExecution($slowMetrics, $slowMetadata, 5);
|
|
echo " Result: " . ($result3->isAnomalous ? "🚨 ANOMALOUS" : "✓ NORMAL") . "\n";
|
|
echo " Confidence: " . sprintf("%.2f%%", $result3->anomalyScore->value() * 100) . "\n";
|
|
echo " Severity: {$result3->getSeverity()}\n";
|
|
if ($result3->isAnomalous) {
|
|
echo " Primary Indicator: {$result3->primaryIndicator}\n";
|
|
echo " Top Contributors:\n";
|
|
foreach ($result3->getTopContributors(3) as $contributor) {
|
|
echo " - {$contributor['feature']}: " . sprintf("%.2f%%", $contributor['score']->value() * 100) . "\n";
|
|
}
|
|
}
|
|
echo "\n";
|
|
|
|
// Test Case 4: Queue Backlog Impact
|
|
echo "6. Test Case 4: Queue Backlog Impact (High Queue Depth)\n";
|
|
$backlogMetrics = new JobMetrics(
|
|
jobId: 'job-backlog-004',
|
|
queueName: 'default',
|
|
status: 'completed',
|
|
attempts: 2,
|
|
maxAttempts: 3,
|
|
executionTimeMs: 800.0,
|
|
memoryUsageBytes: 20 * 1024 * 1024, // 20MB
|
|
errorMessage: null,
|
|
createdAt: date('Y-m-d H:i:s'),
|
|
startedAt: date('Y-m-d H:i:s'),
|
|
completedAt: date('Y-m-d H:i:s'),
|
|
failedAt: null,
|
|
metadata: []
|
|
);
|
|
|
|
$backlogMetadata = new JobMetadata(
|
|
id: new Ulid(new SystemClock()),
|
|
class: ClassName::create('EmailNotificationJob'),
|
|
type: 'job',
|
|
queuedAt: Timestamp::now(),
|
|
tags: ['email'],
|
|
extra: []
|
|
);
|
|
|
|
$result4 = $anomalyMonitor->analyzeJobExecution($backlogMetrics, $backlogMetadata, 900); // 900 jobs in queue!
|
|
echo " Result: " . ($result4->isAnomalous ? "🚨 ANOMALOUS" : "✓ NORMAL") . "\n";
|
|
echo " Confidence: " . sprintf("%.2f%%", $result4->anomalyScore->value() * 100) . "\n";
|
|
echo " Severity: {$result4->getSeverity()}\n";
|
|
if ($result4->isAnomalous) {
|
|
echo " Primary Indicator: {$result4->primaryIndicator}\n";
|
|
echo " Immediate Attention: " . ($result4->requiresImmediateAttention() ? "YES" : "NO") . "\n";
|
|
}
|
|
echo "\n";
|
|
|
|
// Test monitoring status
|
|
echo "7. Testing Monitoring Status...\n";
|
|
$anomalyMonitor->enableMonitoring('default');
|
|
$status = $anomalyMonitor->getMonitoringStatus();
|
|
echo " ✓ Monitoring enabled for 'default' queue\n";
|
|
echo " Detector Threshold: " . sprintf("%.0f%%", $status['detector_threshold'] * 100) . "\n";
|
|
echo " Z-Score Threshold: {$status['z_score_threshold']}\n";
|
|
echo " IQR Multiplier: {$status['iqr_multiplier']}\n\n";
|
|
|
|
// Summary
|
|
echo "=== Integration Test Summary ===\n";
|
|
echo "✓ QueueJobFeatureExtractor: Working\n";
|
|
echo "✓ JobAnomalyDetector: Working\n";
|
|
echo "✓ QueueAnomalyMonitor: Working\n";
|
|
echo "✓ Event Logging: Working\n";
|
|
echo "✓ Threshold Configuration: Working\n\n";
|
|
|
|
echo "Test Results:\n";
|
|
echo " - Normal Job: " . ($result1->isAnomalous ? "ANOMALOUS" : "✓ NORMAL") . " (" . sprintf("%.2f%%", $result1->anomalyScore->value() * 100) . ")\n";
|
|
echo " - High Failure Job: " . ($result2->isAnomalous ? "🚨 ANOMALOUS" : "NORMAL") . " (" . sprintf("%.2f%%", $result2->anomalyScore->value() * 100) . ")\n";
|
|
echo " - Performance Degradation: " . ($result3->isAnomalous ? "🚨 ANOMALOUS" : "NORMAL") . " (" . sprintf("%.2f%%", $result3->anomalyScore->value() * 100) . ")\n";
|
|
echo " - Queue Backlog Impact: " . ($result4->isAnomalous ? "🚨 ANOMALOUS" : "NORMAL") . " (" . sprintf("%.2f%%", $result4->anomalyScore->value() * 100) . ")\n\n";
|
|
|
|
echo "=== Integration Test Completed Successfully ===\n";
|
|
|
|
} catch (\Throwable $e) {
|
|
echo "\n!!! INTEGRATION TEST FAILED !!!\n";
|
|
echo "Error: " . $e->getMessage() . "\n";
|
|
echo "File: " . $e->getFile() . ":" . $e->getLine() . "\n";
|
|
echo "\nStack trace:\n" . $e->getTraceAsString() . "\n";
|
|
exit(1);
|
|
}
|