Et sted i enhver integrationskontrakt står en sætning som "hver ordre overføres nøjagtigt én gang". Det har aldrig nogensinde været sandt. Netværket blev partitioneret midt i en forespørgsel, og afsenderen gentog forsøget; køen genleverede efter et forbrugernedbrud; en lageroperatør trykkede på eksportknappen to gange. Dubletter er ikke en fejltilstand - de er vejret.
Idempotensnøgler, valgt med omhu
Løsningen er gammel og ugalant: hver besked bærer en nøgle, og behandlingen er en no-op, når nøglen allerede er set. De interessante beslutninger ligger alle i valget af nøglen.
- Naturlige nøgler slår genererede. En ordresynkronisering nøglet på
order_id + statusoverlever, at afsenderen regenererer sin outbox; en tilfældig UUID skabt på afsendelsestidspunktet gør ikke. - Afgræns nøglen til effekten, ikke beskeden. Hvis én besked opretter en faktura og booker lagerbeholdning, er det to effekter med to nøgler - ellers efterlader en delvis fejl dig ude af stand til trygt at forsøge nogen af dem igen.
- Lad nøgler udløbe bevidst. Et dedup-lager, der vokser for evigt, er en tidsindstillet bombe; et der udløber for hurtigt, genindfører dubletter. Vi bruger som standard 3× afsenderens maksimale gentagelseshorisont.
Testen, der betyder noget
Hver integration, vi leverer, består det, vi internt kalder dobbelttryk-testen: afspil hele gårsdagens beskedlog mod dagens system, to gange, og diff den resulterende tilstand. Hvis noget ændrede sig i anden omgang, er pipelinen ikke færdig.
Det er en brutal test, og den fejler konstant under udvikling - på tidsstempler, på "oprettet af"-revisionsfelter, på sekvenstællere. Hver fejl er et sted, hvor en genlevering klokken tre om natten stille og roligt ville have korrumperet data. Langt billigere at fange det under udviklingen end i produktion.