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,11 +2,11 @@
declare(strict_types=1);
use App\Framework\Queue\ValueObjects\LockKey;
use App\Framework\Queue\ValueObjects\WorkerId;
use App\Framework\Queue\ValueObjects\JobId;
use App\Framework\Queue\ValueObjects\QueueName;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Queue\ValueObjects\JobId;
use App\Framework\Queue\ValueObjects\LockKey;
use App\Framework\Queue\ValueObjects\QueueName;
use App\Framework\Queue\ValueObjects\WorkerId;
describe('LockKey Value Object', function () {
@@ -20,7 +20,7 @@ describe('LockKey Value Object', function () {
'resource.database_connection',
'batch.monthly_report_2024',
'a', // minimum length
str_repeat('a', 255) // maximum length
str_repeat('a', 255), // maximum length
];
foreach ($validKeys as $key) {
@@ -56,11 +56,11 @@ describe('LockKey Value Object', function () {
'lock?invalid',
'lock/invalid',
'lock~invalid',
'lock`invalid'
'lock`invalid',
];
foreach ($invalidKeys as $key) {
expect(fn() => LockKey::fromString($key))
expect(fn () => LockKey::fromString($key))
->toThrow(\InvalidArgumentException::class);
}
});
@@ -191,7 +191,7 @@ describe('LockKey Value Object', function () {
expect(strlen($withShortPrefix->toString()))->toBe(252); // 'x' + '.' + 250
// This should fail (would exceed 255 chars)
expect(fn() => $baseKey->withPrefix('toolong'))
expect(fn () => $baseKey->withPrefix('toolong'))
->toThrow(\InvalidArgumentException::class);
});
@@ -210,10 +210,11 @@ describe('Distributed Lock Mock Implementation', function () {
beforeEach(function () {
// Create a mock distributed lock for testing
$this->distributedLock = new class {
$this->distributedLock = new class () {
private array $locks = [];
public function acquire(LockKey $key, WorkerId $workerId, Duration $ttl): bool {
public function acquire(LockKey $key, WorkerId $workerId, Duration $ttl): bool
{
$keyStr = $key->toString();
$now = time();
@@ -230,17 +231,18 @@ describe('Distributed Lock Mock Implementation', function () {
'worker_id' => $workerId->toString(),
'acquired_at' => $now,
'expires_at' => $now + $ttl->toSeconds(),
'ttl' => $ttl->toSeconds()
'ttl' => $ttl->toSeconds(),
];
return true;
}
public function extend(LockKey $key, WorkerId $workerId, Duration $ttl): bool {
public function extend(LockKey $key, WorkerId $workerId, Duration $ttl): bool
{
$keyStr = $key->toString();
$now = time();
if (!isset($this->locks[$keyStr])) {
if (! isset($this->locks[$keyStr])) {
return false; // Lock doesn't exist
}
@@ -258,10 +260,11 @@ describe('Distributed Lock Mock Implementation', function () {
return true;
}
public function release(LockKey $key, WorkerId $workerId): bool {
public function release(LockKey $key, WorkerId $workerId): bool
{
$keyStr = $key->toString();
if (!isset($this->locks[$keyStr])) {
if (! isset($this->locks[$keyStr])) {
return false; // Lock doesn't exist
}
@@ -273,26 +276,30 @@ describe('Distributed Lock Mock Implementation', function () {
}
unset($this->locks[$keyStr]);
return true;
}
public function exists(LockKey $key): bool {
public function exists(LockKey $key): bool
{
$keyStr = $key->toString();
$now = time();
if (!isset($this->locks[$keyStr])) {
if (! isset($this->locks[$keyStr])) {
return false;
}
$lock = $this->locks[$keyStr];
return $lock['expires_at'] > $now;
}
public function getLockInfo(LockKey $key): ?array {
public function getLockInfo(LockKey $key): ?array
{
$keyStr = $key->toString();
$now = time();
if (!isset($this->locks[$keyStr])) {
if (! isset($this->locks[$keyStr])) {
return null;
}
@@ -307,11 +314,12 @@ describe('Distributed Lock Mock Implementation', function () {
'worker_id' => $lock['worker_id'],
'acquired_at' => date('Y-m-d H:i:s', $lock['acquired_at']),
'expires_at' => date('Y-m-d H:i:s', $lock['expires_at']),
'ttl_seconds' => $lock['expires_at'] - $now
'ttl_seconds' => $lock['expires_at'] - $now,
];
}
public function acquireWithTimeout(LockKey $key, WorkerId $workerId, Duration $ttl, Duration $timeout): bool {
public function acquireWithTimeout(LockKey $key, WorkerId $workerId, Duration $ttl, Duration $timeout): bool
{
$startTime = microtime(true);
$timeoutSeconds = $timeout->toSeconds();
@@ -325,7 +333,8 @@ describe('Distributed Lock Mock Implementation', function () {
return false;
}
public function releaseAllWorkerLocks(WorkerId $workerId): int {
public function releaseAllWorkerLocks(WorkerId $workerId): int
{
$workerIdStr = $workerId->toString();
$released = 0;
@@ -339,7 +348,8 @@ describe('Distributed Lock Mock Implementation', function () {
return $released;
}
public function cleanupExpiredLocks(): int {
public function cleanupExpiredLocks(): int
{
$now = time();
$cleaned = 0;
@@ -353,7 +363,8 @@ describe('Distributed Lock Mock Implementation', function () {
return $cleaned;
}
public function getLockStatistics(): array {
public function getLockStatistics(): array
{
$now = time();
$activeLocks = 0;
$expiredLocks = 0;
@@ -374,8 +385,8 @@ describe('Distributed Lock Mock Implementation', function () {
'expired_locks' => $expiredLocks,
'unique_workers' => count($workers),
'avg_ttl_seconds' => $activeLocks > 0 ?
array_sum(array_map(fn($lock) => $lock['ttl'], array_filter($this->locks, fn($lock) => $lock['expires_at'] > $now))) / $activeLocks
: 0
array_sum(array_map(fn ($lock) => $lock['ttl'], array_filter($this->locks, fn ($lock) => $lock['expires_at'] > $now))) / $activeLocks
: 0,
];
}
};
@@ -644,9 +655,11 @@ describe('Distributed Lock Mock Implementation', function () {
describe('Distributed Lock Integration Scenarios', function () {
beforeEach(function () {
$this->distributedLock = new class {
$this->distributedLock = new class () {
private array $locks = [];
public function acquire(LockKey $key, WorkerId $workerId, Duration $ttl): bool {
public function acquire(LockKey $key, WorkerId $workerId, Duration $ttl): bool
{
$keyStr = $key->toString();
$now = time();
if (isset($this->locks[$keyStr]) && $this->locks[$keyStr]['expires_at'] > $now) {
@@ -655,36 +668,45 @@ describe('Distributed Lock Integration Scenarios', function () {
$this->locks[$keyStr] = [
'worker_id' => $workerId->toString(),
'acquired_at' => $now,
'expires_at' => $now + $ttl->toSeconds()
'expires_at' => $now + $ttl->toSeconds(),
];
return true;
}
public function release(LockKey $key, WorkerId $workerId): bool {
public function release(LockKey $key, WorkerId $workerId): bool
{
$keyStr = $key->toString();
if (!isset($this->locks[$keyStr]) || $this->locks[$keyStr]['worker_id'] !== $workerId->toString()) {
if (! isset($this->locks[$keyStr]) || $this->locks[$keyStr]['worker_id'] !== $workerId->toString()) {
return false;
}
unset($this->locks[$keyStr]);
return true;
}
public function exists(LockKey $key): bool {
public function exists(LockKey $key): bool
{
$keyStr = $key->toString();
return isset($this->locks[$keyStr]) && $this->locks[$keyStr]['expires_at'] > time();
}
};
$this->emailJob = new class {
$this->emailJob = new class () {
public function __construct(
public string $batchId = 'email-batch-123',
public int $recipientCount = 1000
) {}
) {
}
};
$this->reportJob = new class {
$this->reportJob = new class () {
public function __construct(
public string $reportId = 'monthly-sales-2024',
public string $resourceType = 'database'
) {}
) {
}
};
});
@@ -810,4 +832,4 @@ describe('Distributed Lock Integration Scenarios', function () {
$queueNowAcquired = $this->distributedLock->acquire($queueLock, $queueWorker, Duration::fromMinutes(30));
expect($queueNowAcquired)->toBeTrue();
});
});
});