feat(Production): Complete production deployment infrastructure

- Add comprehensive health check system with multiple endpoints
- Add Prometheus metrics endpoint
- Add production logging configurations (5 strategies)
- Add complete deployment documentation suite:
  * QUICKSTART.md - 30-minute deployment guide
  * DEPLOYMENT_CHECKLIST.md - Printable verification checklist
  * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle
  * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference
  * production-logging.md - Logging configuration guide
  * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation
  * README.md - Navigation hub
  * DEPLOYMENT_SUMMARY.md - Executive summary
- Add deployment scripts and automation
- Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment
- Update README with production-ready features

All production infrastructure is now complete and ready for deployment.
This commit is contained in:
2025-10-25 19:18:37 +02:00
parent caa85db796
commit fc3d7e6357
83016 changed files with 378904 additions and 20919 deletions

View File

@@ -2,30 +2,33 @@
declare(strict_types=1);
use App\Framework\Queue\Entities\Worker;
use App\Framework\Queue\ValueObjects\WorkerId;
use App\Framework\Queue\ValueObjects\QueueName;
use App\Framework\Queue\Services\WorkerRegistry;
use App\Framework\Queue\Services\WorkerHealthCheckService;
use App\Framework\Core\ValueObjects\Percentage;
use App\Framework\Core\ValueObjects\Byte;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Core\ValueObjects\Percentage;
use App\Framework\Queue\Entities\Worker;
use App\Framework\Queue\Services\WorkerHealthCheckService;
use App\Framework\Queue\Services\WorkerRegistry;
use App\Framework\Queue\ValueObjects\QueueName;
use App\Framework\Queue\ValueObjects\WorkerId;
describe('Worker Management System', function () {
beforeEach(function () {
// Mock database connection for testing
$this->mockConnection = new class {
$this->mockConnection = new class () {
private array $data = [];
private int $lastInsertId = 0;
private int $rowCount = 0;
public function prepare(string $sql): object
{
return new class($sql, $this) {
return new class ($sql, $this) {
public function __construct(
private string $sql,
private object $connection
) {}
) {
}
public function execute(array $params = []): bool
{
@@ -55,6 +58,7 @@ describe('Worker Management System', function () {
$this->connection->data['health_checks'][] = $params;
$this->connection->rowCount = 1;
}
return true;
}
@@ -62,6 +66,7 @@ describe('Worker Management System', function () {
{
if (str_contains($this->sql, 'SELECT * FROM queue_workers WHERE id = :id')) {
$id = func_get_args()[0]['id'] ?? null;
return $this->connection->data['workers'][$id] ?? false;
}
@@ -72,6 +77,7 @@ describe('Worker Management System', function () {
return $worker;
}
}
return false;
}
@@ -79,13 +85,13 @@ describe('Worker Management System', function () {
if (str_contains($this->sql, 'COUNT(*) as total_workers')) {
return [
'total_workers' => count($this->connection->data['workers'] ?? []),
'active_workers' => count(array_filter($this->connection->data['workers'] ?? [], fn($w) => $w['is_active'])),
'healthy_workers' => count(array_filter($this->connection->data['workers'] ?? [], fn($w) => $w['is_active'])),
'active_workers' => count(array_filter($this->connection->data['workers'] ?? [], fn ($w) => $w['is_active'])),
'healthy_workers' => count(array_filter($this->connection->data['workers'] ?? [], fn ($w) => $w['is_active'])),
'total_capacity' => 100,
'current_load' => 50,
'avg_cpu_usage' => 25.5,
'avg_memory_usage' => 1024 * 1024 * 512, // 512MB
'unique_hosts' => 2
'unique_hosts' => 2,
];
}
@@ -103,10 +109,11 @@ describe('Worker Management System', function () {
'total_checks' => 10,
'healthy_count' => 8,
'warning_count' => 2,
'critical_count' => 0
]
'critical_count' => 0,
],
];
}
return [];
}
@@ -124,7 +131,7 @@ describe('Worker Management System', function () {
};
// Mock logger for testing
$this->mockLogger = new class {
$this->mockLogger = new class () {
public array $logs = [];
public function info(string $message, array $context = []): void
@@ -179,7 +186,7 @@ describe('Worker Management System', function () {
});
it('throws exception for empty worker ID', function () {
expect(fn() => WorkerId::fromString(''))
expect(fn () => WorkerId::fromString(''))
->toThrow(\InvalidArgumentException::class, 'WorkerId cannot be empty');
});
@@ -226,7 +233,7 @@ describe('Worker Management System', function () {
});
it('throws exception when no queues provided', function () {
expect(fn() => new Worker(
expect(fn () => new Worker(
id: WorkerId::generate(),
hostname: 'test',
processId: 1234,
@@ -237,7 +244,7 @@ describe('Worker Management System', function () {
});
it('validates job constraints during construction', function () {
$baseWorker = fn($maxJobs, $currentJobs) => new Worker(
$baseWorker = fn ($maxJobs, $currentJobs) => new Worker(
id: WorkerId::generate(),
hostname: 'test',
processId: 1234,
@@ -248,15 +255,15 @@ describe('Worker Management System', function () {
);
// Invalid max jobs
expect(fn() => $baseWorker(0, 0))
expect(fn () => $baseWorker(0, 0))
->toThrow(\InvalidArgumentException::class, 'Max jobs must be greater than 0');
expect(fn() => $baseWorker(-1, 0))
expect(fn () => $baseWorker(-1, 0))
->toThrow(\InvalidArgumentException::class, 'Max jobs must be greater than 0');
// Invalid current jobs
expect(fn() => $baseWorker(5, -1))
expect(fn () => $baseWorker(5, -1))
->toThrow(\InvalidArgumentException::class, 'Current jobs cannot be negative');
expect(fn() => $baseWorker(5, 10))
expect(fn () => $baseWorker(5, 10))
->toThrow(\InvalidArgumentException::class, 'Current jobs cannot exceed max jobs');
});
@@ -525,14 +532,14 @@ describe('Worker Management System', function () {
'hostname' => 'test-host',
'process_id' => 1234,
'queues' => [QueueName::default()],
'max_jobs' => 10
]
'max_jobs' => 10,
],
]);
expect($this->mockLogger->logs)->toContain([
'level' => 'debug',
'message' => 'Worker registered successfully',
'context' => ['worker_id' => $worker->id->toString()]
'context' => ['worker_id' => $worker->id->toString()],
]);
});
@@ -543,8 +550,8 @@ describe('Worker Management System', function () {
$this->mockConnection->setWorkerData([
$workerId->toString() => [
'id' => $workerId->toString(),
'is_active' => 1
]
'is_active' => 1,
],
]);
$this->workerRegistry->deregister($workerId);
@@ -552,7 +559,7 @@ describe('Worker Management System', function () {
expect($this->mockLogger->logs)->toContain([
'level' => 'info',
'message' => 'Deregistering worker',
'context' => ['worker_id' => $workerId->toString()]
'context' => ['worker_id' => $workerId->toString()],
]);
});
@@ -566,14 +573,14 @@ describe('Worker Management System', function () {
$this->mockConnection->setWorkerData([
$workerId->toString() => [
'id' => $workerId->toString(),
'is_active' => 1
]
'is_active' => 1,
],
]);
$this->workerRegistry->updateHeartbeat($workerId, $cpuUsage, $memoryUsage, $currentJobs);
// Should not log warnings when worker found and updated
$warningLogs = array_filter($this->mockLogger->logs, fn($log) => $log['level'] === 'warning');
$warningLogs = array_filter($this->mockLogger->logs, fn ($log) => $log['level'] === 'warning');
expect($warningLogs)->toBeEmpty();
});
@@ -586,7 +593,7 @@ describe('Worker Management System', function () {
$this->workerRegistry->updateHeartbeat($workerId, $cpuUsage, $memoryUsage, 2);
$warningLogs = array_filter($this->mockLogger->logs, fn($log) => $log['level'] === 'warning');
$warningLogs = array_filter($this->mockLogger->logs, fn ($log) => $log['level'] === 'warning');
expect($warningLogs)->not()->toBeEmpty();
});
@@ -605,7 +612,7 @@ describe('Worker Management System', function () {
'registered_at' => '2024-01-01 12:00:00',
'last_heartbeat' => '2024-01-01 12:05:00',
'capabilities' => json_encode(['pdf']),
'version' => '1.0.0'
'version' => '1.0.0',
];
$this->mockConnection->setWorkerData([$workerId->toString() => $workerData]);
@@ -651,7 +658,7 @@ describe('Worker Management System', function () {
expect($this->mockLogger->logs)->toContain([
'level' => 'info',
'message' => 'Starting cleanup of inactive workers',
'context' => ['inactive_minutes' => 5]
'context' => ['inactive_minutes' => 5],
]);
});
});
@@ -777,8 +784,8 @@ describe('Worker Management System', function () {
'registered_at' => '2024-01-01 12:00:00',
'last_heartbeat' => date('Y-m-d H:i:s'),
'capabilities' => json_encode([]),
'version' => '1.0.0'
]
'version' => '1.0.0',
],
]);
$report = $this->healthCheckService->generateSystemHealthReport();
@@ -802,8 +809,8 @@ describe('Worker Management System', function () {
'message' => 'Health check cleanup completed',
'context' => [
'deleted_records' => $deletedCount,
'retention_days' => 7.0
]
'retention_days' => 7.0,
],
]);
});
});
@@ -857,7 +864,7 @@ describe('Worker Management System', function () {
);
// Should log warning
$warningLogs = array_filter($this->mockLogger->logs, fn($log) => $log['level'] === 'warning');
$warningLogs = array_filter($this->mockLogger->logs, fn ($log) => $log['level'] === 'warning');
expect($warningLogs)->not()->toBeEmpty();
});
@@ -890,4 +897,4 @@ describe('Worker Management System', function () {
expect($inactiveWorker->isActive)->toBeFalse(); // New instance inactive
});
});
});
});