Перейти к содержимому

Очередь писем - фоновая отправка, порции, повторы, чистка

Разбираемся с очередью писем: почему отправка не мгновенная, как подобрать размер порции под ограничения провайдера и что делать с зависшими записями.

Что нужно знать заранее

Вызов отправки письма кладёт запись в очередь, а не отдаёт письмо получателю сразу. Разбирает очередь фоновая задача, и между вызовом и почтовым ящиком проходит от секунд до нескольких минут.

Очередь спасает страницу от ожидания почтового сервера. Отправка сотни писем прямо в запросе покупателя растянула бы оформление заказа на десятки секунд и всё равно упёрлась бы в ограничения провайдера.

У любого почтового сервиса есть предел писем в минуту и в сутки. Превышение предела заканчивается отказами и временной блокировкой отправителя, поэтому размер порции подбирают под конкретный тариф.

Шаги

  1. Посмотреть текущее состояние очереди: сколько записей ждёт и как давно.
  2. Убедиться, что фоновая задача разбора очереди выполняется по расписанию.
  3. Подобрать размер порции отправки под ограничения почтового сервиса проекта.
  4. Разобраться с зависшими записями и понять, почему они не ушли получателю.
  5. Настроить чистку старых записей очереди, чтобы таблица не росла бесконечно.

Решение

Смотрим состояние очереди писем:

SELECT SUCCESS_EXEC, COUNT(*) AS cnt, MIN(DATE_INSERT) AS oldest
FROM b_event GROUP BY SUCCESS_EXEC;
-- признак успешной отправки делит очередь на отправленное и ожидающее

Растущее число неотправленных записей означает, что очередь не разбирается. Чаще всего это выключенные агенты либо ошибка почтового сервера на каждой попытке отправки письма.

Проверяем, работает ли разбор очереди:

$rs = \CAgent::GetList(['ID' => 'DESC'], ['MODULE_ID' => 'main', 'ACTIVE' => 'Y']);
while ($row = $rs->Fetch()) {
printf("%-40s следующий запуск: %s\n", $row['NAME'], $row['NEXT_EXEC']);
}
// время следующего запуска в прошлом означает, что задания стоят

Отправляем порциями со своей стороны:

foreach (array_chunk($recipients, 50) as $i => $chunk) {
foreach ($chunk as $email) {
\CEvent::Send('VENDOR_NEWS', SITE_ID, ['EMAIL' => $email], 'N');
}
sleep(2); // пауза между порциями держит нас в пределах тарифа провайдера
}

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

Разбираем зависшие записи очереди:

SELECT ID, EVENT_NAME, DATE_INSERT, LID
FROM b_event WHERE SUCCESS_EXEC = 'N' AND DATE_INSERT < NOW() - INTERVAL 1 DAY LIMIT 20;
-- эти письма не ушли, и причина почти всегда одна на всю пачку

Чистим отправленные записи по расписанию:

$conn = \Bitrix\Main\Application::getConnection();
$conn->queryExecute("DELETE FROM b_event
WHERE SUCCESS_EXEC = 'Y' AND DATE_INSERT < DATE_SUB(NOW(), INTERVAL 90 DAY) LIMIT 5000");
// удаляют только отправленное и только порциями, иначе таблица блокируется надолго

Таблица очереди на магазине с рассылками растёт быстрее большинства других. Чистка порциями по расписанию решает вопрос заранее, а разовая уборка на миллионах строк надолго занимает базу данных.

Типичные проблемы

Письма уходят с задержкой в несколько минут.

Это нормальная работа очереди: отправку выполняет фоновая задача по расписанию. Мгновенная отправка возможна, но она замедляет страницу и упирается в лимиты провайдера.

Очередь растёт, а письма никому не приходят.

Фоновые задания выключены или падают с ошибкой на каждой попытке отправки. Проверяют расписание заданий и журнал почтового сервера за то же время.

Провайдер заблокировал отправку после рассылки.

Письма уходили без пауз и превысили предел писем в минуту по тарифу. Отправку ведут порциями с паузой, подобранной под ограничения сервиса.

Таблица очереди занимает больше самого каталога.

Отправленные записи копятся годами и никогда не удаляются. Старые успешно отправленные записи чистят по расписанию небольшими порциями.

Часть писем уходит дважды одному получателю.

Запись очереди не помечена отправленной из-за обрыва, и задание берёт её повторно. Такие случаи ловят по журналу отправки и по признаку успешности записи.

Частые вопросы

Почему письмо не уходит мгновенно?

Вызов отправки кладёт запись в очередь, а разбирает её фоновая задача по расписанию. Так страница не ждёт ответа почтового сервера и не упирается в его ограничения.

Можно ли отправить письмо сразу, минуя очередь?

Можно, но такой вызов задерживает страницу на время ответа сервера. Мгновенную отправку оставляют для единичных служебных писем, а не для покупателей.

Какой размер порции выбрать?

Тот, что укладывается в ограничения тарифа почтового сервиса с запасом. Числа берут из документации сервиса, а не подбирают после первой блокировки.

Что делать с зависшими записями?

Найти общую причину по журналу почтового сервера, починить её и перезапустить отправку. Удалять записи имеет смысл только после того, как причина устранена.

Сколько хранить отправленные записи?

Обычно достаточно двух-трёх месяцев для разбора жалоб покупателей. Дальше записи чистят по расписанию, освобождая место и ускоряя выборки.

Смежное

Первоисточник