При обработке сообщения Rebus выполняет свою работу в следующем порядке:
- Ваш обработчик выполнен (возможно, задействует транзакцию базы данных для фиксации вашей собственной работы)
- Исходящие сообщения отправляются
- Входящее сообщение удаляется из очереди
Поскольку мир полон неудач, ваша программа может потерпеть неудачу в любой момент между (или во время!) Этими шагами.
Если сбой происходит до или во время (1), тогда проблем нет, потому что ваша собственная работа (по крайней мере, в этом случае) выполняется внутри транзакции, которую можно атомарно откатить.
Если что-то не получится после (1) и до полного завершения (3), вы получите гарантию «хотя бы раз» доставки Rebus, что означает, что - в случае подобных сбоев - сообщения БУДУТ обрабатываться хотя бы один раз, что означает, что с можно справиться дважды, а может, даже больше, если вам повезет.
Этого не избежать, поэтому, если вам небезразлична эта ситуация, вам нужно сделать свой обработчик сообщений идемпотентным .
Идемпотентность может быть достигнута многими способами: иногда благодаря идемпотентности самой операции (например, просто добавление полученных данных, установка значений некоторых полей в значение из сообщения и т. Д.), Иногда полагаясь на то, что возможность удалять устаревшие данные (например, если вы можете сравнить «последнее измененное» значение ваших данных с отметкой времени обновления из сообщения).
Но иногда, если ваша система окажется в плохом состоянии при обработке повторно доставленного сообщения, вам необходимо тщательно кодировать свой выход из него, например, путем сохранения идентификатора обработанного сообщения в таблице с уникальным ограничением идентификатора.
Сложность заключается в следующем: истинная идемпотентность требует, чтобы вы эмулировали все общедоступное поведение, в том числе при обработке сообщения во второй раз. Это означает, что все сообщения, отправленные / опубликованные при обработке вашего сообщения, должны быть отправлены и опубликованы также во второй раз.
Как вы, вероятно, можете себе представить, реализация истинной идемпотентности не всегда тривиальна.
(...) создатели NServiceBus утверждают, что всегда будет правильный результат области транзакции - либо все зафиксировано, либо все откатано, независимо от того, произойдет ли сбой машины в любой точке области транзакции (.. .)
С распределенными транзакциями и двухфазной фиксацией это не может быть правдой, поскольку существует вероятность третьего результата: все транзакции подтверждаются на этапе подготовки, а затем одна из них завершается неудачей на этапе фиксации (из-за сети отключение, переполнение диска или какая-либо иная неисправимая проблема) - тогда у координатора транзакций не остается другого выбора, кроме как оставить транзакцию в подвешенном состоянии, требуя ручного вмешательства, чтобы мир продолжался.