Время ответа растёт только под нагрузкой, а одиночный запрос быстрый. Найдите причину.
В проде время ответа на пике растёт со 120 мс до 8 с, а потом восстанавливается. Одиночный запрос к той же машине быстрый, CPU на веб-серверах держится на 30%.
Ограничения: код не менялся, CPU базы тоже не нагружен, ошибок в логах нет. У вас есть status-страница PHP-FPM и slow log за пиковое окно.
pool: www
process manager: dynamic
max children: 20
active processes: 20
idle processes: 0
listen queue: 312
max listen queue: 512
slow requests: 847
[slow-log] script /index.php request_uri=/api/orders
[0x00] curl_exec() /app/Service/Warehouse.php:41
Определите причину.
Пул насыщен, а не медленный: 20 из 20 воркеров заняты и 312 запросов стоят в listen-очереди, поэтому большая часть из 8 с — ожидание воркера. Slow log называет виновника: блокирующий curl_exec() к сервису склада держит воркер секундами. Поднять pm.max_children — лишь передвинуть очередь; нужен таймаут и вынос этого вызова из запроса.
- ✗Читать длинную listen-очередь как сетевую проблему, а не как исчерпание пула
- ✗Поднять
pm.max_childrenвыше бюджета RAM и превратить очередь в swap - ✗Игнорировать slow log, потому что одиночный запрос выглядит быстрым
- →Почему блокирующий вызов стороннего сервиса под нагрузкой бьёт сильнее, чем в одиночном запросе?
- →Что таймаут
curlгарантирует про худшее время удержания воркера?
active processes: 20 из max children: 20 и listen queue: 312 — это не «медленный код», а исчерпанный пул. Запрос простаивает в очереди сокета, пока не освободится воркер, поэтому одиночный запрос и остаётся быстрым: ему воркер достаётся сразу.
Низкий CPU это подтверждает: воркеры не считают, а ждут.
active processes: 20 / 20 → свободных воркеров нет
listen queue: 312 → 312 запросов ждут воркера
CPU: 30% → воркеры ждут, а не считают
Slow log показывает, чего именно они ждут — блокирующий curl_exec() к сервису склада. Пока он висит, воркер занят и никому не доступен.
// Warehouse.php:41 — вызов без таймаута держит воркер сколько угодно
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, 300);
curl_setopt($ch, CURLOPT_TIMEOUT_MS, 800); // худший случай теперь ограничен
$body = curl_exec($ch);
Порядок действий: поставить таймаут (ограничить худший случай), затем вынести вызов из запроса — в очередь или в кэш ответов склада. Поднимать pm.max_children без этого нельзя: очередь просто переедет внутрь машины, а память кончится.