Отправляйте почту максимально транзакционно

У меня есть простая программа, которая время от времени опрашивает таблицу БД и отправляет любую почту, на которую указывает таблица (используя javax.mail). Когда почта отправлена, я удаляю запись БД.

Я заметил две потенциальные проблемы

  • Есть вероятность отправить письмо, а затем что-то выйдет из строя, поэтому запись в БД все еще существует. В следующий раз письмо будет отправлено снова.
  • Может быть много почты для отправки, поэтому я загружаю все ожидающие записи, отправляю их все, а затем удаляю все записи БД. Если некоторые/все письма отправляются, что-то выходит из строя, и задание запускается снова, то оно дважды спамит многим людям.

Это не спам-приложение, поэтому я хочу попытаться избежать отправки чего-либо дважды, а также убедиться, что я никогда не смогу «потерять» сообщение.

Это общая проблема, я уверен, что есть два физических ресурса, которые не могут участвовать в транзакции (БД + что-то еще), поэтому мне было интересно, какие подходы люди используют для уменьшения/устранения проблем для этого случая и/или общий случай.


person Mike Q    schedule 25.10.2010    source источник


Ответы (2)


У меня есть упрощенное решение вашей проблемы. Добавьте поле в БД с именем 'Pulled'

Проверьте Pulled = 0, затем извлеките данные, обновите 'Pulled' = 1 и отправьте электронное письмо. После отправки письма удалите запись.

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

Если 'Pulled' = 1 при первоначальной проверке, то вы знаете, что произошла ошибка, и вы должны принять решение, рискнуть ли повторно отправить электронное письмо или удалить его, рискуя потерять электронное письмо.

Две вещи ты знаешь наверняка.

(1) Если 'Pulled' = 0, электронное письмо не было отправлено.

(2) Если 'Pulled' = 1, то в процессе отправки или удаления произошла ошибка.

person Michael Eakins    schedule 25.10.2010

Я бы определенно читал каждое письмо из базы данных по отдельности, чтобы избежать второй описанной вами проблемы.

Поместите блокировку в строку базы данных (например, select for update) на случай, если несколько процессов запустятся одновременно (даже если вы не хотите, чтобы это произошло, это может произойти случайно из-за неправильного развертывания; до сих пор работает..)

Либо отправьте письмо непосредственно перед выполнением commit в транзакции, либо отправьте сообщение в какую-либо асинхронную систему, например. через JMS. Но более простое решение — просто отправить электронное письмо непосредственно перед commit, если это возможно. Если что-то пойдет не так с почтой, вы можете откатиться. Если ничего не пойдет не так, маловероятно, что что-то пойдет не так с commit, так что, вероятно, все в порядке.

В противном случае, если это не соответствует дизайну программного обеспечения, которое вы используете (например, электронная почта отправляется из какого-либо метода бизнес-логики, а транзакции управляются процессом более высокого уровня), вы можете создать объекты электронной почты и поместить их в список. Непосредственно перед фиксацией вы можете проверить этот список и отправить электронные письма. Таким образом, электронные письма становятся немного более транзакционными, то есть они отправляются только тогда, когда вы делаете коммит.

person Adrian Smith    schedule 25.10.2010
comment
@Nathan Hughes - Конечно, этот риск существует, но если вы отправляете их непосредственно перед commit, вы минимизируете этот риск, сводя к минимуму временное окно, в котором сбой может вызвать эту ситуацию. Поскольку электронные письма не являются транзакционными (как только они отправлены, они отправлены), другого пути нет. - person Adrian Smith; 25.10.2010