developer.overheid.nl

Ontwikkelaarsportaal van de Nederlandse overheid

Ga naar hoofdinhoud

Veilige retries met volledige idempotency

Bij een schrijfoperatie kan het voor een client onduidelijk zijn of deze is geslaagd, bijvoorbeeld door een netwerkfout. In dat geval zal de client de operatie opnieuw proberen. Een ongewenst effect is dat er door het opnieuw proberen meer wijzigingen worden doorgevoerd dan de bedoeling was. Om dit te voorkomen is het van belang dat alle API-operaties die data wijzigen volledig idempotent zijn.

Een operatie is idempotent als het herhalen ervan geen extra effect heeft op de toestand van de server. GET, PUT en DELETE zijn volgens de HTTP-specificatie idempotent. Een herhaalde DELETE /items/2 zal bijvoorbeeld de tweede keer een 404 geven, maar de toestand van de server blijft consistent. POST en PATCH zijn dit van nature niet: een herhaalde POST /items zou een duplicaat item aanmaken. Voor deze operaties kan idempotent gedrag worden afgedwongen door een Idempotency-Key header toe te voegen.

HTTP VerbIdempotency mechanisme
GETSafe request RFC 9110
PUTIdempotent RFC 9110
DELETEIdempotent RFC 9110
POSTHeader: Idempotency-Key
PATCHHeader: Idempotency-Key

Werking

  1. De client genereert een unieke Idempotency-Key (bijvoorbeeld een UUID) voor een uit te voeren operatie.
  2. De client verstuurt de POST of PATCH request met deze key in de header.
  3. De server controleert of deze Idempotency-Key al eerder is ontvangen.
    • Bestaande key: De server verwerkt de operatie niet opnieuw, maar stuurt direct de eerder opgeslagen response terug.
    • Nieuwe key: De server verwerkt de operatie en slaat de response (statuscode en body) op, gekoppeld aan de Idempotency-Key.
  4. Als de client een retry moet uitvoeren (bijvoorbeeld door een netwerkfout), moet exact dezelfde Idempotency-Key worden gebruikt.

Transactionele veiligheid

Voor kritieke operaties is het aan te bevelen voor de client om de Idempotency-Key lokaal en duurzaam op te slaan voordat de request wordt verstuurd. Stel dat een client crasht nadat het de request heeft verstuurd, maar voordat het een response heeft ontvangen. Bij het herstarten weet de client niet of de operatie op de server is geslaagd. Door de Idempotency-Key vooraf op te slaan, kan de client na een herstart de request veilig opnieuw proberen met dezelfde key. De server zal dan ofwel de operatie alsnog uitvoeren (als de eerste poging nooit aankwam), of de opgeslagen response retourneren (als de eerste poging wel slaagde).

Een correcte implementatie van idempotency op de server vereist transactionele veiligheid. Het verwerken van de business-operatie en het opslaan van de Idempotency-Key met de bijbehorende response moeten binnen dezelfde atomaire transactie plaatsvinden. Zonder deze garantie kan een race condition optreden, waarbij een operatie onterecht twee keer wordt uitgevoerd.

Levensduur van de key

De server bewaart de Idempotency-Key en de bijbehorende response voor een bepaalde tijd om de garantie te kunnen bieden. De bewaartermijn is afhankelijk van het verwachte retry-patroon van clients, waarbij 24 uur een gangbare keuze is. De gekozen termijn moet door de API-provider worden gedocumenteerd. Na het verlopen van de termijn behandelt de server een request met dezelfde key als een nieuwe operatie.

Voorbeeld in OpenAPI

paths:
/items:
post:
summary: Maak een nieuw item aan
parameters:
- name: Idempotency-Key
in: header
required: true
description:
Een unieke sleutel om de idempotent-garantie te bieden. De server
bewaart deze sleutel 24 uur.
schema:
type: string
format: uuid
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/Item"
responses:
"201":
description: Item aangemaakt