Первый день акции: пик регистраций и очередь на письма подтверждения
В десять утра стартует акция: приветственный бонус и промокод для новых пользователей, объявление ушло в рассылку, в соцсетях, партнёрам. К десяти пятнадцати в саппорт летят первые «где моё письмо?» — человек зарегистрировался, а письмо с подтверждением почты либо не пришло вообще, либо появилось через двадцать минут, когда бонус уже неинтересен. Проблема не в акции и не в почтовом ящике пользователя — она в том, что сервис регистрации весь год отправлял по десять писем в час, а сегодня за первые полчаса нужно отправить столько же, сколько раньше уходило за месяц. Разберём, почему резкий скачок регистраций ломает именно доставку писем, а не саму регистрацию, и как выстроить отправку так, чтобы акция не превращалась в тест на прочность почтовой инфраструктуры.
Содержание
- Что ломается в первые минуты акции
- Где реальный потолок пропускной способности
- Очередь отправки вместо прямого вызова из хендлера регистрации
- Риск спама от резкого роста объёма с нового отправителя
- Мониторинг очереди и что делать, если она уже раздулась
- Как подготовиться заранее: чек-лист перед стартом акции
Что ломается в первые минуты акции
Обычный поток регистраций — это ровная линия: несколько пользователей в минуту, каждое письмо подтверждения уходит почти сразу после отправки формы. Большинство систем регистрации написаны именно под этот сценарий: обработчик HTTP-запроса создаёт пользователя в базе и тут же, в рамках того же запроса, вызывает отправку письма — либо напрямую по SMTP, либо через API транзакционного провайдера. Пока писем мало, разница между «отправить сразу» и «поставить в очередь» не видна: оба варианта укладываются в доли секунды.
В первые минуты акции характер нагрузки меняется резко и без разгона. Промо в рассылке или в соцсети открывает сразу тысячи людей, часть из них регистрируется в первые пять-десять минут — это не постепенный рост, а всплеск. Синхронный вызов отправки письма, раньше занимавший доли секунды, начинает вставать в очередь на стороне провайдера или почтового сервера. HTTP-запрос регистрации не завершается, пока не получит ответ от SMTP-сессии — а если у провайдера в этот момент дросселирование (throttling) по вашему аккаунту, ответ может не прийти вовсе, вместо него — таймаут.
Дальше цепочка стандартная: воркеры приложения (Gunicorn, PHP-FPM, Node — не важно) заняты ожиданием SMTP-ответа вместо обработки новых запросов, пул подключений к базе исчерпывается, страница регистрации у следующих пользователей открывается медленно или отдаёт 502. Акция роняет не только доставку писем — она роняет саму регистрацию, потому что отправка письма синхронно встроена в критический путь запроса.
Где реальный потолок пропускной способности
Прежде чем чинить, стоит понять, где именно упирается система — лимитов на самом деле несколько, и они разной природы:
- Лимит на стороне SMTP/API-провайдера. Почти у всех транзакционных сервисов рассылки есть ограничение по скорости — не «сколько писем в день», а именно «сколько запросов в секунду или в минуту» на один API-ключ или на один аккаунт. Это ограничение не всегда прописано крупными цифрами в документации — иногда оно проявляется только как код ошибки 429 или временный отказ в момент всплеска. Точные цифры лимита у каждого провайдера и каждого тарифа свои — уточняйте в своём личном кабинете и в договоре, не полагайтесь на цифры из чужих статей.
- Лимит на стороне собственного почтового сервера. Если письма уходят через свой Postfix или аналог, узкое место — количество одновременных SMTP-сессий наружу (
smtp_destination_concurrency_limit) и скорость, с которой принимающая сторона (Gmail, Mail.ru, Yandex) готова принимать письма именно от вашего IP. - Лимит на стороне принимающего сервера. Даже если у вас нет ограничений на отправку, крупные почтовые провайдеры сами придерживают приём писем от отправителя, который резко нарастил объём — это не техническая поломка, а осознанная защита от спама на их стороне.
- Лимит ресурсов приложения. Пул воркеров, ограничение на число одновременных исходящих HTTP/SMTP-соединений, таймауты на уровне nginx или балансировщика — это тоже потолок, просто не почтовый, а инфраструктурный.
Практический вывод: даже если формально у вас «безлимитный» тариф на отправку, скорость, с которой можно безопасно отправлять письма новым получателям, всё равно ограничена — просто ограничение может быть не на вашей стороне, а на стороне получателя. Проектировать отправку нужно под самое узкое место из всех перечисленных, а не под самое широкое.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверОчередь отправки вместо прямого вызова из хендлера регистрации
Решение — убрать отправку письма из синхронного пути обработки запроса. Регистрация должна делать три быстрые вещи: создать пользователя, положить задачу «отправить письмо подтверждения» в очередь, вернуть пользователю ответ. Сама отправка — задача фонового воркера, который работает со своей скоростью, независимо от того, сколько человек регистрируется в эту секунду.
Минимальная схема на очереди в базе данных (без отдельного брокера — этого достаточно для большинства проектов):
CREATE TABLE email_queue (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(id),
template TEXT NOT NULL DEFAULT 'confirm_email',
status TEXT NOT NULL DEFAULT 'pending', -- pending / sending / sent / failed
attempts INT NOT NULL DEFAULT 0,
next_attempt_at TIMESTAMPTZ NOT NULL DEFAULT now(),
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
last_error TEXT
);
CREATE INDEX idx_email_queue_pending
ON email_queue (next_attempt_at)
WHERE status = 'pending';
Обработчик регистрации вместо прямого SMTP-вызова делает одну быструю вставку в эту таблицу — операция занимает единицы миллисекунд и не зависит от состояния почтового провайдера:
def register_user(email, password):
user = create_user(email, password)
db.execute(
"INSERT INTO email_queue (user_id, template) VALUES (%s, %s)",
(user.id, "confirm_email"),
)
return {"status": "ok", "message": "Письмо придёт в течение нескольких минут"}
Фоновый воркер вычитывает задачи пачками, с блокировкой строк FOR UPDATE SKIP LOCKED, чтобы несколько воркеров не хватали одну и ту же задачу, и отправляет с постоянной паузой между письмами, синхронизированной с реальным лимитом провайдера:
SEND_INTERVAL = 1 / RATE_LIMIT_PER_SEC # RATE_LIMIT_PER_SEC — реальный лимит провайдера/сервера
def worker_loop():
while True:
job = fetch_next_pending_job() # SELECT ... FOR UPDATE SKIP LOCKED LIMIT 1
if job is None:
time.sleep(0.5)
continue
try:
send_email(job)
mark_sent(job.id)
except RateLimitedError:
reschedule(job.id, delay_sec=min(300, 2 ** job.attempts))
except Exception as e:
mark_failed_or_retry(job.id, error=str(e))
time.sleep(SEND_INTERVAL)
Такой воркер запускается как отдельный процесс (systemd-сервис или supervisor). Число воркеров можно масштабировать, но именно суммарная скорость отправки должна упираться в лимит провайдера: пять воркеров без общего ограничителя просто впятеро быстрее упрутся в этот лимит и дадут волну ошибок 429 вместо одной равномерной очереди.
Ключевой эффект такой архитектуры — она сглаживает всплеск. Пять тысяч регистраций за десять минут превращаются в пять тысяч задач в очереди, которые воркер разбирает с постоянной скоростью в течение получаса-часа. Пользователь при этом не ждёт эту отправку на странице регистрации — он получает мгновенный ответ и понятное сообщение, что письмо придёт в течение нескольких минут.
Риск спама от резкого роста объёма с нового отправителя
Даже если очередь настроена и отправка не блокирует регистрацию, остаётся вторая проблема — репутационная, а не пропускная. Почтовые провайдеры (Gmail, Mail.ru, Yandex и другие) оценивают отправителя не только по содержимому письма, но и по поведению: если с домена или IP, который вчера отправлял двадцать писем в день, сегодня внезапно уходит пять тысяч — это классический паттерн, по которому фильтры распознают либо взлом почтового аккаунта, либо спам-рассылку. Результат — часть писем улетает не в исходящие с ошибкой, а тихо оседает в папке «Спам» у получателя, и ни вы, ни пользователь этого не увидите в логах как явную ошибку.
Это особенно критично, если вы используете относительно новый или недавно созданный IP или домен для отправки — прогретая годами репутация переносит скачки объёма спокойнее, чем свежий адрес. Если акция планируется заранее, IP для отправки стоит готовить заранее: подробная механика описана в статье про прогрев IP для рассылок — идея та же, что и с очередью: резкий скачок объёма без разгона выглядит подозрительно для принимающей стороны, независимо от того, легитимен он или нет.
Что снижает риск конкретно для писем подтверждения регистрации:
- Держать письма подтверждения на отдельной инфраструктуре от маркетинговых рассылок. Если промо-письма о самой акции (объявление, follow-up, «не забудьте использовать промокод») уходят с того же домена и того же потока отправки, что и письма подтверждения, жалобы на одно бьют по репутации другого. Разделение транзакционных и маркетинговых писем разобрано отдельно в статье про транзакционные письма и маркетинг.
- Не отправлять с нуля в первый же час акции. Даже с очередью можно организовать плавный старт: в первые 10–15 минут ограничить скорость воркера более консервативным значением, чем реальный лимит провайдера, и постепенно поднимать её, если bounce rate и жалобы остаются низкими.
- Следить за bounce rate и жалобами в реальном времени. Резкий рост hard bounce или пометок «это спам» — сигнал притормозить отправку до выяснения причины, а не продолжать слать в надежде, что «само пройдёт».
- Проверить SPF/DKIM/DMARC заранее, а не в день акции. Технически корректная аутентификация писем не гарантирует попадание во «Входящие», но её отсутствие почти гарантирует папку «Спам» при любом объёме — тем более при резком.
Мониторинг очереди и что делать, если она уже раздулась
Очередь снимает проблему блокировки регистрации, но создаёт свою метрику для наблюдения — глубину очереди и время ожидания письма. Если скорость постановки задач (регистраций в секунду) устойчиво превышает скорость их разбора воркером, очередь растёт без остановки, и «через несколько минут» из ответа регистрации превращается в «через несколько часов».
Минимальный набор того, что стоит смотреть во время акции:
-- сколько писем в каком статусе прямо сейчас
SELECT status, count(*) FROM email_queue GROUP BY status;
-- сколько минут в среднем ждёт письмо, которое ещё не отправлено
SELECT avg(EXTRACT(EPOCH FROM (now() - created_at)) / 60) AS avg_wait_min
FROM email_queue WHERE status = 'pending';
Если письма уходят через собственный Postfix, а не через внешний API, дополнительно смотрите системную очередь: mailq | tail -1 покажет число писем в очереди одной строкой.
Если очередь всё же раздулась настолько, что письма опаздывают на десятки минут — это уже не отладка, а инцидент, и здесь применимы те же принципы, что при разборе разросшейся очереди Postfix на десятки тысяч писем: сначала остановить приток новых задач или замедлить приём регистраций, затем разобраться, что в очереди легитимное, а что нет, и только потом чистить. Подробный разбор такой ситуации — в статье «Очередь Postfix на 40 тысяч писем: как остановить и не потерять свои». Общий принцип rate limiting и почему наивное ограничение иногда бьёт по легитимным пользователям сильнее, чем по проблеме, которую должно решать, разобран в статье «Как работает rate limiting».
Практический план действий при раздувшейся очереди в разгар акции: поднять скорость воркера до реального предела провайдера, не выше (иначе — волна 429 и откат части писем в конец очереди); если этого предела не хватает для текущего темпа регистраций — временно подключить второй канал отправки, а повышение тарифа планировать заранее, а не во время инцидента; не удалять задачи из очереди «чтобы разгрузить» — пользователь всё равно ждёт письмо, и удаление превращает задержку в полное отсутствие письма; зафиксировать метрики инцидента (глубина очереди, задержка, bounce rate) как данные для следующей акции.
Как подготовиться заранее: чек-лист перед стартом акции
Большая часть описанных проблем не чинится в моменте — она предотвращается настройкой, сделанной за дни или недели до запуска акции:
| Что проверить | Зачем |
|---|---|
| Отправка письма вынесена из синхронного пути регистрации в очередь | Регистрация не падает и не тормозит при всплеске отправок |
| Скорость воркера ограничена значением, которое реально выдерживает провайдер/сервер | Не получить массовый 429 или дросселирование в первые минуты |
| Транзакционные письма отправляются отдельно от маркетинговых | Жалобы на промо-рассылку не бьют по доставке писем подтверждения |
| SPF/DKIM/DMARC настроены и проверены заранее | Резкий объём не усугубляет и без того слабую аутентификацию |
| Если используется новый IP/домен — заранее начат прогрев | Скачок объёма на непрогретом адресе выглядит как спам с высокой вероятностью |
| Настроен мониторинг глубины очереди и bounce rate | Проблема видна в первые минуты, а не через час жалоб в поддержку |
Отдельно стоит нагрузочно протестировать саму очередь, а не только веб-слой: сымитировать несколько тысяч регистраций за короткое время и посмотреть на задержку письма, а не только на код ответа HTTP. Часто веб-часть выдерживает нагрузку без проблем, а узким местом оказывается именно очередь писем — на обычном трафике это никогда не проявляется.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Можно ли просто увеличить число воркеров, чтобы разгрузить очередь быстрее?
Само по себе нет, если все воркеры шлют через одного провайдера с общим лимитом — больше воркеров без общего ограничителя скорости просто быстрее упрутся в этот лимит и дадут больше ошибок 429. Число воркеров имеет смысл наращивать вместе с проверкой реального лимита на отправку, а не вместо неё.
Нужен ли отдельный брокер сообщений типа RabbitMQ или Kafka?
Не обязательно. Таблица в существующей базе с SELECT ... FOR UPDATE SKIP LOCKED спокойно выдерживает тысячи писем в час — этого хватает для большинства акций. Отдельный брокер оправдан, если у вас уже много других асинхронных задач, а очередь писем — лишь одна из них.
Что показать пользователю сразу после регистрации, если письмо ещё не отправлено?
Честное сообщение, что письмо придёт в течение нескольких минут, и, если это критично для конверсии, — кнопка «отправить письмо повторно» со своим ограничением частоты, чтобы нетерпеливые клики не плодили дублирующие задачи в очереди.
Как понять, что письма реально уходят в спам, а не просто задерживаются?
Если письмо ушло от сервера или провайдера с кодом успешной доставки, но получатель не видит его во «Входящих» — это репутационная проблема на стороне получающего сервера, а не задержка в вашей очереди. Помогает панель статистики провайдера и разбор того, как письма попадают в спам.
Стоит ли откладывать акцию, если IP или домен для отправки ещё не прогрет?
Если ожидается заметный всплеск регистраций, а отправка идёт с недавно созданного адреса — да, разумнее сдвинуть старт на одну-две недели и прогреть адрес постепенным ростом легитимного объёма, чем получить массовую отправку в спам в первый же день, когда репутация особенно важна.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →