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

@@ -12,7 +12,6 @@ use App\Framework\GraphQL\DataLoader\DataLoader;
use App\Framework\GraphQL\Execution\ExecutionContext;
use App\Framework\GraphQL\Execution\QueryExecutor;
use App\Framework\GraphQL\Execution\QueryParser;
use App\Framework\GraphQL\Schema\Schema;
use App\Framework\GraphQL\Schema\SchemaBuilder;
use App\Framework\GraphQL\Schema\TypeResolver;
@@ -27,6 +26,7 @@ $schemaBuilder = new SchemaBuilder($container, $typeResolver);
class MockDatabase
{
public int $queryCount = 0;
private array $users = [
1 => ['id' => 1, 'name' => 'Alice', 'teamId' => 10],
2 => ['id' => 2, 'name' => 'Bob', 'teamId' => 10],
@@ -43,6 +43,7 @@ class MockDatabase
{
$this->queryCount++;
echo " 📊 Query #{$this->queryCount}: findUser({$id})\n";
return $this->users[$id] ?? null;
}
@@ -57,6 +58,7 @@ class MockDatabase
$result[$id] = $this->users[$id];
}
}
return $result;
}
@@ -64,6 +66,7 @@ class MockDatabase
{
$this->queryCount++;
echo " 📊 Query #{$this->queryCount}: findTeam({$id})\n";
return $this->teams[$id] ?? null;
}
@@ -78,6 +81,7 @@ class MockDatabase
$result[$id] = $this->teams[$id];
}
}
return $result;
}
@@ -85,6 +89,7 @@ class MockDatabase
{
$this->queryCount++;
echo " 📊 Query #{$this->queryCount}: getAllUsers()\n";
return array_values($this->users);
}
@@ -103,7 +108,8 @@ final readonly class Team
public function __construct(
public int $id,
public string $name
) {}
) {
}
}
#[GraphQLType(description: 'A user')]
@@ -115,7 +121,8 @@ final class User
public int $id,
public string $name,
public int $teamId
) {}
) {
}
public static function setDatabase(MockDatabase $database): void
{
@@ -126,7 +133,7 @@ final class User
public function team(ExecutionContext $context): ?Team
{
// Use DataLoader to batch team loading
$teamLoader = $context->loader('teams', function(array $teamIds) {
$teamLoader = $context->loader('teams', function (array $teamIds) {
return self::$database->findTeamsByIds($teamIds);
});
@@ -157,7 +164,7 @@ final class UserQueries
$usersData = self::$database->getAllUsers();
return array_map(
fn($userData) => new User($userData['id'], $userData['name'], $userData['teamId']),
fn ($userData) => new User($userData['id'], $userData['name'], $userData['teamId']),
$usersData
);
}
@@ -187,7 +194,7 @@ $parsed1 = $parser->parse($query1);
$context1 = ExecutionContext::create();
$result1 = $executor->execute($parsed1, [], $context1);
if (!$result1->isSuccessful()) {
if (! $result1->isSuccessful()) {
echo " ❌ Errors: " . json_encode($result1->errors, JSON_PRETTY_PRINT) . "\n";
} else {
echo " ✓ Query Count: {$db->queryCount} (Expected: 1)\n";
@@ -215,7 +222,7 @@ $parsed2 = $parser->parse($query2);
$context2 = ExecutionContext::create();
$result2 = $executor->execute($parsed2, [], $context2);
if (!$result2->isSuccessful()) {
if (! $result2->isSuccessful()) {
echo " ❌ Errors: " . json_encode($result2->errors, JSON_PRETTY_PRINT) . "\n";
} else {
echo " ✓ Query Count: {$db->queryCount} (Expected: 2 instead of 5)\n";
@@ -245,7 +252,7 @@ $parsed3 = $parser->parse($query3);
$context3 = ExecutionContext::create();
// Prime the cache for team 10
$teamLoader = $context3->loader('teams', function(array $teamIds) use ($db) {
$teamLoader = $context3->loader('teams', function (array $teamIds) use ($db) {
return $db->findTeamsByIds($teamIds);
});
$teamLoader->prime(10, ['id' => 10, 'name' => 'Engineering']);
@@ -275,11 +282,11 @@ $parsed4 = $parser->parse($query4);
$context4 = ExecutionContext::create();
// Register multiple loaders
$teamLoader1 = $context4->loader('teams', function(array $ids) use ($db) {
$teamLoader1 = $context4->loader('teams', function (array $ids) use ($db) {
return $db->findTeamsByIds($ids);
});
$userLoader = $context4->loader('users', function(array $ids) use ($db) {
$userLoader = $context4->loader('users', function (array $ids) use ($db) {
return $db->findUsersByIds($ids);
});
@@ -291,8 +298,8 @@ echo " ✓ Both loaders registered and dispatched\n\n";
// Test 5: DataLoader Statistics
echo "5. Testing DataLoader Statistics...\n";
$context5 = ExecutionContext::create();
$testLoader = $context5->loader('test', function(array $ids) {
return array_combine($ids, array_map(fn($id) => "value-{$id}", $ids));
$testLoader = $context5->loader('test', function (array $ids) {
return array_combine($ids, array_map(fn ($id) => "value-{$id}", $ids));
});
// Queue some loads
@@ -315,9 +322,10 @@ echo " ✓ After Dispatch - Dispatched: " . ($statsAfter['dispatched'] ? 'Yes'
// Test 6: Auto-dispatch on threshold
echo "6. Testing Auto-dispatch on Queue Threshold...\n";
$context6 = ExecutionContext::create();
$autoLoader = $context6->loader('auto', function(array $ids) {
$autoLoader = $context6->loader('auto', function (array $ids) {
echo " ⚡ Auto-dispatched batch of " . count($ids) . " items\n";
return array_combine($ids, array_map(fn($id) => "auto-{$id}", $ids));
return array_combine($ids, array_map(fn ($id) => "auto-{$id}", $ids));
});
// Queue 100 items (should auto-dispatch)