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,256 @@
<?php
declare(strict_types=1);
namespace Tests\Performance\Benchmarks;
use App\Framework\Performance\Contracts\PerformanceCollectorInterface;
use App\Framework\Performance\PerformanceCategory;
use App\Framework\Cache\Cache;
use App\Framework\Cache\CacheKey;
use App\Framework\Cache\CacheItem;
use App\Framework\Core\ValueObjects\Duration;
use Tests\Performance\PerformanceTestCase;
use Tests\Performance\PerformanceBenchmarkResult;
/**
* Performance benchmarks for caching system
*
* Tests cache performance including:
* - Get/Set operations
* - Batch operations
* - Cache miss handling
* - Different data sizes
*/
final readonly class CacheBenchmark extends PerformanceTestCase
{
public function __construct(
PerformanceCollectorInterface $collector,
private Cache $cache
) {
parent::__construct($collector);
}
/**
* Benchmark simple cache SET operation
*/
public function benchmarkCacheSet(): PerformanceBenchmarkResult
{
$key = CacheKey::fromString('benchmark_test');
$ttl = Duration::fromMinutes(5);
$result = $this->benchmark(
operation: fn() => $this->cache->set(
CacheItem::forSetting($key, 'test_value', $ttl)
),
iterations: 10000,
name: 'Cache SET'
);
// Cache SET should be very fast
$this->assertPerformanceThreshold($result, maxAvgTimeMs: 0.5);
$this->recordBenchmark($result, PerformanceCategory::CACHE);
return $result;
}
/**
* Benchmark simple cache GET operation
*/
public function benchmarkCacheGet(): PerformanceBenchmarkResult
{
// Warmup: Set a value first
$key = CacheKey::fromString('benchmark_get_test');
$this->cache->set(
CacheItem::forSetting($key, 'test_value', Duration::fromHours(1))
);
$result = $this->benchmark(
operation: fn() => $this->cache->get($key),
iterations: 10000,
name: 'Cache GET (hit)'
);
// Cache GET should be extremely fast
$this->assertPerformanceThreshold($result, maxAvgTimeMs: 0.2);
$this->recordBenchmark($result, PerformanceCategory::CACHE);
return $result;
}
/**
* Benchmark cache GET operation (miss)
*/
public function benchmarkCacheGetMiss(): PerformanceBenchmarkResult
{
$key = CacheKey::fromString('non_existent_key');
$result = $this->benchmark(
operation: fn() => $this->cache->get($key),
iterations: 10000,
name: 'Cache GET (miss)'
);
// Cache miss should still be fast
$this->assertPerformanceThreshold($result, maxAvgTimeMs: 0.3);
$this->recordBenchmark($result, PerformanceCategory::CACHE);
return $result;
}
/**
* Benchmark cache remember pattern
*/
public function benchmarkCacheRemember(): PerformanceBenchmarkResult
{
$key = CacheKey::fromString('benchmark_remember');
$ttl = Duration::fromMinutes(10);
$result = $this->benchmark(
operation: fn() => $this->cache->remember(
key: $key,
callback: fn() => ['computed' => 'value'],
ttl: $ttl
),
iterations: 5000,
name: 'Cache Remember Pattern'
);
$this->assertPerformanceThreshold($result, maxAvgTimeMs: 1.0);
$this->recordBenchmark($result, PerformanceCategory::CACHE);
return $result;
}
/**
* Benchmark batch cache SET operations
*/
public function benchmarkBatchSet(): PerformanceBenchmarkResult
{
$items = [];
for ($i = 0; $i < 100; $i++) {
$items[] = CacheItem::forSetting(
key: CacheKey::fromString("batch_key_{$i}"),
value: "value_{$i}",
ttl: Duration::fromMinutes(5)
);
}
$result = $this->benchmark(
operation: fn() => $this->cache->set(...$items),
iterations: 100,
name: 'Batch SET (100 items)'
);
// Batch operations should be efficient
$this->assertPerformanceThreshold($result, maxAvgTimeMs: 20.0);
$this->recordBenchmark($result, PerformanceCategory::CACHE);
return $result;
}
/**
* Benchmark cache SET with large data
*/
public function benchmarkSetLargeData(): PerformanceBenchmarkResult
{
// Generate 1MB of data
$largeData = str_repeat('A', 1024 * 1024);
$key = CacheKey::fromString('large_data_key');
$result = $this->benchmark(
operation: fn() => $this->cache->set(
CacheItem::forSetting($key, $largeData, Duration::fromMinutes(5))
),
iterations: 100,
name: 'Cache SET (1MB data)'
);
// Large data should still be reasonably fast
$this->assertPerformanceThreshold(
$result,
maxAvgTimeMs: 10.0,
maxMemoryBytes: 5 * 1024 * 1024 // 5MB
);
$this->recordBenchmark($result, PerformanceCategory::CACHE);
return $result;
}
/**
* Benchmark cache DELETE operation
*/
public function benchmarkCacheDelete(): PerformanceBenchmarkResult
{
// Warmup: Set values first
for ($i = 0; $i < 10; $i++) {
$key = CacheKey::fromString("delete_test_{$i}");
$this->cache->set(
CacheItem::forSetting($key, "value_{$i}", Duration::fromMinutes(5))
);
}
$counter = 0;
$result = $this->benchmark(
operation: function() use (&$counter) {
$key = CacheKey::fromString("delete_test_" . ($counter++ % 10));
$this->cache->forget($key);
},
iterations: 10000,
name: 'Cache DELETE'
);
$this->assertPerformanceThreshold($result, maxAvgTimeMs: 0.5);
$this->recordBenchmark($result, PerformanceCategory::CACHE);
return $result;
}
/**
* Benchmark cache HAS operation
*/
public function benchmarkCacheHas(): PerformanceBenchmarkResult
{
// Warmup: Set a value
$key = CacheKey::fromString('has_test');
$this->cache->set(
CacheItem::forSetting($key, 'value', Duration::fromMinutes(5))
);
$result = $this->benchmark(
operation: fn() => $this->cache->has($key),
iterations: 10000,
name: 'Cache HAS'
);
$this->assertPerformanceThreshold($result, maxAvgTimeMs: 0.2);
$this->recordBenchmark($result, PerformanceCategory::CACHE);
return $result;
}
/**
* Run all cache benchmarks and return summary
*/
public function runAllBenchmarks(): array
{
return [
'cache_set' => $this->benchmarkCacheSet(),
'cache_get' => $this->benchmarkCacheGet(),
'cache_get_miss' => $this->benchmarkCacheGetMiss(),
'cache_remember' => $this->benchmarkCacheRemember(),
'batch_set' => $this->benchmarkBatchSet(),
'set_large_data' => $this->benchmarkSetLargeData(),
'cache_delete' => $this->benchmarkCacheDelete(),
'cache_has' => $this->benchmarkCacheHas(),
];
}
}

View File

@@ -0,0 +1,227 @@
<?php
declare(strict_types=1);
namespace Tests\Performance\Benchmarks;
use App\Framework\Performance\Contracts\PerformanceCollectorInterface;
use App\Framework\Performance\PerformanceCategory;
use App\Framework\Database\EntityManager;
use App\Framework\Database\QueryBuilder;
use Tests\Performance\PerformanceTestCase;
use Tests\Performance\PerformanceBenchmarkResult;
/**
* Performance benchmarks for database operations
*
* Tests database performance including:
* - Query execution speed
* - Bulk operations
* - Transaction performance
* - N+1 query prevention
*/
final readonly class DatabaseBenchmark extends PerformanceTestCase
{
public function __construct(
PerformanceCollectorInterface $collector,
private EntityManager $entityManager,
private QueryBuilder $queryBuilder
) {
parent::__construct($collector);
}
/**
* Benchmark simple SELECT query
*/
public function benchmarkSimpleSelect(): PerformanceBenchmarkResult
{
$result = $this->benchmark(
operation: fn() => $this->queryBuilder
->select('*')
->from('users')
->where('id', '=', 1)
->first(),
iterations: 1000,
name: 'Simple SELECT Query'
);
// Simple queries should be very fast
$this->assertPerformanceThreshold($result, maxAvgTimeMs: 1.0);
$this->recordBenchmark($result, PerformanceCategory::DATABASE);
return $result;
}
/**
* Benchmark SELECT with JOIN
*/
public function benchmarkSelectWithJoin(): PerformanceBenchmarkResult
{
$result = $this->benchmark(
operation: fn() => $this->queryBuilder
->select('users.*, profiles.bio')
->from('users')
->join('profiles', 'users.id', '=', 'profiles.user_id')
->where('users.active', '=', true)
->limit(10)
->get(),
iterations: 1000,
name: 'SELECT with JOIN'
);
$this->assertPerformanceThreshold($result, maxAvgTimeMs: 5.0);
$this->recordBenchmark($result, PerformanceCategory::DATABASE);
return $result;
}
/**
* Benchmark bulk INSERT operations
*/
public function benchmarkBulkInsert(): PerformanceBenchmarkResult
{
$records = [];
for ($i = 0; $i < 100; $i++) {
$records[] = [
'name' => "Test User {$i}",
'email' => "test{$i}@example.com",
'created_at' => date('Y-m-d H:i:s')
];
}
$result = $this->benchmark(
operation: function() use ($records) {
$this->entityManager->beginTransaction();
try {
foreach ($records as $record) {
$this->queryBuilder
->table('test_users')
->insert($record);
}
$this->entityManager->commit();
} catch (\Exception $e) {
$this->entityManager->rollback();
throw $e;
}
},
iterations: 10,
name: 'Bulk INSERT (100 records)'
);
// Bulk operations should be efficient
$this->assertPerformanceThreshold(
$result,
maxAvgTimeMs: 50.0,
maxMemoryBytes: 10 * 1024 * 1024 // 10MB
);
$this->recordBenchmark($result, PerformanceCategory::DATABASE);
return $result;
}
/**
* Benchmark transaction performance
*/
public function benchmarkTransaction(): PerformanceBenchmarkResult
{
$result = $this->benchmark(
operation: function() {
$this->entityManager->beginTransaction();
try {
$this->queryBuilder
->table('users')
->where('id', '=', 1)
->update(['updated_at' => date('Y-m-d H:i:s')]);
$this->queryBuilder
->table('audit_log')
->insert([
'action' => 'user_updated',
'user_id' => 1,
'timestamp' => date('Y-m-d H:i:s')
]);
$this->entityManager->commit();
} catch (\Exception $e) {
$this->entityManager->rollback();
throw $e;
}
},
iterations: 500,
name: 'Transaction (2 queries)'
);
$this->assertPerformanceThreshold($result, maxAvgTimeMs: 10.0);
$this->recordBenchmark($result, PerformanceCategory::DATABASE);
return $result;
}
/**
* Benchmark aggregate query (COUNT, AVG, SUM)
*/
public function benchmarkAggregateQuery(): PerformanceBenchmarkResult
{
$result = $this->benchmark(
operation: fn() => $this->queryBuilder
->select('COUNT(*) as total', 'AVG(age) as avg_age')
->from('users')
->where('active', '=', true)
->first(),
iterations: 1000,
name: 'Aggregate Query'
);
$this->assertPerformanceThreshold($result, maxAvgTimeMs: 2.0);
$this->recordBenchmark($result, PerformanceCategory::DATABASE);
return $result;
}
/**
* Benchmark complex WHERE conditions
*/
public function benchmarkComplexWhere(): PerformanceBenchmarkResult
{
$result = $this->benchmark(
operation: fn() => $this->queryBuilder
->select('*')
->from('users')
->where('active', '=', true)
->where('age', '>=', 18)
->where('email', 'LIKE', '%@example.com')
->orWhere('role', '=', 'admin')
->orderBy('created_at', 'DESC')
->limit(20)
->get(),
iterations: 1000,
name: 'Complex WHERE Query'
);
$this->assertPerformanceThreshold($result, maxAvgTimeMs: 5.0);
$this->recordBenchmark($result, PerformanceCategory::DATABASE);
return $result;
}
/**
* Run all database benchmarks and return summary
*/
public function runAllBenchmarks(): array
{
return [
'simple_select' => $this->benchmarkSimpleSelect(),
'select_with_join' => $this->benchmarkSelectWithJoin(),
'bulk_insert' => $this->benchmarkBulkInsert(),
'transaction' => $this->benchmarkTransaction(),
'aggregate_query' => $this->benchmarkAggregateQuery(),
'complex_where' => $this->benchmarkComplexWhere(),
];
}
}

View File

@@ -0,0 +1,211 @@
<?php
declare(strict_types=1);
namespace Tests\Performance\Benchmarks;
use App\Framework\Performance\Contracts\PerformanceCollectorInterface;
use App\Framework\Performance\PerformanceCategory;
use App\Framework\Router\HttpRouter;
use App\Framework\Http\HttpRequest;
use App\Framework\Http\Method;
use App\Framework\Http\ServerEnvironment;
use App\Framework\Http\ParsedUri;
use Tests\Performance\PerformanceTestCase;
use Tests\Performance\PerformanceBenchmarkResult;
/**
* Performance benchmarks for routing system
*
* Tests routing performance including:
* - Route matching speed
* - Parameter extraction
* - Middleware resolution
* - Static vs dynamic routes
*/
final readonly class RoutingBenchmark extends PerformanceTestCase
{
public function __construct(
PerformanceCollectorInterface $collector,
private HttpRouter $router
) {
parent::__construct($collector);
}
/**
* Benchmark static route matching (no parameters)
*/
public function benchmarkStaticRouteMatching(): PerformanceBenchmarkResult
{
$request = $this->createRequest('/api/health', Method::GET);
$result = $this->benchmark(
operation: fn() => $this->router->match($request),
iterations: 10000,
name: 'Static Route Matching'
);
// Assert performance threshold: static routes should be very fast
$this->assertPerformanceThreshold($result, maxAvgTimeMs: 0.1);
// Record metrics
$this->recordBenchmark($result, PerformanceCategory::ROUTING);
return $result;
}
/**
* Benchmark dynamic route matching (with parameters)
*/
public function benchmarkDynamicRouteMatching(): PerformanceBenchmarkResult
{
$request = $this->createRequest('/api/users/123', Method::GET);
$result = $this->benchmark(
operation: fn() => $this->router->match($request),
iterations: 10000,
name: 'Dynamic Route Matching'
);
// Assert performance threshold: dynamic routes can be slightly slower
$this->assertPerformanceThreshold($result, maxAvgTimeMs: 0.5);
$this->recordBenchmark($result, PerformanceCategory::ROUTING);
return $result;
}
/**
* Benchmark complex route matching (multiple parameters)
*/
public function benchmarkComplexRouteMatching(): PerformanceBenchmarkResult
{
$request = $this->createRequest('/api/v1/users/123/posts/456/comments', Method::GET);
$result = $this->benchmark(
operation: fn() => $this->router->match($request),
iterations: 10000,
name: 'Complex Route Matching'
);
// Assert performance threshold: complex routes with multiple params
$this->assertPerformanceThreshold($result, maxAvgTimeMs: 1.0);
$this->recordBenchmark($result, PerformanceCategory::ROUTING);
return $result;
}
/**
* Benchmark route matching with query parameters
*/
public function benchmarkRouteWithQueryParams(): PerformanceBenchmarkResult
{
$request = $this->createRequest(
'/api/search?q=test&limit=10&offset=0&sort=name',
Method::GET
);
$result = $this->benchmark(
operation: fn() => $this->router->match($request),
iterations: 10000,
name: 'Route Matching with Query Params'
);
$this->assertPerformanceThreshold($result, maxAvgTimeMs: 0.2);
$this->recordBenchmark($result, PerformanceCategory::ROUTING);
return $result;
}
/**
* Benchmark route not found scenario
*/
public function benchmarkRouteNotFound(): PerformanceBenchmarkResult
{
$request = $this->createRequest('/non-existent-route', Method::GET);
$result = $this->benchmark(
operation: function() use ($request) {
try {
$this->router->match($request);
} catch (\Exception $e) {
// Expected - route not found
}
},
iterations: 10000,
name: 'Route Not Found'
);
// Route not found should still be fast (fail fast principle)
$this->assertPerformanceThreshold($result, maxAvgTimeMs: 0.1);
$this->recordBenchmark($result, PerformanceCategory::ROUTING);
return $result;
}
/**
* Benchmark POST route matching
*/
public function benchmarkPostRouteMatching(): PerformanceBenchmarkResult
{
$request = $this->createRequest('/api/users', Method::POST);
$result = $this->benchmark(
operation: fn() => $this->router->match($request),
iterations: 10000,
name: 'POST Route Matching'
);
$this->assertPerformanceThreshold($result, maxAvgTimeMs: 0.2);
$this->recordBenchmark($result, PerformanceCategory::ROUTING);
return $result;
}
/**
* Run all routing benchmarks and return summary
*/
public function runAllBenchmarks(): array
{
return [
'static_route' => $this->benchmarkStaticRouteMatching(),
'dynamic_route' => $this->benchmarkDynamicRouteMatching(),
'complex_route' => $this->benchmarkComplexRouteMatching(),
'query_params' => $this->benchmarkRouteWithQueryParams(),
'route_not_found' => $this->benchmarkRouteNotFound(),
'post_route' => $this->benchmarkPostRouteMatching(),
];
}
/**
* Helper: Create HTTP request for testing
*/
private function createRequest(string $uri, Method $method): HttpRequest
{
$parsedUri = ParsedUri::fromString('https://localhost' . $uri);
$server = new ServerEnvironment([
'REQUEST_METHOD' => $method->value,
'REQUEST_URI' => $uri,
'SERVER_NAME' => 'localhost',
'SERVER_PORT' => '443',
'HTTPS' => 'on'
]);
return new HttpRequest(
method: $method,
uri: $parsedUri,
server: $server,
headers: [],
body: '',
parsedBody: null,
queryParameters: $parsedUri->query ?? [],
cookies: [],
files: []
);
}
}