ENIGMA AI
ENIGMA AI

Как реализовать механизм отправки email-сообщений с гарантией доставки, используя статусы и подписку на события?

встречается 1× middle architecture

Как ответить

Механизм гарантированной доставки email строится на паттерне Outbox (транзакционный вывод) и событийной модели со статусами. Суть: запись письма в БД с начальным статусом «pending» происходит в рамках той же транзакции, что и бизнес-операция. Отдельный процесс (scheduler или consumer) читает новые pending-письма, отправляет через SMTP и фиксирует результат.

Статусы письма в БД:

  • pending — только создано, не отправлено
  • sending — процесс взял в работу (с lock на случай параллельных обработчиков)
  • sent — успешно отправлено, подтверждено почтовым сервером
  • failed — ошибка отправки (временная или постоянная)

Подписка на события: После отправки публикуется событие EmailSent или EmailFailed. Подписчики (например, сервис уведомлений) обновляют статус в основной таблице и, при необходимости, запускают retry. Для повторных попыток используется exponential backoff и максимальное количество retry (обычно 3–5).

Пример упрощённой структуры:

// Таблица outbox_emails
id          UUID PRIMARY KEY,
body        JSONB,
status      VARCHAR(20) DEFAULT 'pending',
created_at  TIMESTAMP,
sent_at     TIMESTAMP,
retry_count INT DEFAULT 0,
lock_until  TIMESTAMP

// Псевдокод обработчика
function processOutbox() {
   emails = db.query(
      "UPDATE outbox_emails SET status='sending', lock_until=NOW()+30s
       WHERE status='pending' AND (lock_until IS NULL OR lock_until < NOW())
       RETURNING *"
   )
   foreach email in emails {
       try {
           smtp.send(email.body)
           db.update(id, status: 'sent', sent_at: NOW())
           eventBus.publish(EmailSent, {id, timestamp: NOW()})
       } catch (ex) {
           db.update(id, status: 'failed', retry_count++)
           eventBus.publish(EmailFailed, {id, reason: ex.message})
       }
   }
}

Ключевые моменты:

  • Транзакционная консистентность — письмо сохраняется атомарно с бизнес-данными.
  • Идемпотентность — повторная доставка одного письма не должна приводить к дубляжам (проверка по idempotency_key).
  • Мониторинг — метрики: кол-во pending, failed, время задержки.
  • Обработка ошибок — временные сбои (таймаут) → retry, постоянные (невалидный адрес) → не retry, а логирование и алерт.

Ключевые тезисы

  • Использовать паттерн Outbox для атомарности записи и отправки
  • Статусная модель: pending → sending → sent/failed, с блокировкой строк
  • Событийная подписка для обновления статусов и повторных попыток
  • Exponential backoff и лимит retry для временных ошибок
  • Идемпотентность на уровне idempotency_key, чтобы избежать дублей

Что спросят дальше

  • — Как вы обрабатываете случай, когда после SMTP ответа сервер упал, и статус не сохранился?
  • — Если почтовый провайдер блокирует IP из-за слишком частых запросов, как вы это предотвратите?
  • — Как вы тестируете гарантию доставки (например, симулируете сбой БД или SMTP)?

Готовьтесь к собеседованию с ENIGMA AI

AI-суфлёр подсказывает ответы прямо на собеседовании в реальном времени — незаметно для интервьюера.

Скачать приложение