Exactly-once is een leugen: integraties ontwerpen voor duplicaten

Drie kopieën van dezelfde berichttegel die samenkomen in één verwerkte tegel

Ergens in elk integratiecontract staat een zin als "elke order wordt precies één keer verzonden." Dat is nog nooit waar geweest. Het netwerk raakte halverwege het verzoek gepartitioneerd en de verzender probeerde opnieuw; de wachtrij leverde opnieuw af na een crash van de consument; een magazijnmedewerker drukte twee keer op de exportknop. Duplicaten zijn geen storingsmodus - ze zijn weer.

Idempotentiesleutels, zorgvuldig gekozen

De oplossing is oud en onopvallend: elk bericht draagt een sleutel, en verwerking is een no-op zodra de sleutel al eerder is gezien. De interessante beslissingen zitten allemaal in het kiezen van de sleutel.

  • Natuurlijke sleutels verslaan gegenereerde sleutels. Een ordersynchronisatie met als sleutel order_id + status overleeft het wanneer de verzender zijn outbox opnieuw genereert; een willekeurige UUID die op verzendmoment wordt aangemaakt niet.
  • Bepaal de scope van de sleutel op het effect, niet op het bericht. Als één bericht een factuur aanmaakt én voorraad boekt, zijn dat twee effecten met twee sleutels - anders kunt u bij een gedeeltelijke fout geen van beide veilig opnieuw proberen.
  • Laat sleutels bewust verlopen. Een dedup-store die eindeloos blijft groeien is een tijdbom; een die te snel verloopt, introduceert opnieuw duplicaten. Wij hanteren standaard 3× de maximale retryhorizon van de verzender.

De test die ertoe doet

Elke integratie die wij opleveren doorstaat wat we intern de "dubbeltik-test" noemen: speel gisterens volledige berichtenlog twee keer opnieuw af tegen het systeem van vandaag, en vergelijk de resulterende status. Als er bij de tweede doorloop iets veranderd is, is de pijplijn niet af.

Het is een meedogenloze test en hij faalt voortdurend tijdens de ontwikkeling - op timestamps, op "aangemaakt door"-auditvelden, op volgnummers. Elke fout is een plek waar een opnieuw-afleveren om 3 uur 's nachts stilletjes data zou hebben gecorrumpeerd. Het is veel goedkoper om dat tijdens de ontwikkeling te ontdekken dan in productie.