TATECHATLAS
◎ Français
Web et API

Soumissions API en double : concevoir un contrat d'idempotence avant d'ajouter une clé

Définissez l'appelant, la charge, la réclamation atomique et la politique de relecture pour qu'un réessai ait un résultat d'application prévisible.

Dans ce guide

Pour une soumission d'ordre réessayée, réutilisez une clé pour le même appelant authentifié et la même charge logique. L'application peut réclamer atomiquement cette combinaison et conserver son résultat terminé pour relecture. Une charge modifiée utilisant la même clé doit recevoir un conflit d'application documenté. Une ligne de base de données unique coordonne les réclamations ; elle ne garantit pas seule qu'un paiement, un email ou tout autre effet externe se produit exactement une fois.

Séparer la sémantique HTTP du contrat d'application

Une opération idempotente a le même effet serveur intentionnel lorsqu'elle est répétée que lorsqu'elle est effectuée une fois. Les réponses n'ont pas besoin d'être identiques pour que cette définition soit valable. Un point de terminaison POST n'acquiert pas un contrat de réessai sûr simplement parce qu'un client envoie un en-tête supplémentaire. Le serveur doit implémenter et documenter comment il interprète cette clé, quelles opérations elle couvre, et comment les réessais interagissent avec l'authentification et l'état stocké.

Délimiter la clé à un appelant de confiance

Dans l'exemple hypothétique, la clé k1 appartient à un seul appelant authentifié et à une seule opération de création d'ordre. Un autre appelant utilisant k1 ne doit pas recevoir le résultat du premier appelant. Obtenez l'identité de l'appelant à partir de l'authentification de confiance plutôt que d'un champ de charge librement fourni. Définissez si les clés partagent un espace de noms entre les opérations ou sont délimitées à un point de terminaison particulier, et préservez cette délimitation dans la règle d'unicité de la base de données.

Spécifier l'équivalence des charges

La même clé doit représenter la même soumission logique. Définissez quels champs font partie de l'opération et comment l'application les compare, par exemple en utilisant une représentation canonique ou un condensé selon une règle documentée. Les octets JSON bruts peuvent différer tout en représentant les mêmes données, donc l'égalité des octets est un choix de politique. À l'inverse, exclure un champ important comme le montant peut identifier incorrectement des commandes différentes comme équivalentes.

Parcourir le réessai construit

Supposons que l'appelant A soumet la clé k1 avec une charge demandant deux unités de l'article X. La première requête réussie stocke un résultat d'ordre. Un réessai par A avec k1 et la charge équivalente retourne ce résultat conservé selon ce contrat proposé. Une requête utilisant k1 mais demandant trois unités est rejetée comme une non-concordance de charge. Ceci est une illustration de conception, pas une affirmation que chaque API existante utilise le même code de statut ou le même comportement de relecture.

Réclamer la clé atomiquement

Une séquence vérifier-puis-insérer peut entrer en concurrence : deux requêtes peuvent toutes deux voir aucune clé existante. Une contrainte d'unicité de base de données sur la délimitation choisie d'appelant, opération et clé peut imposer une seule réclamation stockée. PostgreSQL INSERT ON CONFLICT peut aider à coordonner l'insertion. L'application doit toujours interpréter si elle possède une nouvelle réclamation ou a trouvé une existante, et ne doit pas laisser chaque requête perdante effectuer l'opération protégée quand même.

Représenter les états de traitement et terminé

Stockez assez d'état pour distinguer une requête encore en cours de traitement d'une dont le résultat peut être relu. Définissez comment un réessai concurrent répond pendant que la première tentative s'exécute : attendre, une réponse temporaire documentée, ou une instruction de réessai sont des politiques possibles. Persistez l'état terminé et son résultat de façon cohérente avec les changements de base de données lorsque cela est possible. N'assumez pas que la présence d'une ligne de clé quelconque prouve que la commande s'est terminée avec succès.

Gérer les effets externes et les fenêtres de panne

Un fournisseur de paiement ou un service de messagerie ne participe pas automatiquement à la transaction de base de données. Une panne après un effet externe mais avant le stockage du résultat terminé crée un problème de récupération. Une boîte de sortie durable, une déduplication prise en charge par le fournisseur, ou une réconciliation explicite peuvent former une partie de la solution, selon l'effet. Chacun a son propre contrat. Envelopper simplement l'insertion de clé locale dans une transaction n'établit pas un comportement exactement une fois entre les systèmes.

Documenter les limites de rétention et de récupération

Précisez combien de temps les clés et les résultats sont conservés, ce qu'un relecture retourne, et ce qui se passe après expiration. Supprimer un enregistrement peut permettre à une requête ultérieure avec la même clé de devenir une nouvelle soumission. Définissez aussi la récupération pour les états de traitement abandonnés et si les tentatives échouées sont réutilisables. Ces choix affectent à la fois la correction et le stockage. Un client doit pouvoir distinguer un réessai sûr d'une création d'une nouvelle opération logique.

Points à vérifier

  • Réutilisez une clé uniquement pour la même opération logique.
  • Limitez la recherche à l'appelant authentifié.
  • Définissez l'équivalence des charges et le comportement en cas de non-concordance.
  • Rendez la réclamation atomique et unique.
  • Prévoyez la récupération pour les effets externes et les clés expirées.

Ce guide décrit un contrat d'application hypothétique, pas une implémentation serveur complète ni une norme universelle Idempotency-Key. L'unicité de la base de données et l'idempotence HTTP ne garantissent pas seules des effets secondaires externes exactement une fois.

Sources

  1. MDN: HTTP idempotency ↗
  2. PostgreSQL: unique constraints ↗
  3. PostgreSQL: atomic INSERT ON CONFLICT ↗
Retour en haut ↑