Någonstans i varje integrationskontrakt finns en mening i stil med "varje order kommer att skickas exakt en gång." Den har aldrig någonsin varit sann. Nätverket delades mitt i en förfrågan och avsändaren gjorde ett nytt försök; kön levererade om efter en konsumentkrasch; en lageroperatör tryckte på exportknappen två gånger. Dubbletter är inget felläge - de är väder.
Idempotensnycklar, valda med omsorg
Lösningen är gammal och oglamorös: varje meddelande bär en nyckel, och bearbetningen blir en no-op när nyckeln redan har setts. De intressanta besluten ligger alla i valet av nyckel.
- Naturliga nycklar slår genererade sådana. En ordersynkronisering nyckelad på
order_id + statusöverlever att avsändaren regenererar sin utkorg; ett slumpmässigt UUID skapat vid sändningstillfället gör det inte. - Avgränsa nyckeln till effekten, inte meddelandet. Om ett meddelande skapar en faktura och bokar lager, är det två effekter med två nycklar - annars lämnar ett partiellt fel er oförmögna att säkert göra om försöket för någotdera.
- Låt nycklar löpa ut medvetet. Ett dedup-lager som växer för evigt är en tidsinställd bomb; ett som löper ut för tidigt återinför dubbletter. Vi använder som standard 3× avsändarens maximala omförsökshorisont.
Testet som spelar roll
Varje integration vi levererar klarar det vi internt kallar dubbeltryckstestet: spela upp gårdagens hela meddelandelogg mot dagens system, två gånger, och jämför det resulterande tillståndet. Om något ändrades vid den andra genomkörningen är pipelinen inte klar.
Det är ett brutalt test och det misslyckas ständigt under utveckling - på tidsstämplar, på "skapad av"-granskningsfält, på sekvensräknare. Varje misslyckande är en plats där en ombeprövning klockan tre på natten tyst hade korrumperat data. Det är mycket billigare att fånga felet under utvecklingen än i produktion.