WordPress выполняет свои фоновые задачи — отложенную публикацию, рассылку, очистку, прогрев кэша — «попутно»: при обычном запросе страницы он проверяет, не пора ли что-то запустить. На сайте, где страницы отдаются из кэша nginx, этот механизм почти перестаёт работать: запрос страницы до PHP больше не доходит.

Это вторая часть цикла «WordPress на кэшируемом сайте».

Почему встроенный крон не годится

  • Запросов к PHP мало и они неравномерны. Страница из кэша PHP не трогает. До WordPress доходят только промахи кэша, ajax-запросы и работа в админке. Ночью может не быть ни одного — и отложенная на 3:00 статья выйдет утром.
  • Запуск идёт запросом сайта к самому себе. WordPress стучится на свой же адрес /wp-cron.php. В контейнере Docker запрос на собственный домен у нас не проходит вовсе (cURL error 7): адрес снаружи, а обратно в ту же машину маршрута нет. Задачи просто не стартуют, и никаких ошибок на экране.
  • Длинные задачи режет лок. Встроенный замок крона живёт 60 секунд (WP_CRON_LOCK_TIMEOUT). Задача дольше минуты рискует запуститься второй раз параллельно себе — для прогрева кэша или рассылки нужен свой замок.

Как правильно: системный крон раз в минуту

  1. выключить запуск «попутно» в wp-config.php;
  2. запускать wp-cron.php внешним планировщиком раз в минуту — от того же пользователя, под которым работает PHP;
  3. проверить по журналу планировщика, что задание идёт и завершается без ошибок.
// wp-config.php
define('DISABLE_WP_CRON', true);

У нас планировщик — отдельный контейнер, который читает метки контейнеров сайта. Задание описывается метками в compose-файле сайта, примерно так:

labels:
  - "ofelia.enabled=true"
  - "ofelia.job-exec.site-wpcron.schedule=@every 1m"
  - "ofelia.job-exec.site-wpcron.command=php -f /var/www/wp-cron.php"
  - "ofelia.job-exec.site-wpcron.user=www-data"

С обычным crontab на сервере это одна строка: раз в минуту php -f wp-cron.php от пользователя веб-сервера.

Грабля 1: крон от root

Если задание запускается от root, всё, что оно создаёт — миниатюры, файлы кэша, логи, — принадлежит root. Потом PHP под своим пользователем не может эти файлы перезаписать: Permission denied, картинка не генерируется, кэш не обновляется.

Масштаб, который мы видели: на одном сайте за час набежало 3811 файлов и каталог от root и 90 ошибок Permission denied; на соседнем — 140 523 файла кэша миниатюр от root, накопленные за долгое время незаметно.

Планировщик читал метки только при своём старте. Контейнер сайта пересоздали с меткой «запускать от www-data», а крон ещё пять часов ходил старой командой от root, пока не перезапустили сам планировщик. После любой правки задания — перезапуск планировщика и проверка по его журналу.

И обратная ошибка: дописать в команду runuser -u www-data -- «для надёжности». Если планировщик уже запускает задание от www-data, runuser под обычным пользователем падает за 92 мс, и задание не выполняется вовсе.

Проверка, а не вера на слово:

# кто запускает крон
ps -eo user,args | grep wp-cron
# сколько файлов кэша не у веб-пользователя (должно быть 0)
find wp-content/cache ! -user www-data | wc -l

Грабля 2: зонды будят крон

Любой служебный скрипт, который подключает wp-load.php, — проверка, миграция, замер — сам запускает задачи крона, если «попутный» запуск не выключен. Замер, сделанный «посмотреть очередь», выполнит эту очередь. Поэтому в каждом таком скрипте первой строкой:

define('DISABLE_WP_CRON', true);
require '/var/www/wp-load.php';

Что ещё стоит знать про одноразовые события

wp_schedule_single_event молча не ставит событие, если такое же уже назначено в пределах 10 минут, — подробно в 36-й части цикла про кэш страниц. А очередь, которая перепланирует себя только при успешном прогоне, однажды встаёт навсегда — 35-я часть.

Отложенные статьи выходят не вовремя, а задачи «иногда не срабатывают»?

Пришлите адрес — проверю, кто и как запускает крон WordPress на вашем сервере.

· полезно?
АЧ
Алексей Чикун веб-разработчик, команда TRIOHEAD

Делаю сайты и сервисы целиком: от вёрстки до сервера. Пишу о том, что проверил на своих проектах.

Комментарии

// загружаю комментарии…

Комментарии публикуются после проверки. Ссылки и HTML не принимаются.