Action Scheduler в WooCommerce: Настройка очередей и решение проблем

Когда в магазине на WooCommerce не создаются заказы, не отправляются письма или не продлеваются подписки — в 80% случаев причина кроется в Action Scheduler. Это асинхронная система очередей, встроенная в WooCommerce, которая обрабатывает фоновые задачи: создание заказов, отправку вебхуков, генерацию отчётов, продление подписок.

В отличие от стандартного wp-cron.php, который хранит задачи в опциях WordPress, Action Scheduler использует отдельные таблицы в базе данных и умеет обрабатывать десятки тысяч задач параллельно. Но при неправильной настройке или высокой нагрузке система начинает захлёбываться: задачи висят в статусе «in-progress» часами, а таблица actionscheduler_actions разрастается до миллионов записей.

Что такое Action Scheduler и как он работает

Action Scheduler (AS) — это библиотека для планирования и выполнения отложенных задач в WordPress. Её разработала команда Automattic (создатели WooCommerce), и сегодня она используется в десятках популярных плагинов:

  • WooCommerce — обработка заказов, отправка email, обновление статистики
  • WooCommerce Subscriptions — продление подписок, списание платежей
  • WooCommerce Payments — обработка транзакций и рефандов
  • Yoast SEO — генерация sitemap и индексация
  • Rank Math — фоновая проверка ссылок и SEO-аудит

Архитектура Action Scheduler:

  • Таблица actionscheduler_actions — хранит все задачи с их статусами (pending, in-progress, complete, failed, canceled)
  • Таблица actionscheduler_groups — группирует задачи по плагинам
  • Таблица actionscheduler_logs — логи выполнения задач
  • Таблица actionscheduler_claims — блокировки для параллельной обработки

По умолчанию AS использует wp-cron.php для триггера обработки. Это создаёт две проблемы: задачи не выполняются на сайтах с низкой посещаемостью, а на нагруженных сайтах wp-cron не справляется с объёмом.

Шаг 1: Диагностика текущего состояния

Перед оптимизацией нужно понять, насколько плохо работает система. В админке WordPress перейдите в Инструменты → Запланированные действия (или WooCommerce → Статус → Запланированные действия).

Что проверять:

  • Статус «In-Progress»: если таких задач больше 10-20 и они «висят» часами — система зависла
  • Статус «Pending»: если в очереди тысячи задач, которые не обрабатываются — не хватает worker’ов
  • Статус «Failed»: ошибки выполнения, требующие разбора
  • Количество записей: если в таблице больше 100 000 записей — требуется очистка

Проверка через SQL-запросы:

Для точной диагностики подключаемся к базе данных и выполняем запросы:

-- Подсчёт задач по статусам
SELECT status, COUNT(*) as count
FROM wp_actionscheduler_actions
GROUP BY status;

-- Поиск "зависших" задач (in-progress больше 1 часа)
SELECT hook, COUNT(*) as count, MIN(claim_id) as claim_id
FROM wp_actionscheduler_actions
WHERE status = 'in-progress'
AND last_attempt_gmt < DATE_SUB(NOW(), INTERVAL 1 HOUR)
GROUP BY hook;

-- Размер таблиц
SELECT 
    table_name AS 'Таблица',
    ROUND(((data_length + index_length) / 1024 / 1024), 2) AS 'Размер (MB)',
    table_rows AS 'Записей'
FROM information_schema.TABLES
WHERE table_schema = 'custom_code_db'
AND table_name LIKE 'wp_actionscheduler%';

Шаг 2: Очистка старых записей

По умолчанию Action Scheduler хранит завершённые и отменённые задачи 30 дней. На крупных магазинах за месяц накапливаются сотни тысяч записей, что замедляет выборки.

Уменьшение периода хранения:

Добавляем в functions.php темы или в кастомный плагин:

// Хранить завершённые задачи только 7 дней (вместо 30)
add_filter( 'action_scheduler_retention_period', function() {
    return 7 * DAY_IN_SECONDS;
} );

// Хранить отменённые задачи только 1 день
add_filter( 'action_scheduler_cancelled_retention_period', function() {
    return 1 * DAY_IN_SECONDS;
} );

Ручная очистка через WP-CLI:

Если таблица уже переполнена, встроенная очистка может не сработать из-за таймаутов. Используем WP-CLI для прямой очистки:

# Удаление всех завершённых задач старше 7 дней
wp action-scheduler clean --status=complete --before=7days

# Удаление всех отменённых задач
wp action-scheduler clean --status=cancelled

# Удаление всех failed-задач (если они накопились)
wp action-scheduler clean --status=failed

Оптимизация таблицы через SQL:

После массового удаления записей таблица остаётся раздутой. Оптимизируем её:

wp db query "OPTIMIZE TABLE wp_actionscheduler_actions"
wp db query "OPTIMIZE TABLE wp_actionscheduler_logs"

Шаг 3: Увеличение производительности обработки

По умолчанию Action Scheduler обрабатывает по 20 задач за раз с интервалом в минуту. Для магазинов с большим количеством заказов этого недостаточно.

Увеличение размера батча:

// Обрабатывать по 100 задач за один запуск
add_filter( 'action_scheduler_queue_runner_batch_size', function() {
    return 100;
} );

// Уменьшить интервал между запусками до 30 секунд
add_filter( 'action_scheduler_queue_runner_interval', function() {
    return 30;
} );

Настройка конкурентной обработки (concurrency):

Начиная с версии 3.0, Action Scheduler поддерживает параллельную обработку задач через систему claims:

// Разрешить до 5 одновременных worker'ов
add_filter( 'action_scheduler_queue_runner_concurrent_batches', function() {
    return 5;
} );

// Увеличить время жизни claim до 10 минут
add_filter( 'action_scheduler_claim_ttl', function() {
    return 10 * MINUTE_IN_SECONDS;
} );

При увеличении concurrency критично иметь правильно настроенный пул процессов PHP-FPM, иначе сервер начнёт захлёбываться от нагрузки. Детальный расчёт pm.max_children приведён в руководстве по оптимизации PHP-FPM.

Шаг 4: Подключение системного cron

Самая важная настройка — отвязать Action Scheduler от wp-cron.php и запускать его через системный cron. Это гарантирует выполнение задач даже при отсутствии трафика и убирает нагрузку с фронтенда.

Отключение встроенного триггера:

// Отключаем стандартный триггер AS через wp-cron
add_filter( 'action_scheduler_run_queue', '__return_false' );

Настройка системного cron:

Добавляем задачу в crontab для запуска каждые 1 минуту:

sudo crontab -u www-data -e

# Запуск обработки очереди каждую минуту
* * * * * cd /var/www/wordpress && /usr/local/bin/wp action-scheduler run --batch-size=100 > /dev/null 2>&1

# Запуск очистки старых записей раз в день (в 4:00)
0 4 * * * cd /var/www/wordpress && /usr/local/bin/wp action-scheduler clean > /dev/null 2>&1

Флаг --batch-size=100 должен совпадать со значением из фильтра action_scheduler_queue_runner_batch_size, чтобы параметры были консистентными.

Шаг 5: Решение проблемы "зависших" задач

Частая ситуация: задача переходит в статус "in-progress", но никогда не завершается. Это происходит при падении PHP-процесса (из-за нехватки памяти или таймаута) или при блокировке claim.

Автоматическое освобождение зависших задач:

// Считать задачу зависшей, если она выполняется больше 5 минут
add_filter( 'action_scheduler_timeout_period', function() {
    return 5 * MINUTE_IN_SECONDS;
} );

// Уменьшить время до освобождения claim
add_filter( 'action_scheduler_failure_period', function() {
    return 10 * MINUTE_IN_SECONDS;
} );

Ручное освобождение через SQL:

Если в таблице много задач со статусом "in-progress", которые висят давно, освобождаем их вручную:

-- Возвращаем "зависшие" задачи в статус pending
UPDATE wp_actionscheduler_actions
SET status = 'pending', claim_id = 0
WHERE status = 'in-progress'
AND last_attempt_gmt < DATE_SUB(NOW(), INTERVAL 10 MINUTE);

-- Сбрасываем все claims (если их много)
UPDATE wp_actionscheduler_claims
SET date_created_gmt = NOW()
WHERE date_created_gmt < DATE_SUB(NOW(), INTERVAL 30 MINUTE);

Шаг 6: Переход на HPOS (High-Performance Order Storage)

Начиная с WooCommerce 8.0, появилась функция HPOS, которая переносит хранение заказов из стандартной таблицы wp_posts в отдельную таблицу wp_wc_orders. Это напрямую влияет на производительность Action Scheduler.

Включение HPOS:

В админке: WooCommerce → Настройки → Продвинутые → Функции → включите "High-Performance Order Storage".

# Проверка статуса через WP-CLI
wp wc hpos status

# Синхронизация существующих заказов
wp wc hpos sync

# Окончательное включение
wp wc hpos enable

HPOS снижает нагрузку на таблицы Action Scheduler в 3-5 раз, так как больше не нужно синхронизировать метаданные заказов через общие таблицы WordPress.

Шаг 7: Мониторинг через WP-CLI

Регулярный мониторинг состояния очереди предотвращает большинство проблем:

# Список всех запланированных задач с группировкой
wp action-scheduler list --status=pending --format=table

# Поиск задач конкретного хука
wp action-scheduler list --hook=woocommerce_process_product_file_download_paths

# Статистика по всем статусам
wp action-scheduler list --format=count

# Запуск одной задачи для отладки
wp action-scheduler run --hook=woocommerce_deliver_webhook_async

Скрипт для ежедневной проверки:

#!/bin/bash
# /usr/local/bin/as-monitor.sh

PENDING=$(wp action-scheduler list --status=pending --format=count 2>/dev/null)
IN_PROGRESS=$(wp action-scheduler list --status=in-progress --format=count 2>/dev/null)
FAILED=$(wp action-scheduler list --status=failed --format=count 2>/dev/null)

# Отправка уведомления в Telegram при превышении порогов
if [ "$PENDING" -gt 1000 ] || [ "$IN_PROGRESS" -gt 50 ] || [ "$FAILED" -gt 10 ]; then
    curl -s -X POST https://api.telegram.org/bot$BOT_TOKEN/sendMessage \
        -d chat_id=$CHAT_ID \
        -d text="⚠️ AS Alert: pending=$PENDING, in_progress=$IN_PROGRESS, failed=$FAILED"
fi

Добавляем скрипт в cron для проверки каждые 15 минут. Интеграция с Telegram настраивается по аналогии с скриптом уведомлений о бэкапах.

Шаг 8: Использование кастомного кода с Action Scheduler

Action Scheduler — не только внутренняя система WooCommerce, но и мощный инструмент для собственных фоновых задач. Вместо создания своих cron-задач используйте AS для любых асинхронных операций.

Планирование одноразовой задачи:

// Запустить функцию через 1 час
as_schedule_single_action(
    time() + HOUR_IN_SECONDS,
    'customcode_send_notification',
    array( 'user_id' => 123, 'message' => 'Hello!' ),
    'customcode' // группа задач
);

// Обработчик
add_action( 'customcode_send_notification', function( $user_id, $message ) {
    wp_mail( get_user_by( 'id', $user_id )->user_email, 'Уведомление', $message );
}, 10, 2 );

Планирование повторяющейся задачи:

// Запускать каждый день в полночь
as_schedule_cron_action(
    time(),
    '0 0 * * *',
    'customcode_daily_report',
    array(),
    'customcode'
);

Обработка большого массива данных (batch):

function customcode_process_users_batch( $user_ids ) {
    foreach ( $user_ids as $user_id ) {
        as_enqueue_async_action(
            'customcode_process_single_user',
            array( $user_id ),
            'customcode'
        );
    }
}

// Вызов для 10 000 пользователей
$all_users = get_users( array( 'fields' => 'ID' ) );
customcode_process_users_batch( $all_users );

Такой подход автоматически использует всю инфраструктуру Action Scheduler: параллельную обработку, retry-механику, логирование и мониторинг.

Чек-лист оптимизации Action Scheduler

  • ✅ Период хранения завершённых задач снижен до 7 дней
  • ✅ Размер батча увеличен до 50-100
  • ✅ Включена конкурентная обработка (3-5 worker'ов)
  • ✅ Системный cron запускает wp action-scheduler run каждую минуту
  • ✅ Таблицы оптимизированы через OPTIMIZE TABLE
  • ✅ Включён HPOS в WooCommerce
  • ✅ Настроен мониторинг с уведомлениями в Telegram

Решение частых проблем

Таблица actionscheduler_actions не очищается

Стандартная очистка выполняется через wp-cron, который может не запускаться. Запускаем вручную: wp action-scheduler clean. Если таблица больше 10 ГБ, разделяем удаление на несколько запусков с параметром --before, чтобы избежать блокировок.

Задачи WooCommerce не выполняются после переноса сайта

При миграции через search-replace могут повредиться сериализованные данные в таблице задач. Выполняем: wp action-scheduler clean --status=failed и перезапускаем очередь. Если задачи продолжают падать — проверяем логи через wp action-scheduler list --hook=woocommerce.

Высокое потребление CPU при обработке очереди

Слишком большой batch-size или слишком много concurrent workers нагружают сервер. Снижаем до batch_size=25 и concurrency=2. Если нагрузка остаётся высокой — проверяем slow_query_log, как описано в руководстве по оптимизации MySQL, часто проблема в медленных SQL-запросах внутри задач.

Задачи дублируются и выполняются несколько раз

Это признак проблем с claim-системой. Проверяем, что не запущено несколько экземпляров обработчика одновременно (например, wp-cron + системный cron + плагин Action Scheduler). Оставляем только один триггер.

Итог

Action Scheduler — критически важный компонент современного WordPress, особенно для WooCommerce-магазинов. Правильная настройка системы очередей превращает её из источника проблем в надёжный инструмент для асинхронной обработки. Комбинация системного cron, оптимизированных батчей и HPOS даёт 5-10 кратный прирост производительности при обработке фоновых задач.