Как реализовать логику выборки документов для уведомлений
Что проверяют
Умеете ли вы спроектировать выборку так, чтобы каждое уведомление ушло ровно один раз, даже при сбоях, рестартах и нескольких инстансах сервиса. Следом спрашивают, что будет, если джоба упала посреди отправки, и как не заблокировать таблицу документов.
Как отвечать
Вопрос часто звучит как просьба рассказать о своём проекте, поэтому назовите объёмы: сколько документов в сутки, как быстро должно прийти уведомление. Каркас решения:
- явное состояние: выборка «созданные за последние 24 часа» ненадёжна, джоба может опоздать или запуститься дважды. Нужна отметка: колонка
notified_atили таблица задач со статусамиNEW,PROCESSING,SENT,FAILED; - transactional outbox: запись о событии вставляется в той же транзакции, что и документ, поэтому событие не теряется. Дальше её забирает планировщик или CDC вроде Debezium с отправкой в Kafka;
- захват пачки:
SELECT ... WHERE status = 'NEW' ORDER BY id LIMIT 500 FOR UPDATE SKIP LOCKEDв PostgreSQL даёт нескольким воркерам брать разные строки без взаимных блокировок. Частичный индексWHERE status = 'NEW'держит запрос быстрым; - отправка вне транзакции: строки помечаются
PROCESSINGс временем захвата и коммитятся, письмо уходит потом. Зависшие дольше таймаута возвращаются в очередь; - идемпотентность: уникальный ключ
(document_id, recipient_id, type)не даст записать второе уведомление при повторе; - повторы: счётчик попыток и экспоненциальная задержка, после лимита статус
FAILEDи алерт.
В Spring это @Scheduled плюс ShedLock, чтобы джоба не шла на всех инстансах разом, или Spring Batch с чанками при больших объёмах. Читайте только нужные колонки: SELECT * с полем content гоняет по сети мегабайты.
Где спотыкаются
Держат транзакцию с блокировкой строк, пока ходят в почтовый сервис. Медленный SMTP превращается в очередь занятых соединений пула.
Листают пачки через OFFSET. Пока строки меняют статус, выборка сдвигается и часть документов пропускается.
Глотают исключение через e.printStackTrace() и возвращают пустой список. Уведомления молча не приходят, а мониторинг ничего не видит.