Awesome-claude-code scalability-knowledge

Scalability knowledge base. Provides vertical vs horizontal scaling, stateless design, session management, connection pooling, capacity planning, and PHP-FPM tuning for scalability audits.

install
source · Clone the upstream repo
git clone https://github.com/dykyi-roman/awesome-claude-code
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/dykyi-roman/awesome-claude-code "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/scalability-knowledge" ~/.claude/skills/dykyi-roman-awesome-claude-code-scalability-knowledge && rm -rf "$T"
manifest: skills/scalability-knowledge/SKILL.md
source content

Scalability Knowledge Base

Quick reference for scalability patterns, stateless design, PHP-FPM tuning, and capacity planning in PHP applications.

Vertical vs Horizontal Scaling

AspectVertical (Scale Up)Horizontal (Scale Out)
ApproachBigger server (CPU, RAM)More servers
Cost curveExponential (diminishing returns)Linear (commodity hardware)
DowntimeOften required for upgradeZero-downtime rolling deploys
LimitHardware ceilingTheoretically unlimited
ComplexityLow (single server)High (distributed system)
Data consistencySimple (single node)Requires distributed coordination
Failure blast radiusEntire applicationSingle instance
PHP suitabilityQuick win, limited ceilingNatural fit (shared-nothing)

When to Use Each

ScenarioStrategyWhy
Early stage, simple appVerticalCheapest, simplest
Read-heavy workloadHorizontal + read replicasDistribute read load
Write-heavy workloadHorizontal + shardingDistribute write load
Unpredictable trafficHorizontal + auto-scalingElastic capacity
Legacy monolithVertical first, then decomposeBuys time for refactoring

Stateless vs Stateful: PHP Shared-Nothing Architecture

PHP is shared-nothing by design — each request starts with a clean process, no shared memory between requests. This is a natural advantage for horizontal scaling.

┌─────────────────────────────────────────────────────────────────────────┐
│                    SHARED-NOTHING ARCHITECTURE                          │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│   Load Balancer                                                         │
│       │                                                                 │
│   ┌───┼───────────────────┬─────────────────────┐                      │
│   │   │                   │                     │                      │
│   ▼   ▼                   ▼                     ▼                      │
│ ┌──────────┐        ┌──────────┐          ┌──────────┐                 │
│ │ PHP-FPM  │        │ PHP-FPM  │          │ PHP-FPM  │                 │
│ │ Worker 1 │        │ Worker 2 │          │ Worker N │                 │
│ │          │        │          │          │          │                 │
│ │ No shared│        │ No shared│          │ No shared│                 │
│ │ state    │        │ state    │          │ state    │                 │
│ └────┬─────┘        └────┬─────┘          └────┬─────┘                 │
│      │                   │                     │                       │
│      └───────────────────┼─────────────────────┘                       │
│                          │                                              │
│                          ▼                                              │
│                 ┌─────────────────┐                                     │
│                 │  External State  │                                    │
│                 │  Redis / DB      │                                    │
│                 └─────────────────┘                                     │
│                                                                         │
│   Key Principle: ANY request can be served by ANY worker.               │
│   State lives in external stores (Redis, DB), NOT in process memory.   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

Stateless Checklist

RequirementStatelessStateful (Problem)
Session dataRedis / JWT
$_SESSION
with file storage
File uploadsObject storage (S3)Local filesystem
CacheRedis / MemcachedAPCu (per-process)
ConfigurationEnv vars / config serviceLocal config files that vary per server
Scheduled jobsCentralized schedulerLocal cron per server
WebSocket stateRedis pub/subIn-memory connections

Session Management

File-Based Sessions (Problem)

Server A: session file → /tmp/sess_abc123
Server B: no session file → user logged out!

Sticky sessions (workaround) → couples user to server → defeats horizontal scaling

Redis Sessions (Solution)

<?php

declare(strict_types=1);

namespace Infrastructure\Session;

final readonly class RedisSessionConfig
{
    public function __construct(
        private string $redisHost,
        private int $redisPort = 6379,
        private string $redisPrefix = 'sess:',
        private int $ttlSeconds = 1800,
    ) {}

    public function configure(): void
    {
        ini_set('session.save_handler', 'redis');
        ini_set('session.save_path', sprintf(
            'tcp://%s:%d?prefix=%s&timeout=2',
            $this->redisHost,
            $this->redisPort,
            $this->redisPrefix,
        ));
        ini_set('session.gc_maxlifetime', (string) $this->ttlSeconds);
    }
}

JWT Stateless Alternative

<?php

declare(strict_types=1);

namespace Infrastructure\Auth;

final readonly class JwtTokenFactory
{
    public function __construct(
        private string $secretKey,
        private string $algorithm = 'HS256',
        private int $ttlSeconds = 3600,
    ) {}

    /**
     * @param array<string, mixed> $claims
     */
    public function create(string $userId, array $claims = []): string
    {
        $header = base64_encode(json_encode([
            'alg' => $this->algorithm,
            'typ' => 'JWT',
        ], JSON_THROW_ON_ERROR));

        $payload = base64_encode(json_encode(array_merge($claims, [
            'sub' => $userId,
            'iat' => time(),
            'exp' => time() + $this->ttlSeconds,
        ]), JSON_THROW_ON_ERROR));

        $signature = base64_encode(hash_hmac(
            'sha256',
            sprintf('%s.%s', $header, $payload),
            $this->secretKey,
            true,
        ));

        return sprintf('%s.%s.%s', $header, $payload, $signature);
    }
}

Connection Pooling

PHP creates a new database connection per request (shared-nothing). Without pooling, high-concurrency scenarios exhaust database connections.

Why PHP Needs External Poolers

ProblemCauseSolution
Connection exhaustionEach PHP-FPM worker opens own connectionpgbouncer / ProxySQL
Connection overheadTCP handshake + auth per requestPersistent connections
Idle connectionsWorkers hold connections while waiting for I/OExternal pooler reclaims idle
Max connections limitPostgreSQL default 100, MySQL 151Pooler multiplexes

Connection Pool Wrapper

<?php

declare(strict_types=1);

namespace Infrastructure\Database;

final readonly class ConnectionPoolConfig
{
    public function __construct(
        private string $host,
        private int $port,
        private string $database,
        private string $user,
        private string $password,
        private bool $persistent = true,
        private int $connectTimeoutSeconds = 5,
        private int $statementTimeoutMs = 30000,
    ) {}

    public function createPdo(): \PDO
    {
        $dsn = sprintf(
            'pgsql:host=%s;port=%d;dbname=%s',
            $this->host,
            $this->port,
            $this->database,
        );

        $options = [
            \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
            \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
            \PDO::ATTR_EMULATE_PREPARES => false,
            \PDO::ATTR_PERSISTENT => $this->persistent,
            \PDO::ATTR_TIMEOUT => $this->connectTimeoutSeconds,
        ];

        $pdo = new \PDO($dsn, $this->user, $this->password, $options);
        $pdo->exec(sprintf(
            'SET statement_timeout = %d',
            $this->statementTimeoutMs,
        ));

        return $pdo;
    }
}

External Pooler Architecture

┌──────────────────────────────────────────────────────────────────┐
│                   CONNECTION POOLING                              │
├──────────────────────────────────────────────────────────────────┤
│                                                                   │
│   PHP-FPM Workers (200+)                                         │
│   ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐                 │
│   │  W1  │ │  W2  │ │  W3  │ │ ...  │ │ W200 │                 │
│   └──┬───┘ └──┬───┘ └──┬───┘ └──┬───┘ └──┬───┘                 │
│      │        │        │        │        │                       │
│      └────────┴────────┴────┬───┴────────┘                       │
│                             │                                    │
│                             ▼                                    │
│                    ┌─────────────────┐                           │
│                    │   pgbouncer /   │  Pool: 20-50 connections  │
│                    │   ProxySQL      │  Mode: transaction        │
│                    └────────┬────────┘                           │
│                             │                                    │
│                             ▼                                    │
│                    ┌─────────────────┐                           │
│                    │   PostgreSQL /  │  max_connections: 100     │
│                    │   MySQL         │                           │
│                    └─────────────────┘                           │
│                                                                   │
│   200 PHP workers share 20-50 DB connections via pooler          │
│                                                                   │
└──────────────────────────────────────────────────────────────────┘

Capacity Planning

Amdahl's Law

Speedup = 1 / ((1 - P) + P/N)

P = parallelizable fraction of workload
N = number of processors/instances

Example: If 90% of work is parallelizable (P=0.9), 10 servers:
  Speedup = 1 / ((1 - 0.9) + 0.9/10) = 1 / (0.1 + 0.09) = 5.26x

Lesson: Serial bottlenecks (DB writes, locks) limit scaling.

Little's Law

L = λ × W

L = average number of concurrent requests
λ = arrival rate (requests/second)
W = average response time (seconds)

Example: 500 req/s, 200ms avg response time:
  L = 500 × 0.2 = 100 concurrent requests needed

Lesson: To handle 500 req/s at 200ms, you need capacity for 100 concurrent requests.

Throughput Formula

Throughput = Workers / Avg_Response_Time

Example: 50 PHP-FPM workers, 100ms avg:
  Throughput = 50 / 0.1 = 500 req/s

To increase throughput:
  1. Add more workers (horizontal scaling)
  2. Reduce response time (optimization)
  3. Both

PHP-FPM Scaling

Worker Calculation Formula

pm.max_children = Available_Memory / Avg_Worker_Memory

Example:
  Server RAM: 4 GB
  OS + overhead: 512 MB
  Available: 3584 MB
  Avg PHP worker: 40 MB

  pm.max_children = 3584 / 40 = 89 workers

Process Manager Modes

Modepm.max_childrenWorkersUse Case
static
Fixed pool sizeAlways runningStable, predictable traffic
dynamic
Max pool sizeScale between min/maxGeneral purpose, variable traffic
ondemand
Max pool sizeCreated per request, killed after idleLow-traffic, memory-constrained

Recommended Settings

SettingStatic ModeDynamic ModeOndemand Mode
pm
static
dynamic
ondemand
pm.max_children
898989
pm.start_servers
20
pm.min_spare_servers
10
pm.max_spare_servers
30
pm.max_requests
500500500
pm.process_idle_timeout
10s

OPcache Preloading (PHP 8.4)

; php.ini — OPcache settings for production
opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0
opcache.preload=/app/config/preload.php
opcache.preload_user=www-data
opcache.jit=1255
opcache.jit_buffer_size=128M
<?php

declare(strict_types=1);

namespace App\Config;

// preload.php — Preload hot classes into OPcache at FPM startup
// All preloaded classes are available without autoloading overhead

$classMap = [
    __DIR__ . '/../src/Domain/Entity/',
    __DIR__ . '/../src/Domain/ValueObject/',
    __DIR__ . '/../src/Application/UseCase/',
];

foreach ($classMap as $directory) {
    $iterator = new \RecursiveIteratorIterator(
        new \RecursiveDirectoryIterator($directory),
    );

    /** @var \SplFileInfo $file */
    foreach ($iterator as $file) {
        if ($file->isFile() && $file->getExtension() === 'php') {
            opcache_compile_file($file->getRealPath());
        }
    }
}

Quick Reference Tables

Scaling Decision Matrix

SignalActionImplementation
CPU > 80% sustainedAdd instances or upgrade CPUHorizontal + auto-scaling
Memory > 85%Reduce worker count or add RAMpm.max_children tuning
Response time > SLAProfile + optimize or add capacityAPM + horizontal scaling
Connection pool exhaustedAdd pooler or increase poolpgbouncer / ProxySQL
Disk I/O bottleneckMove to SSD, offload to object storageInfrastructure change
Request queue growingAdd PHP-FPM workerspm.max_children increase

Common Bottlenecks

BottleneckSymptomFix
Database queriesSlow response, high DB CPUQuery optimization, caching, read replicas
Session storageInconsistent sessions across serversRedis sessions
File uploadsDisk I/O, storage limitsObject storage (S3)
External API callsTimeout, high latencyCircuit breaker, async processing
PHP-FPM workers502/504 errors, request queueIncrease pm.max_children
OPcacheSlow first requests after deployPreloading, warm-up scripts

Detection Patterns

# PHP-FPM configuration
Grep: "pm\.max_children|pm\.start_servers|pm\.min_spare|pm\.max_spare" --glob "**/php-fpm*.conf"
Grep: "pm\.max_children|pm\.start_servers" --glob "**/www.conf"
Grep: "pm\.max_requests|pm\.process_idle_timeout" --glob "**/php-fpm*.conf"

# OPcache settings
Grep: "opcache\." --glob "**/php.ini"
Grep: "opcache_compile_file|opcache_reset" --glob "**/*.php"
Grep: "opcache\.preload" --glob "**/php.ini"

# Session configuration
Grep: "session\.save_handler|session\.save_path" --glob "**/php.ini"
Grep: "session_start|SESSION" --glob "**/*.php"
Grep: "Redis.*session|session.*redis" --glob "**/*.php"

# Connection pooling
Grep: "PDO::ATTR_PERSISTENT|ATTR_PERSISTENT" --glob "**/*.php"
Grep: "pgbouncer|proxysql" --glob "**/docker-compose*.yml"

# Stateless violations
Grep: "file_put_contents|fwrite.*tmp" --glob "**/src/**/*.php"
Grep: "\\\$_SESSION" --glob "**/src/**/*.php"
Grep: "apc_store|apcu_store" --glob "**/src/**/*.php"

# Scaling indicators
Grep: "HORIZONTAL_SCALE|AUTO_SCALE|REPLICAS" --glob "**/.env*"
Grep: "replicas:|scale:" --glob "**/docker-compose*.yml"

References

For detailed information, load these reference files:

  • references/scaling-patterns.md
    — Horizontal scaling strategies, auto-scaling triggers, read replicas, write scaling, caching as scaling tool
  • references/php-specifics.md
    — PHP-FPM tuning, OPcache settings, shared-nothing architecture, persistent connections, external poolers, real-time alternatives