WordPress «Максимальний час виконання перевищено»: чому це відбувається й безпечні виправлення

Було корисно?

Ви натискаєте «Оновити плагін». Колесо крутиться. Потім WordPress падає з повідомленням: «Fatal error: Maximum execution time of 30 seconds exceeded». Або сторінка просто стає білою й ви дивитеся на власне відображення, неначе йому щось винні.

Ця помилка — не театральна витівка WordPress. Це сервер, який примусово припиняє виконання: PHP‑запит триває довше, ніж дозволено. Головне — виправити це без нічого-не-рятуючих рішень типу «поставимо 300 секунд» і на цьому все закриємо — довгі таймаути перетворюють дрібні проблеми на інциденти в продакшені.

Що насправді означає помилка (і чого вона не означає)

У більшості WordPress‑стеків повідомлення «Max execution time exceeded» генерується PHP, коли запит триває довше, ніж значення max_execution_time. Це обмеження PHP (для кожного запиту), створене, щоб запобігти безконтрольному використанню CPU.

Але в реальних системах обмеження PHP рідко є єдиним таймаутом. Зазвичай у вас є шар таймаутів:

  • Таймаут браузера/клієнта (користувач здається або reverse proxy закриває з’єднання).
  • Таймаути зворотного проксі (Nginx, CDN, балансувальник навантаження).
  • Таймаути веб‑сервера (Apache, Nginx fastcgi).
  • Таймаути PHP‑FPM (request_terminate_timeout або налаштування пулу).
  • Ліміт скрипта PHP (max_execution_time).
  • Таймаути бази даних (MySQL wait timeout, lock wait timeout).
  • Зовнішні API (платіжні шлюзи, SMTP, ліцензійні сервери).

Тому, коли ви бачите помилку, не робіть автоматичного висновку «піднімемо max_execution_time і готово». Іноді PHP — лише посланець. Справжній винуватець — повільний запит до БД, блокування таблиць, перевантаження CPU, насичення дискового вводу/виводу або плагін, який робить сумнівну річ, наприклад змінює розміри 200 зображень в одному веб‑запиті.

Позиція автора: ставтеся до помилок max execution time як до сигналу пожежної сигналізації. Батарейки можна дістати, звісно. Але спершу перевірте, чому кухня в полум’ї.

Цитата, яку варто пам’ятати

«Надія — не стратегія.» — Джин Кранц

Так, це популярна фраза в операційних колах. І вона точна. Вгадай‑в‑що — це надія. Міряти — це стратегія.

Чому це відбувається: реальні вузькі місця

1) Голодування CPU: PHP чекає своєї черги

PHP сам по собі не надто повільний, але чутливий до конкуренції. Якщо на сервері забагато PHP‑FPM воркерів, кожен отримає лише шматочок CPU. Завдання, яке «звично займає 2 секунди», може затягнутися до 45. Бум — таймаут.

Це поширено на шаред‑хостингах, недостатньо виділених VPS і в конфігураціях «ми зменшили інстанс, щоб заощадити».

2) Дисковий ввід/вивід: тихий вбивця

WordPress багато читає й пише: PHP‑файли, плагіни, сесії, опції, генерація мініатюр. Якщо сховище повільне (мережеві диски, перевантажені диски, деградований RAID, вичерпані кредити на burst), PHP може витрачати більшу частину «часу виконання» на очікування I/O.

3) Вузькі місця бази даних: повільні запити, блокування і реальність ALTER

Коли WordPress зависає, часто він чекає MySQL/MariaDB. Класичні причини:

  • Відсутні індекси, додані плагінами таблиці без оптимізації.
  • Величезний wp_options з надто багато autoload‑рядками.
  • Конфлікти блокувань під час імпортів, пакетних оновлень або збоїв cron‑завдань.
  • Довгі ALTER TABLE під час робочого часу. Ризиковано.

4) Зовнішні виклики: сайт такий швидкий, як та одна стороння API

Платіжні шлюзи, калькулятори доставки, перевірки ліцензій, сервіси оптимізації зображень, поштові провайдери — якщо плагін робить синхронні HTTP‑виклики під час завантаження сторінки або адміністрування, він успадковує всю крихкість мережі.

5) WP‑Cron: «не справжній cron» означає «іноді накопичення»

WP‑Cron запускається під час завантажень сторінок, якщо його не відключити та не налаштувати системний cron. Якщо трафік низький — завдання виконуються з запізненням і накопичуються. Якщо трафік високий — завдання можуть перекриватися й конкурувати за ресурси. Довгі cron‑завдання, що виконуються через HTTP‑запити, — головні кандидати на таймаут.

6) Тиск пам’яті, маскуючийся під таймаути

Інколи запит повільний тому, що він “смикається”: виділення пам’яті, звертання до swap, запуск GC або повторні невдачі при виділенні пам’яті для обробки зображень. Вона все одно може виявитися як execution timeout, хоча справжня проблема — пам’ять.

Жарт короткий #1: таймаути як дедлайни — їх ігнорують, поки бос не почне карати.

Швидкий план діагностики (перевірити спочатку/потім/після)

Якщо хочете швидко знайти вузьке місце — робіть у цьому порядку. Це мінімізує гадання й максимізує «доказовість».

Перший крок: визначте, який таймаут спрацював (PHP vs PHP‑FPM vs веб‑сервер)

  • Якщо бачите «Fatal error: Maximum execution time of X seconds exceeded» у логах або виводі — ймовірно спрацював PHP‑параметр max_execution_time.
  • Якщо бачите 502/504 на проксі — підозрюйте таймаути Nginx/Apache/proxy або завершення роботи PHP‑FPM.
  • Якщо в логах PHP‑FPM є рядки на зразок «request terminated» — підозрюйте request_terminate_timeout або вбивство на рівні пулу.

Другий крок: вирішіть, чи це системне (ресурсне) або локальне (одна точка)

  • Системне: CPU у піку, високе середнє навантаження, висока латентність диска, багато повільних запитів.
  • Локальне: завжди відбувається під час оновлення плагіна, імпорту, на конкретній сторінці адмінки або під час оформлення замовлення.

Третій крок: доведіть, на що саме чекають (CPU vs диск vs БД vs мережа)

  • CPU: черга виконання велика, багато PHP‑воркерів, довгий реальний час при малій зайнятості CPU.
  • Диск: високий iowait, повільний await у iostat.
  • БД: повільні запити, очікування блокувань, нитки в стані Sending data або Waiting for table metadata lock.
  • Мережа/зовнішні: запити зависають у HTTP‑викликах; логи плагінів показують таймаути на зовнішні сервіси.

Четвертий крок: оберіть найменше безпечне виправлення

Часто найменше безпечне виправлення — не «ставимо все на 600 секунд». Це одне з таких рішень:

  • Перенести важку роботу за межі веб‑запиту (WP‑CLI, фонова обробка).
  • Виправити повільний запит / додати індекс / зменшити autoload‑біль.
  • Підібрати число PHP‑FPM воркерів відповідно до CPU і пам’яті.
  • Виправити дисковий I/O або перейти на швидше сховище.
  • Додати розумні таймаути й повторні спроби для зовнішніх викликів з резервною логікою.

Практичні завдання: команди, виводи та рішення (12+)

Це «зроби зараз і дізнайся щось» завдання. Кожне містить команду, приклад виводу, що це означає і як вирішити. Припускає Linux з systemd, Nginx або Apache і PHP‑FPM. Підлаштуйте шляхи під вашу дистрибуцію.

Task 1: Confirm PHP max_execution_time (CLI baseline)

cr0x@server:~$ php -i | grep -E '^max_execution_time'
max_execution_time => 30 => 30

Що це означає: PHP CLI має значення 30 секунд. Web SAPI може відрізнятися, але це дає базову відправну точку.

Рішення: Якщо CLI показує розумне значення (30–60), але веб‑запити все одно таймаутять по‑іншому, зосередьтеся на конфігураціях PHP‑FPM і веб‑сервера, а не на здогадках щодо глобального php.ini.

Task 2: Confirm PHP-FPM pool effective settings

cr0x@server:~$ php-fpm8.2 -tt 2>/dev/null | head -n 20
[27-Dec-2025 10:11:02] NOTICE: configuration file /etc/php/8.2/fpm/php-fpm.conf test is successful
[27-Dec-2025 10:11:02] NOTICE: fpm is running, pid 1162
[27-Dec-2025 10:11:02] NOTICE: ready to handle connections

Що це означає: Конфігурація парситься. Це не доказ робочих значень за час виконання, але запобігає сюрпризам «ви відредагували не той файл».

Рішення: Якщо тест конфігурації не пройшов — зупиніться й виправте синтаксис. Таймаути через «сервіс не перезавантажили» трапляються соромно часто.

Task 3: Check Nginx FastCGI timeouts

cr0x@server:~$ sudo nginx -T 2>/dev/null | grep -E 'fastcgi_read_timeout|fastcgi_send_timeout|proxy_read_timeout' | head
fastcgi_read_timeout 60s;
fastcgi_send_timeout 60s;

Що це означає: Nginx чекатиме 60 секунд відповіді від PHP‑FPM. Якщо PHP дозволено 120 секунд, а Nginx чекає 60, ви отримаєте 504 ще до того, як PHP завершить.

Рішення: Вирівняйте таймаути. Таймаут на краю/проксі зазвичай має бути трохи більшим, ніж PHP, для користувацьких запитів, якщо ви не хочете свідомо обмежувати час відповіді.

Task 4: Check Apache timeout (if using prefork/event with proxy_fcgi)

cr0x@server:~$ apachectl -S 2>/dev/null | head -n 5
VirtualHost configuration:
*:80                   is a NameVirtualHost
         default server example.com (/etc/apache2/sites-enabled/000-default.conf:1)
cr0x@server:~$ sudo apachectl -t -D DUMP_RUN_CFG 2>/dev/null | grep -E '^Timeout'
Timeout: 60

Що це означає: Apache чекає 60 секунд. Знову: якщо PHP може виконуватися 120 секунд, клієнт може ніколи цього не побачити.

Рішення: Або знизьте PHP, щоб збігалося й примусити фонову обробку, або підніміть веб‑таймаут лише для окремих кінцевих точок (імпорти) й тримайте решту суворою.

Task 5: Find the exact error in PHP-FPM and PHP logs

cr0x@server:~$ sudo grep -R "Maximum execution time" -n /var/log/php* /var/log/php8.2-fpm.log 2>/dev/null | tail -n 5
/var/log/php8.2-fpm.log:21944:PHP Fatal error:  Maximum execution time of 30 seconds exceeded in /var/www/html/wp-includes/http.php on line 320

Що це означає: Стек‑трек вказує на WordPress HTTP‑запити. Зазвичай це плагін/тема, що робить зовнішній виклик, або ядро WordPress, яке «розмовляє само з собою».

Рішення: Далі — ідентифікувати, який плагін викликав запит (тимчасово ввімкнути WP debug‑логування або відтворити з відключеними плагінами).

Task 6: Observe live resource pressure (CPU, memory, iowait)

cr0x@server:~$ top -b -n 1 | head -n 12
top - 10:13:44 up 12 days,  2:17,  1 user,  load average: 7.12, 6.95, 6.20
Tasks: 245 total,   2 running, 243 sleeping,   0 stopped,   0 zombie
%Cpu(s): 22.1 us,  3.2 sy,  0.0 ni, 63.4 id, 11.0 wa,  0.0 hi,  0.3 si,  0.0 st
MiB Mem :   7842.1 total,    412.6 free,   6120.3 used,   1309.2 buff/cache
MiB Swap:   2048.0 total,   1880.0 free,    168.0 used.   1200.0 avail Mem

Що це означає: wa на рівні 11% вказує на очікування диска. Середнє навантаження високе відносно CPU‑ядер (інтерпретувати відповідно), що означає утворення черг.

Рішення: Не підвищуйте таймаути поки не перевірите. Розслідуйте латентність сховища та конкурентність PHP‑FPM (надто багато воркерів може посилити I/O‑конфлікт).

Task 7: Measure disk latency with iostat

cr0x@server:~$ iostat -xz 1 3
Linux 6.1.0 (server) 	12/27/2025 	_x86_64_	(4 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          18.52    0.00    3.01   12.44    0.00   66.03

Device            r/s     w/s    rkB/s    wkB/s  avgrq-sz avgqu-sz   await  svctm  %util
nvme0n1         82.00  140.00  3200.0   8200.0     78.00     2.10   14.80   0.55  12.30

Що це означає: await близько ~15ms не катастрофічний, але для завантаженого WordPress може накопичуватися. Якщо бачите 50–200ms — це червоний сигнал.

Рішення: Якщо await стабільно високий — виправляйте сховище (перейдіть на швидший том, зупиніть «шумних сусідів», перевірте RAID/диск) перед тим, як торкатися таймаутів PHP.

Task 8: Check PHP-FPM pool saturation

cr0x@server:~$ sudo ss -s
Total: 1123 (kernel 0)
TCP:   317 (estab 54, closed 233, orphaned 0, timewait 233)

Transport Total     IP        IPv6
RAW	  0         0         0
UDP	  6         4         2
TCP	  84        47        37
INET	  90        51        39
FRAG	  0         0         0
cr0x@server:~$ sudo ps -o pid,pcpu,pmem,etime,cmd -C php-fpm8.2 --sort=-pcpu | head
  PID %CPU %MEM     ELAPSED CMD
 1422 18.2  2.7       01:03 php-fpm: pool www
 1418 15.9  2.6       01:01 php-fpm: pool www
 1399 12.4  2.5       00:59 php-fpm: pool www

Що це означає: Кілька воркерів витрачають CPU понад хвилину — це свідчення повільних запитів або дедлоків. Якщо всі застрягли на приблизно однаковому часі — можлива «стадна» ситуація.

Рішення: Перевірте статус PHP‑FPM (якщо ввімкнено) або логи на предмет «server reached pm.max_children». Якщо пул насичений — зменшіть конкурентність або додайте CPU/пам’яті; не підвищуйте таймаути просто так.

Task 9: Inspect MySQL for slow queries and lock waits

cr0x@server:~$ mysql -e "SHOW PROCESSLIST\G" | head -n 30
*************************** 1. row ***************************
     Id: 27
   User: wpuser
   Host: localhost
     db: wordpress
Command: Query
   Time: 41
  State: Sending data
   Info: SELECT option_name, option_value FROM wp_options WHERE autoload='yes'
*************************** 2. row ***************************
     Id: 29
   User: wpuser
   Host: localhost
     db: wordpress
Command: Query
   Time: 39
  State: Waiting for table metadata lock
   Info: ALTER TABLE wp_posts ADD COLUMN foo int(11)

Що це означає: Один запит повільний (wp_options autoload). Інша сесія заблокована на metadata lock через зміну схеми.

Рішення: Не робіть змін схеми під активним трафіком. Також проведіть аудит autoload‑блоку wp_options — це класичний камінь спотикання WordPress.

Task 10: Quantify autoloaded options size

cr0x@server:~$ mysql -N -e "SELECT ROUND(SUM(LENGTH(option_value))/1024/1024,2) AS autoload_mb FROM wp_options WHERE autoload='yes';"
12.47

Що це означає: 12.47MB автозавантажених опцій — багато. WordPress завантажує їх на кожен запит. Вітаємо: ви побудували маленьку базу конфігурацій всередині бази.

Рішення: Ідентифікуйте найбільших порушників і змініть autoload на «no», якщо безпечно, або перенесіть кеш у object cache/транзієнти. Якщо ви вище ~1–2MB — зазвичай ви платите «податок» на кожен перегляд сторінки.

Task 11: Find the largest autoload offenders

cr0x@server:~$ mysql -e "SELECT option_name, ROUND(LENGTH(option_value)/1024,1) AS kb FROM wp_options WHERE autoload='yes' ORDER BY LENGTH(option_value) DESC LIMIT 10;"
+------------------------------+-------+
| option_name                  | kb    |
+------------------------------+-------+
| some_plugin_cache_blob       | 2048.3|
| another_plugin_settings      | 512.8 |
| rewrite_rules                | 256.4 |
| widget_custom_html           | 188.1 |
+------------------------------+-------+

Що це означає: Плагін зберігає кеш‑блоб в autoload (погано), інші опції великі.

Рішення: Для кеш‑блобів: перемістіть в object cache або транзієнти з терміном дії; встановіть autoload «no», якщо плагін це дозволяє. Не редагуйте вручну без бекапу й плану відкату.

Task 12: Check WP-Cron health and backlog with WP-CLI

cr0x@server:~$ cd /var/www/html && sudo -u www-data wp cron event list --fields=hook,next_run,recurrence --format=table | head
+------------------------------+---------------------+------------+
| hook                         | next_run            | recurrence |
+------------------------------+---------------------+------------+
| wp_version_check             | 2025-12-27 10:30:00 | twice_daily|
| woocommerce_cleanup_sessions | 2025-12-27 10:15:00 | hourly     |
| some_plugin_heavy_job        | 2025-12-27 10:01:00 | every_min  |
+------------------------------+---------------------+------------+

Що це означає: Завдання з параметром «every_min» викликає підозру. Воно може перекриватися, якщо виконується довше хвилини.

Рішення: Якщо хук занадто частий або важкий — перенесіть його на системний cron, зменшіть частоту або рефакторіть у батчі.

Task 13: Reproduce the slow endpoint with curl and timing

cr0x@server:~$ curl -s -o /dev/null -w "code=%{http_code} t_total=%{time_total} t_connect=%{time_connect} t_starttransfer=%{time_starttransfer}\n" https://example.com/wp-admin/admin-ajax.php?action=some_action
code=504 t_total=60.012 t_connect=0.012 t_starttransfer=60.001

Що це означає: Жорсткий відсічник у 60 секунд співпадає з Nginx fastcgi_read_timeout або таймаутом проксі. Бекенд, ймовірно, працював далі або його вбили.

Рішення: Перевірте логи Nginx/Apache у цей час, потім логи PHP‑FPM на предмет термінації, потім логи застосунку, щоб з’ясувати, що він робив.

Task 14: Inspect Nginx error log for upstream timeouts

cr0x@server:~$ sudo tail -n 20 /var/log/nginx/error.log
2025/12/27 10:16:02 [error] 2281#2281: *991 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 203.0.113.10, server: example.com, request: "POST /wp-admin/admin-ajax.php HTTP/2.0", upstream: "fastcgi://unix:/run/php/php8.2-fpm.sock", host: "example.com"

Що це означає: Nginx припинив чекати на PHP‑FPM. Це не обов’язково означає, що PHP помер — лише те, що проксі перестав чекати.

Рішення: Якщо ця кінцева точка дійсно довга — переробіть її в асинхронну/фонову. Якщо ні — знайдіть вузьке місце всередині PHP/БД.

Task 15: Confirm PHP-FPM termination settings

cr0x@server:~$ sudo grep -R "request_terminate_timeout" -n /etc/php/8.2/fpm/pool.d /etc/php/8.2/fpm/php-fpm.conf
/etc/php/8.2/fpm/pool.d/www.conf:392:request_terminate_timeout = 60s

Що це означає: PHP‑FPM вбиватиме запити через 60с, навіть якщо max_execution_time PHP більше.

Рішення: Вирівняйте налаштування. Якщо тримаєте 60с у FPM — тримайте PHP не більшим за це або рівним. Краще зменшувати і виносити довгі задачі за межі запитів.

Task 16: Check if opcode cache is enabled (performance sanity)

cr0x@server:~$ php -i | grep -E '^opcache.enable|^opcache.memory_consumption'
opcache.enable => On => On
opcache.memory_consumption => 128 => 128

Що це означає: Opcache увімкнено (добре). Якби він був вимкнений, PHP постійно компілював би скрипти, що збільшує латентність і таймаути під навантаженням.

Рішення: Якщо opcache вимкнений у FPM — увімкніть його. Це одна з небагатьох «оптимізацій», яка нудна, але правильна.

Безпечні виправлення, які не створюють нових проблем

Є дві категорії виправлень:

  1. Підвищити ліміти (таймаути, пам’ять) щоб зупинити негайні помилки.
  2. Зменшити або пришвидшити роботу, щоб вона вкладалася в розумні ліміти.

Підвищення лімітів іноді необхідне, особливо для імпортів чи бекапів. Але для рутинних сторінок і дій в адмінці це часто пластир. Пластирі — для порізів, а не для артеріальної кровотечі.

Виправлення 1: Збільшити max_execution_time (тільки з наміром)

Якщо у вас справді легітимний довготривалий запит (великий імпорт, складний звіт), можна підвищити max_execution_time. Робіть це в правильному місці:

  • PHP‑FPM: редагуйте /etc/php/8.2/fpm/php.ini або налаштування пулу.
  • Apache mod_php: редагуйте /etc/php/8.2/apache2/php.ini.
  • CLI WP‑CLI: редагуйте /etc/php/8.2/cli/php.ini (відрізняється від web).

Потім перезавантажте сервіси. Не перезавантажуйте весь сервер без потреби.

Виправлення 2: Вирівняти таймаути в стеку (proxy/web/FPM/PHP)

Невідповідні таймаути створюють фантомні відмови: клієнт бачить 504, тоді як PHP працює далі, або PHP вбито, а Nginx все ще чекає.

Практичне вирівнювання для типової установки WordPress:

  • Користувацькі запити: обмежуйте 30–60 секунд (часто менше).
  • Адмін‑дії: 60–120 секунд, якщо надзвичайно необхідно, але краще уникати.
  • Імпорти/експорти/бекапи: не робіть їх через веб‑запит; використовуйте WP‑CLI або фон.

Коли підвищуєте таймаути — робіть це локально. Використовуйте location‑блоки для кінцевих точок імпорту, якщо потрібно. Не збільшуйте глобально таймаути для всього і потім дивуйтеся, чому воркер‑пул заповнений зомбі‑процесами.

Виправлення 3: Перенести важку адмінську роботу в WP‑CLI

Веб‑запити — погане місце для важкої роботи. Вони керуються користувачем, вразливі й обмежені таймаутами проксі. WP‑CLI дає можливість довготривалих операцій без проксі‑таймаутів і зручніше логувати.

Приклади: оновлення плагінів, пошук/заміна в БД, великі імпорти, перебудова кешу.

Виправлення 4: Замінити «WP‑Cron через трафік» на системний cron

Це одне з найкращих оновлень для надійності.

  • Вимкніть тригерення WP‑Cron при кожному запиті.
  • Запустіть wp cron event run --due-now з системного cron кожну хвилину або кожні п’ять хвилин.

Це стабілізує час виконання завдань і зменшить раптові сплески під час завантаження сторінок.

Виправлення 5: Усунути автозавантажувальний бліт (autoload bloat)

Autoload‑bloat — це самозавдана латентність. Безпечний підхід:

  1. Виміряйте загальний розмір autoload.
  2. Визначте найбільших порушників.
  3. Для кожного — вирішіть, чи безпечно встановити autoload=’no’ або перемістити в transient/object cache.
  4. Тестуйте в staging. Деплойте з планом відкату.

Виправлення 6: Виправити повільні запити й додати індекси (акуратно)

Індекси — не магія; це компроміс. Але відсутність індексів у таблицях плагінів часто призводить до таймаутів.

Не додавайте індекси сліпо в продакшні під час пікового трафіку. Додавання індексу може блокувати таблиці або принаймні збільшити I/O під час побудови. Плануйте.

Виправлення 7: Підібрати розміри PHP‑FPM воркерів (налаштування pm)

Більше воркерів — не завжди більша пропускна спроможність. Надто багато воркерів може:

  • вичерпати пам’ять і викликати swap (повільніше → ще повільніше),
  • збільшити кількість з’єднань до БД і контенцію,
  • посилити дисковий I/O‑конфлікт.

Почніть з реальних даних: CPU‑ядра, пам’ять, середня вартість запиту. Потім встановіть pm.max_children так, щоб система працювала без swap.

Виправлення 8: Вирішити питання зі сховищем і файловою системою

Як інженер сховищ, ось частина, яку пропускають, бо це не налаштування WordPress:

  • Якщо латентність диска висока — оптимізуйте сховище. Жодна PHP‑налаштування не вирішить повільні диски.
  • Перевірте, чи не заповнений диск: при повному диску все стає дивним.
  • Мережеві томи («дешеві» cloud‑диски) можуть мати непередбачувану латентність під обмеженнями burst.

Жарт короткий #2: Встановити max_execution_time = 0 — як зняти ремені безпеки, бо вони мнуть сорочку.

Три корпоративні міні‑історії з практики

Міні‑історія №1: Інцидент через неправильне припущення

Компанія мала «простий» магазин на WooCommerce. Не величезний, але достатній, щоб збої генерували Slack‑повідомлення в агресивному тоні. Раптом почався сплеск відмов при оформленні: клієнти бачили нескінченне завантаження, а потім помилки. Логи Nginx показували 504.

Розробник на чергуванні припустив, що це PHP‑таймаут, і підняв max_execution_time з 30 до 120. Помилки посилилися. Це не рідкість: довші таймаути означають, що більше запитів накопичуються. Конкуренція зростає, тиск на БД росте, і все гальмує далі. Система перетворилася на повільний затор.

Неправильне припущення було в тому, що «повідомлення про таймаут» означає «PHP треба більше часу». Насправді PHP чекав відповіді. SHOW PROCESSLIST вказав на кілька сесій, заблокованих метаданами через ALTER TABLE. Оновлення плагіна запустило ALTER TABLE у робочий час. ALTER утримував блоки, на які чекали запити оформлення замовлення, і PHP‑воркери чекали, поки не досягали проксі‑таймауту.

Вирішення не було «більше часу». Це було: зупинити зміну схеми, очистити чергу й запланувати зміни схеми у вікні обслуговування з перевіркою в staging на копії production даних.

Постмортем був прямим: таймаути — симптом. Блокування — хвороба. Команда оновила свій рукопис: перевіряти блокування БД перед тим, як чіпати таймаути.

Міні‑історія №2: Оптимізація, що дала зворотний ефект

Інша організація мала контент‑насичений сайт і агресивне кешування. Вони хотіли пришвидшити адмінку, тож налаштували PHP‑FPM на «максимальну пропускну здатність»: значно підвищили pm.max_children і знизили час очікування, щоб «швидко відкидати» помилки. На тихому стенді результати виглядали відмінно.

Продакшн думав інакше. Під час пікової роботи редактори масово завантажували зображення і редагували пости. Дисковий I/O підскочив через обробку зображень і записів кешу. Надто багато PHP‑FPM воркерів призвело до збільшення черги на диск, підвищення iowait, і кожен запит сповільнився. Запити почали таймаутити — не тому, що кожен окремий запит важкий, а тому, що система була перевантажена.

Оптимізація дала зворотний ефект, бо передбачала, що вузьке місце — CPU. Насправді вузьке місце — сховище, а множення воркерів лише посилило конкуренцію. Вони, по суті, побудували підсилювач конкурентності поверх повільного диска.

Вирішення було контрінтуїтивним: зменшити конкурентність PHP‑FPM до рівня можливостей I/O, правильно увімкнути opcache і перемістити обробку зображень в асинхронну чергу. Додали моніторинг латентності диска як важливий SLO сигнал поряд із CPU і пам’яттю.

Після цього таймаути переважно зникли. Не тому, що дали більше часу. А тому, що перестали починати більше роботи, ніж сховище могло опрацювати.

Міні‑історія №3: Нудна, але правильна практика, що врятувала

SaaS‑маркетингова команда тримала WordPress як «вхідні двері». Вони не були технічними, але мали одного SRE, який наполягав на нудних звичках: збереження логів, дашборди, staging, і щомісячний «день оновлень» з відпрацюванням відкатів.

Якось у вівторок оновлення плагіна внесло регресію: новий віджет адмін‑панелі робив повільний зовнішній API‑виклик на кожному завантаженні адмінки. Редактори почали бачити помилки про максимальний час виконання. Це могло перерости в багатогодинний firefight.

Але їхні нудні практики спрацювали. SRE порівняв графіки до/після: зовнішня латентність стрибнула точно в момент оновлення плагіна. PHP‑логи показали таймаут у WordPress HTTP‑функціях. Вони відкотили плагін за задокументованою процедурою. Помилки впали відразу.

Потім вони розгорнули тимчасове рішення: вимкнули віджет, додали короткий таймаут і кешування для зовнішнього виклику та тиснули на вендора з виправленням. Історія закінчилася рядком, який хоче почути кожен опсер: «Клієнти майже не помітили.»

Нудна практика не була магією. Це була наявність спостереження і плану відкату до того, як це знадобилося.

Типові помилки: симптом → корінна причина → виправлення

Ці патерни я бачу повторно. Якщо впізнали один — можна пропустити героїчне дебагування і йти одразу до ймовірної причини.

1) Симптом: 504 Gateway Timeout у браузері, немає PHP‑фаталу

Корінна причина: Таймаут Nginx/Apache/проксі менший за PHP/PHP‑FPM або бекенд застряг і проксі відмовився чекати.

Виправлення: Перевірте лог Nginx на «upstream timed out». Узгодьте fastcgi_read_timeout з налаштуваннями термінації PHP‑FPM. Потім знайдіть, чому бекенд повільний (блокування БД, диск, CPU), замість бездумного підняття таймауту проксі.

2) Симптом: «Maximum execution time of 30 seconds exceeded» під час оновлення плагінів

Корінна причина: Апдейтер плагіна робить файлові операції повільно (права, повільний диск), зовнішні виклики або розпакування під навантаженням.

Виправлення: Робіть оновлення через WP‑CLI у час низького трафіку. Переконайтесь, що права на файлову систему коректні, щоб уникнути повторних спроб. Перевірте латентність сховища.

3) Симптом: Адмінка повільна, фронтенд «переважно в порядку»

Корінна причина: Адмінки викликають більше запитів, завантажують більше плагінів, роблять зовнішні виклики або створюють звіти. Також частіше страждає від autoload‑bloat і важких хуків.

Виправлення: Профілюйте адмін‑ендпоїнти; аудитуйте плагіни, які додають віджети; зменшіть автозавантажені опції; відключіть непотрібні адмін‑функції у продакшені.

4) Симптом: Помилки концентруються під час імпортів або завантаження медіа

Корінна причина: Обробка зображень (GD/Imagick) важка по CPU й I/O; імпорти роблять великі вставки в БД і можуть викликати блокування.

Виправлення: Використовуйте WP‑CLI для імпортів; робіть пакетні операції; відвантажуйте обробку медіа або генерацію мініатюр у фон. Збільшуйте пам’ять, а не час, якщо ви стикаєтеся зі swap/OOM‑тремтінням.

5) Симптом: Випадкові таймаути, особливо при низькому трафіку

Корінна причина: WP‑Cron‑накопичення: завдання виконуються нерегулярно й тоді один запит запускає багато прострочених задач.

Виправлення: Вимкніть WP‑Cron і налаштуйте системний cron. Перевірте перетікання робіт і зменшіть частоту при потребі.

6) Симптом: Після «підвищення продуктивності» таймаути стали гірші

Корінна причина: Більше PHP‑FPM воркерів або операцій запису кешу посилили контенцію (БД, диск). Або оптимізація прибрала кеш для адмін‑запитів, збільшивши навантаження.

Виправлення: Відкотіть до попередньої відомо‑дієвої конфігурації. Підібрати конкурентність під реальні ресурси. Міряйте iowait і блокування БД. Робіть по одній зміні за раз.

7) Симптом: Таймаути лише на одній конкретній сторінці/действії

Корінна причина: Один ендпоїнт плагіна виконує дороге завдання (віддалені виклики, великий запит, повне сканування таблиці).

Виправлення: Вимкніть плагін для підтвердження; потім виправте або замініть його. Додайте індекси, якщо потрібно. Перенесіть роботу в фон.

Чеклісти / покроковий план

Чекліст A: Зробити сайт стабільним за 15 хвилин (тріаж)

  1. Підтвердіть режим відмови: PHP fatal vs 504 vs 502. Користуйтеся логами, не інтуїцією.
  2. Перевірте навантаження: top і iostat. Якщо iowait високий — не чіпайте таймаути.
  3. Перевірте блокування БД: SHOW PROCESSLIST. Якщо бачите metadata lock — припиніть зміни схеми і усуньте блокувальники.
  4. Ідентифікуйте кінцеву точку: Який URL/дія це викликає? Відтворіть з curl -w.
  5. Мітинг: вимкніть проблемний плагін або функцію, якщо це явно причина; або перенаправте важку роботу на WP‑CLI.

Чекліст B: Зробити виправлення стійким (на наступний день)

  1. Вирівняйте таймаути між шарами: proxy/web/FPM/PHP. Задокументуйте.
  2. Виправіть autoload‑bloat: виміряйте, знайдіть порушників, виправте з тестами.
  3. Виправіть повільні запити: тимчасово ввімкніть slow query logging; додавайте індекси, де виправдано.
  4. Впровадьте системний cron: відключіть WP‑Cron і заплануйте правильно.
  5. Підійміть PHP‑FPM: налаштуйте pm.max_children згідно пам’яті й CPU.
  6. Встановіть безпечний pipeline оновлень: staging‑перевірка, вікна обслуговування, процедура відкату.

Чекліст C: Коли піднімати таймаути прийнятно

  • Великі разові імпорти, які ви контролюєте (краще робити через WP‑CLI).
  • Звітність для адмінки з обмеженою конкурентністю, де ви також обмежуєте доступ.
  • Бекапи/експорти виконуються через CLI або фон, а не через веб‑запити.

Якщо ви піднімаєте таймаути для рутинних сторінок або оформлення замовлення — швидше за все ви лікуєте симптом.

Цікаві факти й контекст (історія важлива)

  • Обмеження часу виконання в PHP існує декілька десятиліть і було створене, щоб захистити шаред‑хостинг від неконтрольованих скриптів.
  • «Крон» у WordPress — не системний cron; це псевдо‑cron, який тригериться веб‑трафіком, ідея вражає — поки не починаєш на ній опиратися.
  • Класичний дефолт 30 секунд поширений, бо багато веб‑серверів історично вважали «людина не чекатиме довше», і це захищає від ресурсного блокування типу slow‑loris.
  • Шари таймаутів задумано як захист: кожен шар захищає себе незалежно. Ось чому виправлення одного таймауту часто відкриває наступний.
  • Autoload‑опції завантажуються на кожен запит, і таблиця опцій може стати місцем поглинання продуктивності, коли плагіни ховають там великі блоби.
  • Багато адмін‑дій WordPress синхронні (оновлення, встановлення, імпорт), що операційно незручно в продукції.
  • PHP‑FPM додав контролі на рівні пулу (наприклад таймаути термінації), частково щоб не дозволяти одному пулу монополізувати сервер.
  • Блокування метаданих БД стали помітнішою проблемою з ростом сайтів, коли «просто запустіть ALTER TABLE» починає конфліктувати з постійним трафіком.
  • Object caching еволюціонував як обхід для повторного завантаження опцій і запитів, але він може маскувати бліт, якщо ви не вимірюєте основу.

FAQ

1) Чи просто підняти max_execution_time до 300?

Тільки якщо можете пояснити, яка робота потребує 300 секунд і чому вона має виконуватися в веб‑запиті. Для звичайних сторінок це помилка, яку треба виправити, а не ліміт для підняття.

2) Чому це відбувається тільки під час оновлення плагінів/тем?

Оновлення вимагають завантаження, перевірки, розпакування архівів і запису багатьох файлів. На повільних дисках або при обмеженому CPU це може перевищити 30–60 секунд. Робіть оновлення через WP‑CLI у час низького трафіку та перевіряйте права файлової системи.

3) Я підняв max_execution_time, але все одно отримую 504. Чому?

Бо таймаут проксі/веб‑сервера (Nginx fastcgi_read_timeout, Apache Timeout, idle timeout балансувальника) швидше за PHP. Узгодьте їх, а потім виправляйте справжню повільність.

4) У чому різниця між max_execution_time і request_terminate_timeout?

max_execution_time контролює PHP. request_terminate_timeout контролює PHP‑FPM і може вбити запит незалежно від PHP‑налаштувань. Якщо вони розходяться, на практиці спрацьовує менший з них.

5) Чи може проблема бази даних спричинити помилку виконання PHP?

Так. PHP «час виконання» включає час очікування відповіді бази. Повільний запит або блокування можуть витратити весь бюджет, хоча PHP фактично «простає».

6) Чи допоможе збільшення лиміту пам’яті PHP при помилках часу виконання?

Іноді. Якщо запит повільний через thrashing пам’яті або swap — більше пам’яті може зменшити час стінного виконання. Але якщо повільність через блокування БД або дискову латентність — пам’ять не врятує.

7) Як зрозуміти, чи причетний WP‑Cron?

Шукайте таймаути на звичайних сторінках, які корелюють з запуском cron‑хуків, простроченими подіями або частими кастомними хуками. Використайте WP‑CLI, щоб перелікувати події і виявити важкі завдання. Якщо прострочених подій накопичується багато — перейдіть на системний cron.

8) Чи безпечно відключити плагін для виправлення таймаутів?

Під час інциденту — так, якщо ви розумієте бізнес‑наслідки. Вимкніть плагін, що спричиняє таймаут, щоб відновити сервіс, а потім розслідуйте. Для WooCommerce/платіжних плагінів будьте обережні: вимкнення може зламати чекаут. Краще використовувати feature flags або точкове відключення.

9) Чому це частіше трапляється на шаред‑хостингу?

Шаред‑хостинг зазвичай має суворіші ліміти, «шумних сусідів», повільніше сховище і менший контроль над PHP‑FPM та проксі. Ви частіше обмежені низькими дефолтами і обмеженою видимістю.

10) Яке найправильніше з точки зору SRE рішення для довготривалих задач?

Зробіть їх асинхронними: поставте в чергу, повертайтеся швидко, і обробляйте у фоні з повторними спробами, таймаутами й ідемпотентністю. WordPress може робити це неідеально, але це краще, ніж фіксація веб‑воркерів.

Висновок: реальні кроки, які варто виконати

Якщо запам’ятати одне: помилки часу виконання — це зазвичай сигнал сервера «цей запит занадто великий або система занадто повільна». Безпечний шлях — довести, який саме варіант.

  1. Класифікуйте таймаут (PHP vs проксі vs FPM) за логами.
  2. Запустіть швидку діагностику: CPU/iowait, блокування БД, відтворення кінцевої точки.
  3. Виправте вузьке місце (autoload‑bloat, повільні запити, латентність сховища, перевантажений FPM) перед підвищенням лімітів.
  4. Перенесіть важку роботу з веб‑запитів: WP‑CLI, системний cron, бекграунд‑джоби.
  5. Узгодьте таймаути навмисно, документуйте їх і тримайте їх суворими для шляхів, орієнтованих на користувача.

Зробіть це — і «Max execution time exceeded» повернеться до своєї ролі: страховочного обмеження, яке ви рідко переступаєте, а не постійної проблеми.

← Попередня
ZFS zdb -C: Читання конфігурації пулу безпосередньо з диска
Наступна →
Обмеження SMTP: доведіть, що це провайдер, і адаптуйтеся правильно

Залишити коментар