Ровно один раз - это ложь: проектирование интеграций с расчётом на дубликаты

Три копии одной и той же плитки сообщения, сходящиеся в единую обработанную плитку

В каждом интеграционном контракте где-то есть фраза вроде «каждый заказ будет передан ровно один раз». Она не была правдой ни разу. Сеть разрывалась посреди запроса, и отправитель повторял попытку; очередь доставляла сообщение повторно после падения потребителя; оператор склада дважды нажимал кнопку экспорта. Дубликаты - это не режим сбоя, это погода.

Ключи идемпотентности, выбранные с умом

Решение старое и непримечательное: каждое сообщение несёт ключ, и обработка ничего не делает, если ключ уже встречался. Все интересные решения - в выборе самого ключа.

  • Естественные ключи лучше сгенерированных. Синхронизация заказов с ключом order_id + status переживёт пересоздание исходящей очереди отправителем; случайный UUID, сгенерированный в момент отправки, - нет.
  • Привязывайте ключ к эффекту, а не к сообщению. Если одно сообщение создаёт счёт и резервирует товар на складе - это два эффекта с двумя ключами, иначе частичный сбой лишит вас возможности безопасно повторить хоть одно из действий.
  • Продумывайте срок жизни ключей. Хранилище дедупликации, растущее бесконечно, - это бомба замедленного действия; то, что истекает слишком быстро, снова впускает дубликаты. По умолчанию мы берём 3-кратный горизонт максимальных повторных попыток отправителя.

Тест, который имеет значение

Каждая интеграция, которую мы поставляем, проходит то, что мы внутри команды называем тестом двойного касания: весь вчерашний лог сообщений дважды прогоняется через сегодняшнюю систему, а итоговые состояния сравниваются. Если что-то изменилось при втором проходе, конвейер ещё не готов.

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