Кейс: сервис уведомлений слал письмо после того, как пользователь уже отписался

Пользователь нажимал «отписаться» в письме, get-запрос на отписку долетал и записывал флаг в БД. Но письмо всё равно приходило снова — иногда через минуту, иногда через час.

Логика выглядела разумно: джоба раз в 5 минут выбирает пользователей на отправку, читает их настройки подписки одним запросом, формирует батч писем и шлёт через очередь. От выборки данных до фактической отправки в SMTP проходило от нескольких секунд до пары минут — из-за очереди и ретраев на стороне провайдера почты.

Если пользователь отписывался в этом окне — между чтением списка адресатов и реальной отправкой — флаг в БД уже стоял, но письмо было прочитано и поставлено в очередь раньше. Отписка не отменяла уже сформированную задачу.

Первое, что предлагали в качестве фикса — уменьшить интервал джобы. Это не решает проблему, только сужает окно гонки. Реальный фикс — проверка флага подписки не на этапе выборки, а непосредственно перед отправкой, максимально близко к SMTP-вызову. Второй вариант — TTL у задачи в очереди с повторной проверкой актуальности при обработке, если задержка может быть большой.

Вывод один: если между «прочитали условие» и «выполнили действие» проходит заметное время, условие нужно перепроверять перед самим действием, а не доверять снимку, сделанному раньше.

Тренажёр: 600 вопросов, мок с таймером, план повторов

senior·base — что спрашивают на самом деле


В этом посте были ссылки, но мы их удалили по правилам Сетки