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,38 +2,34 @@
declare(strict_types=1);
use App\Framework\Database\EntityManagerInterface;
use App\Framework\DI\Container;
use App\Framework\DI\DefaultContainer;
use App\Framework\Queue\Queue;
use App\Framework\Queue\QueueInitializer;
use App\Framework\Queue\QueueDependencyInitializer;
// Queue service interfaces
use App\Framework\Queue\Interfaces\DistributedLockInterface;
use App\Framework\Queue\Contracts\JobProgressTrackerInterface;
use App\Framework\Queue\Contracts\JobDependencyManagerInterface;
use App\Framework\Queue\Contracts\DeadLetterQueueInterface;
use App\Framework\Queue\Contracts\JobChainManagerInterface;
use App\Framework\Queue\Services\JobMetricsManagerInterface;
// Concrete implementations
use App\Framework\Queue\Services\DatabaseDistributedLock;
use App\Framework\Queue\Services\DatabaseJobProgressTracker;
use App\Framework\Queue\Services\DatabaseJobDependencyManager;
use App\Framework\Queue\Services\DatabaseDeadLetterQueue;
use App\Framework\Queue\Services\DatabaseJobChainManager;
use App\Framework\Queue\Services\JobMetricsManager;
// Additional services
use App\Framework\Queue\Services\WorkerRegistry;
use App\Framework\Queue\Services\JobDistributionService;
use App\Framework\Queue\Services\WorkerHealthCheckService;
use App\Framework\Queue\Services\FailoverRecoveryService;
use App\Framework\Queue\Services\DependencyResolutionEngine;
// Framework dependencies
use App\Framework\Database\EntityManagerInterface;
use App\Framework\Logging\Logger;
use App\Framework\Queue\Contracts\DeadLetterQueueInterface;
// Queue service interfaces
use App\Framework\Queue\Contracts\JobChainManagerInterface;
use App\Framework\Queue\Contracts\JobDependencyManagerInterface;
use App\Framework\Queue\Contracts\JobProgressTrackerInterface;
use App\Framework\Queue\Interfaces\DistributedLockInterface;
use App\Framework\Queue\Queue;
use App\Framework\Queue\QueueDependencyInitializer;
// Concrete implementations
use App\Framework\Queue\QueueInitializer;
use App\Framework\Queue\Services\DatabaseDeadLetterQueue;
use App\Framework\Queue\Services\DatabaseDistributedLock;
use App\Framework\Queue\Services\DatabaseJobChainManager;
use App\Framework\Queue\Services\DatabaseJobDependencyManager;
use App\Framework\Queue\Services\DatabaseJobProgressTracker;
// Additional services
use App\Framework\Queue\Services\DependencyResolutionEngine;
use App\Framework\Queue\Services\FailoverRecoveryService;
use App\Framework\Queue\Services\JobDistributionService;
use App\Framework\Queue\Services\JobMetricsManager;
use App\Framework\Queue\Services\JobMetricsManagerInterface;
// Framework dependencies
use App\Framework\Queue\Services\WorkerHealthCheckService;
use App\Framework\Queue\Services\WorkerRegistry;
describe('Queue Service Registration', function () {
@@ -41,33 +37,105 @@ describe('Queue Service Registration', function () {
$this->container = new DefaultContainer();
// Mock essential framework dependencies
$this->mockEntityManager = new class implements EntityManagerInterface {
public function persist(object $entity): void {}
public function find(string $className, mixed $id): ?object { return null; }
public function flush(): void {}
public function remove(object $entity): void {}
public function clear(): void {}
public function detach(object $entity): void {}
public function contains(object $entity): bool { return false; }
public function refresh(object $entity): void {}
public function createQueryBuilder(): object { return new stdClass(); }
public function getRepository(string $className): object { return new stdClass(); }
public function beginTransaction(): void {}
public function commit(): void {}
public function rollback(): void {}
public function isTransactionActive(): bool { return false; }
$this->mockEntityManager = new class () implements EntityManagerInterface {
public function persist(object $entity): void
{
}
public function find(string $className, mixed $id): ?object
{
return null;
}
public function flush(): void
{
}
public function remove(object $entity): void
{
}
public function clear(): void
{
}
public function detach(object $entity): void
{
}
public function contains(object $entity): bool
{
return false;
}
public function refresh(object $entity): void
{
}
public function createQueryBuilder(): object
{
return new stdClass();
}
public function getRepository(string $className): object
{
return new stdClass();
}
public function beginTransaction(): void
{
}
public function commit(): void
{
}
public function rollback(): void
{
}
public function isTransactionActive(): bool
{
return false;
}
};
$this->mockLogger = new class implements Logger {
public function emergency(string $message, array $context = []): void {}
public function alert(string $message, array $context = []): void {}
public function critical(string $message, array $context = []): void {}
public function error(string $message, array $context = []): void {}
public function warning(string $message, array $context = []): void {}
public function notice(string $message, array $context = []): void {}
public function info(string $message, array $context = []): void {}
public function debug(string $message, array $context = []): void {}
public function log(string $level, string $message, array $context = []): void {}
$this->mockLogger = new class () implements Logger {
public function emergency(string $message, array $context = []): void
{
}
public function alert(string $message, array $context = []): void
{
}
public function critical(string $message, array $context = []): void
{
}
public function error(string $message, array $context = []): void
{
}
public function warning(string $message, array $context = []): void
{
}
public function notice(string $message, array $context = []): void
{
}
public function info(string $message, array $context = []): void
{
}
public function debug(string $message, array $context = []): void
{
}
public function log(string $level, string $message, array $context = []): void
{
}
};
// Register mocked dependencies
@@ -81,8 +149,9 @@ describe('Queue Service Registration', function () {
// Note: This will fallback to FileQueue since Redis is not available in tests
$queueInitializer = new QueueInitializer(
pathProvider: new class {
public function resolvePath(string $path): string {
pathProvider: new class () {
public function resolvePath(string $path): string
{
return '/home/michael/dev/michaelschiemer/tests/tmp/queue/';
}
}
@@ -96,7 +165,7 @@ describe('Queue Service Registration', function () {
it('Queue service is accessible from container after registration', function () {
// Register queue manually for testing
$this->container->singleton(Queue::class, function() {
$this->container->singleton(Queue::class, function () {
return new \App\Framework\Queue\InMemoryQueue();
});
@@ -111,19 +180,34 @@ describe('Queue Service Registration', function () {
$this->dependencyInitializer = new QueueDependencyInitializer();
// Register a basic queue interface for the dependencies
$this->container->singleton(\App\Framework\Queue\Contracts\QueueInterface::class, function() {
return new class implements \App\Framework\Queue\Contracts\QueueInterface {
public function push(mixed $job): void {}
public function pop(): mixed { return null; }
public function size(): int { return 0; }
$this->container->singleton(\App\Framework\Queue\Contracts\QueueInterface::class, function () {
return new class () implements \App\Framework\Queue\Contracts\QueueInterface {
public function push(mixed $job): void
{
}
public function pop(): mixed
{
return null;
}
public function size(): int
{
return 0;
}
};
});
// Register EventDispatcher mock
$this->container->singleton(\App\Framework\Core\Events\EventDispatcherInterface::class, function() {
return new class implements \App\Framework\Core\Events\EventDispatcherInterface {
public function dispatch(object $event): void {}
public function listen(string $event, callable $listener): void {}
$this->container->singleton(\App\Framework\Core\Events\EventDispatcherInterface::class, function () {
return new class () implements \App\Framework\Core\Events\EventDispatcherInterface {
public function dispatch(object $event): void
{
}
public function listen(string $event, callable $listener): void
{
}
};
});
});
@@ -228,11 +312,21 @@ describe('Queue Service Registration', function () {
$this->mockLogger
));
$this->container->singleton(\App\Framework\Queue\Contracts\QueueInterface::class, function() {
return new class implements \App\Framework\Queue\Contracts\QueueInterface {
public function push(mixed $job): void {}
public function pop(): mixed { return null; }
public function size(): int { return 0; }
$this->container->singleton(\App\Framework\Queue\Contracts\QueueInterface::class, function () {
return new class () implements \App\Framework\Queue\Contracts\QueueInterface {
public function push(mixed $job): void
{
}
public function pop(): mixed
{
return null;
}
public function size(): int
{
return 0;
}
};
});
@@ -264,23 +358,23 @@ describe('Queue Service Registration', function () {
expect($resolutionEngine)->toBeInstanceOf(DependencyResolutionEngine::class);
// These services should be functional (not throw errors)
expect(fn() => $dependencyManager)->not->toThrow();
expect(fn() => $chainManager)->not->toThrow();
expect(fn() => $resolutionEngine)->not->toThrow();
expect(fn () => $dependencyManager)->not->toThrow();
expect(fn () => $chainManager)->not->toThrow();
expect(fn () => $resolutionEngine)->not->toThrow();
});
it('can resolve complex dependency graph', function () {
$this->dependencyInitializer->__invoke($this->container);
// Add additional services
$this->container->singleton(DistributedLockInterface::class, function() {
$this->container->singleton(DistributedLockInterface::class, function () {
return new DatabaseDistributedLock(
$this->mockEntityManager,
$this->mockLogger
);
});
$this->container->singleton(JobProgressTrackerInterface::class, function() {
$this->container->singleton(JobProgressTrackerInterface::class, function () {
return new DatabaseJobProgressTracker(
$this->mockEntityManager,
$this->mockLogger
@@ -323,11 +417,22 @@ describe('Queue Service Registration', function () {
$original = $this->container->get(JobMetricsManager::class);
// Create mock replacement
$mock = new class implements JobMetricsManagerInterface {
public function recordJobExecution(\App\Framework\Queue\ValueObjects\JobId $jobId, float $executionTime): void {}
public function recordJobFailure(\App\Framework\Queue\ValueObjects\JobId $jobId, string $errorMessage): void {}
public function getJobMetrics(\App\Framework\Queue\ValueObjects\JobId $jobId): ?\App\Framework\Queue\ValueObjects\JobMetrics { return null; }
public function getQueueMetrics(\App\Framework\Queue\ValueObjects\QueueName $queueName): \App\Framework\Queue\ValueObjects\QueueMetrics {
$mock = new class () implements JobMetricsManagerInterface {
public function recordJobExecution(\App\Framework\Queue\ValueObjects\JobId $jobId, float $executionTime): void
{
}
public function recordJobFailure(\App\Framework\Queue\ValueObjects\JobId $jobId, string $errorMessage): void
{
}
public function getJobMetrics(\App\Framework\Queue\ValueObjects\JobId $jobId): ?\App\Framework\Queue\ValueObjects\JobMetrics
{
return null;
}
public function getQueueMetrics(\App\Framework\Queue\ValueObjects\QueueName $queueName): \App\Framework\Queue\ValueObjects\QueueMetrics
{
return new \App\Framework\Queue\ValueObjects\QueueMetrics(
queueName: $queueName,
totalJobs: 0,
@@ -336,7 +441,11 @@ describe('Queue Service Registration', function () {
averageExecutionTime: 0.0
);
}
public function getSystemMetrics(): array { return []; }
public function getSystemMetrics(): array
{
return [];
}
};
// Replace with mock
@@ -357,7 +466,7 @@ describe('Queue Service Registration', function () {
$dependencyInitializer = new QueueDependencyInitializer();
// This should fail due to missing dependencies
expect(fn() => $dependencyInitializer->__invoke($this->container))
expect(fn () => $dependencyInitializer->__invoke($this->container))
->toThrow();
});
});
@@ -369,47 +478,134 @@ describe('Queue Service Integration Test', function () {
$this->container = new DefaultContainer();
// Register all required mocks
$this->container->instance(EntityManagerInterface::class, new class implements EntityManagerInterface {
public function persist(object $entity): void {}
public function find(string $className, mixed $id): ?object { return null; }
public function flush(): void {}
public function remove(object $entity): void {}
public function clear(): void {}
public function detach(object $entity): void {}
public function contains(object $entity): bool { return false; }
public function refresh(object $entity): void {}
public function createQueryBuilder(): object { return new stdClass(); }
public function getRepository(string $className): object { return new stdClass(); }
public function beginTransaction(): void {}
public function commit(): void {}
public function rollback(): void {}
public function isTransactionActive(): bool { return false; }
$this->container->instance(EntityManagerInterface::class, new class () implements EntityManagerInterface {
public function persist(object $entity): void
{
}
public function find(string $className, mixed $id): ?object
{
return null;
}
public function flush(): void
{
}
public function remove(object $entity): void
{
}
public function clear(): void
{
}
public function detach(object $entity): void
{
}
public function contains(object $entity): bool
{
return false;
}
public function refresh(object $entity): void
{
}
public function createQueryBuilder(): object
{
return new stdClass();
}
public function getRepository(string $className): object
{
return new stdClass();
}
public function beginTransaction(): void
{
}
public function commit(): void
{
}
public function rollback(): void
{
}
public function isTransactionActive(): bool
{
return false;
}
});
$this->container->instance(Logger::class, new class implements Logger {
public function emergency(string $message, array $context = []): void {}
public function alert(string $message, array $context = []): void {}
public function critical(string $message, array $context = []): void {}
public function error(string $message, array $context = []): void {}
public function warning(string $message, array $context = []): void {}
public function notice(string $message, array $context = []): void {}
public function info(string $message, array $context = []): void {}
public function debug(string $message, array $context = []): void {}
public function log(string $level, string $message, array $context = []): void {}
$this->container->instance(Logger::class, new class () implements Logger {
public function emergency(string $message, array $context = []): void
{
}
public function alert(string $message, array $context = []): void
{
}
public function critical(string $message, array $context = []): void
{
}
public function error(string $message, array $context = []): void
{
}
public function warning(string $message, array $context = []): void
{
}
public function notice(string $message, array $context = []): void
{
}
public function info(string $message, array $context = []): void
{
}
public function debug(string $message, array $context = []): void
{
}
public function log(string $level, string $message, array $context = []): void
{
}
});
$this->container->singleton(\App\Framework\Queue\Contracts\QueueInterface::class, function() {
return new class implements \App\Framework\Queue\Contracts\QueueInterface {
public function push(mixed $job): void {}
public function pop(): mixed { return null; }
public function size(): int { return 0; }
$this->container->singleton(\App\Framework\Queue\Contracts\QueueInterface::class, function () {
return new class () implements \App\Framework\Queue\Contracts\QueueInterface {
public function push(mixed $job): void
{
}
public function pop(): mixed
{
return null;
}
public function size(): int
{
return 0;
}
};
});
$this->container->singleton(\App\Framework\Core\Events\EventDispatcherInterface::class, function() {
return new class implements \App\Framework\Core\Events\EventDispatcherInterface {
public function dispatch(object $event): void {}
public function listen(string $event, callable $listener): void {}
$this->container->singleton(\App\Framework\Core\Events\EventDispatcherInterface::class, function () {
return new class () implements \App\Framework\Core\Events\EventDispatcherInterface {
public function dispatch(object $event): void
{
}
public function listen(string $event, callable $listener): void
{
}
};
});
});
@@ -420,28 +616,28 @@ describe('Queue Service Integration Test', function () {
$dependencyInitializer->__invoke($this->container);
// Register additional services that would normally be auto-registered
$this->container->singleton(DistributedLockInterface::class, function($container) {
$this->container->singleton(DistributedLockInterface::class, function ($container) {
return new DatabaseDistributedLock(
$container->get(EntityManagerInterface::class),
$container->get(Logger::class)
);
});
$this->container->singleton(JobProgressTrackerInterface::class, function($container) {
$this->container->singleton(JobProgressTrackerInterface::class, function ($container) {
return new DatabaseJobProgressTracker(
$container->get(EntityManagerInterface::class),
$container->get(Logger::class)
);
});
$this->container->singleton(DeadLetterQueueInterface::class, function($container) {
$this->container->singleton(DeadLetterQueueInterface::class, function ($container) {
return new DatabaseDeadLetterQueue(
$container->get(EntityManagerInterface::class),
$container->get(Logger::class)
);
});
$this->container->singleton(WorkerRegistry::class, function($container) {
$this->container->singleton(WorkerRegistry::class, function ($container) {
return new WorkerRegistry(
$container->get(EntityManagerInterface::class),
$container->get(Logger::class)
@@ -486,8 +682,8 @@ describe('Queue Service Integration Test', function () {
$metricsManager = $this->container->get(JobMetricsManager::class);
// Basic interaction tests (should not throw)
expect(fn() => $dependencyManager)->not->toThrow();
expect(fn() => $chainManager)->not->toThrow();
expect(fn() => $metricsManager)->not->toThrow();
expect(fn () => $dependencyManager)->not->toThrow();
expect(fn () => $chainManager)->not->toThrow();
expect(fn () => $metricsManager)->not->toThrow();
});
});
});