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

@@ -0,0 +1,208 @@
<?php
declare(strict_types=1);
use App\Framework\Database\QueryOptimization\Analysis\EagerLoadingAnalyzer;
use App\Framework\Database\QueryOptimization\Analysis\NPlusOneDetector;
use App\Framework\Database\QueryOptimization\NPlusOneDetectionService;
use App\Framework\Database\QueryOptimization\QueryLogger;
use App\Framework\Database\QueryOptimization\ValueObjects\QueryLog;
use App\Framework\Logging\Logger;
describe('NPlusOneDetectionService', function () {
beforeEach(function () {
$this->queryLogger = new QueryLogger();
$this->detector = new NPlusOneDetector();
$this->analyzer = new EagerLoadingAnalyzer();
$this->logger = $this->createMock(Logger::class);
$this->service = new NPlusOneDetectionService(
$this->queryLogger,
$this->detector,
$this->analyzer,
$this->logger
);
});
it('starts and stops logging', function () {
expect($this->queryLogger->isEnabled())->toBeFalse();
$this->service->startLogging();
expect($this->queryLogger->isEnabled())->toBeTrue();
$this->service->stopLogging();
expect($this->queryLogger->isEnabled())->toBeFalse();
});
it('analyzes logged queries', function () {
// Enable logging
$this->service->startLogging();
// Simulate N+1 queries
for ($i = 1; $i <= 10; $i++) {
$this->queryLogger->logQuery(
sql: 'SELECT * FROM posts WHERE user_id = ?',
bindings: [$i],
executionTimeMs: 5.0,
rowCount: 1
);
}
// Analyze
$result = $this->service->analyze();
expect($result)->toHaveKeys(['detections', 'strategies', 'statistics']);
expect($result['detections'])->not->toBeEmpty();
expect($result['statistics']['total_queries'])->toBe(10);
});
it('generates formatted report', function () {
$this->service->startLogging();
// Simulate N+1 queries
for ($i = 1; $i <= 10; $i++) {
$this->queryLogger->logQuery(
sql: 'SELECT * FROM posts WHERE user_id = ?',
bindings: [$i],
executionTimeMs: 5.0,
rowCount: 1
);
}
$report = $this->service->analyzeAndReport();
expect($report)->toContain('N+1 Query Detection Report');
expect($report)->toContain('Query Statistics');
expect($report)->toContain('N+1 Problems Detected');
});
it('profiles code execution', function () {
$executedCallable = false;
$analysis = $this->service->profile(function () use (&$executedCallable) {
$executedCallable = true;
return 'result';
});
expect($executedCallable)->toBeTrue();
expect($analysis)->toHaveKey('execution_time_ms');
expect($analysis)->toHaveKey('callback_result');
expect($analysis['callback_result'])->toBe('result');
});
it('returns critical problems only', function () {
$this->service->startLogging();
// Add critical N+1 (high execution count)
for ($i = 1; $i <= 50; $i++) {
$this->queryLogger->logQuery(
sql: 'SELECT * FROM posts WHERE user_id = ?',
bindings: [$i],
executionTimeMs: 10.0,
rowCount: 1
);
}
// Add low severity N+1
for ($i = 1; $i <= 6; $i++) {
$this->queryLogger->logQuery(
sql: 'SELECT * FROM comments WHERE post_id = ?',
bindings: [$i],
executionTimeMs: 1.0,
rowCount: 1
);
}
$critical = $this->service->getCriticalProblems();
// Should only contain the high-severity posts pattern
expect($critical)->toHaveCount(1);
expect($critical[0]->isCritical())->toBeTrue();
});
it('quick check detects N+1 problems', function () {
$this->service->startLogging();
for ($i = 1; $i <= 10; $i++) {
$this->queryLogger->logQuery(
sql: 'SELECT * FROM posts WHERE user_id = ?',
bindings: [$i],
executionTimeMs: 5.0,
rowCount: 1
);
}
expect($this->service->hasNPlusOneProblems())->toBeTrue();
});
it('returns query statistics', function () {
$this->service->startLogging();
$this->queryLogger->logQuery('SELECT * FROM users', [], 5.0, 10);
$this->queryLogger->logQuery('INSERT INTO logs (message) VALUES (?)', ['test'], 2.0, 1);
$this->queryLogger->logQuery('UPDATE users SET name = ? WHERE id = ?', ['John', 1], 3.0, 1);
$stats = $this->service->getQueryStatistics();
expect($stats['total_queries'])->toBe(3);
expect($stats['select_count'])->toBe(1);
expect($stats['insert_count'])->toBe(1);
expect($stats['update_count'])->toBe(1);
});
it('clears query logs', function () {
$this->service->startLogging();
$this->queryLogger->logQuery('SELECT * FROM users', [], 5.0);
expect($this->service->getQueryLogs())->toHaveCount(1);
$this->service->clearLogs();
expect($this->service->getQueryLogs())->toBeEmpty();
});
it('integrates detections with eager loading strategies', function () {
$this->service->startLogging();
// Create N+1 pattern
for ($i = 1; $i <= 15; $i++) {
$this->queryLogger->logQuery(
sql: 'SELECT * FROM posts WHERE user_id = ?',
bindings: [$i],
executionTimeMs: 8.0,
rowCount: 1
);
}
$result = $this->service->analyze();
expect($result['detections'])->toHaveCount(1);
expect($result['strategies'])->toHaveCount(1);
$strategy = $result['strategies'][0];
expect($strategy->tableName)->toBe('posts');
expect($strategy->codeExample)->toContain('eager loading');
});
it('handles empty query logs gracefully', function () {
$this->service->startLogging();
$result = $this->service->analyze();
expect($result['detections'])->toBeEmpty();
expect($result['strategies'])->toBeEmpty();
expect($result['statistics']['total_queries'])->toBe(0);
});
it('logs analysis completion', function () {
$this->logger->expects($this->once())
->method('info')
->with(
$this->stringContains('Analysis completed'),
$this->anything()
);
$this->service->startLogging();
$this->queryLogger->logQuery('SELECT * FROM users', [], 5.0);
$this->service->analyze();
});
});