Някъде във всеки договор за интеграция има изречение от рода на „всяка поръчка ще бъде предадена точно веднъж.“ Това никога не е било вярно. Мрежата се е разделила по средата на заявката и подателят е направил повторен опит; опашката е доставила повторно след срив на консуматора; складов оператор е натиснал бутона за износ два пъти. Дубликатите не са режим на отказ - те са времето навън.
Идемпотентни ключове, избрани внимателно
Решението е старо и невзрачно: всяко съобщение носи ключ, а обработката е без ефект, когато ключът вече е бил видян. Интересните решения са изцяло в избора на ключа.
- Естествените ключове побеждават генерираните. Синхронизация на поръчки, ключирана по
order_id + status, оцелява при повторно генериране на изходящата опашка от подателя; произволен UUID, генериран в момента на изпращане, не оцелява. - Обвържете ключа с ефекта, не със съобщението. Ако едно съобщение създава фактура и резервира наличност, това са два ефекта с два ключа - иначе частичен отказ ви оставя невъзможност да повторите безопасно нито едното от двете.
- Изтривайте ключовете съзнателно. Хранилище за дедупликация, което расте безкрайно, е бомба със закъснител; такова, което изтича твърде рано, връща дубликатите обратно. По подразбиране използваме 3× максималния хоризонт на повторни опити на подателя.
Тестът, който има значение
Всяка интеграция, която доставяме, преминава през това, което вътрешно наричаме теста с двойно докосване: възпроизвеждаме целия вчерашен журнал от съобщения срещу днешната система, два пъти, и сравняваме получените състояния. Ако нещо се е променило при второто преминаване, пайплайнът не е готов.
Това е жесток тест и се проваля постоянно по време на разработка - при времеви маркери, при одитни полета от типа „създадено от“, при брояч на последователности. Всеки провал е място, където повторна доставка в 3 сутринта тихомълком би повредила данните. Далеч по-евтино е да откриете проблема по време на разработката, отколкото в реална експлоатация.