Passer au contenu
DedicatedPHP Contact

Quand un processus PHP reste inachevé : concevoir des compensations

Découvrez comment récupérer des flux PHP en échec entre plusieurs systèmes : quand réessayer, quand compenser et comment consigner les résultats et faire remonter les exceptions.

Schéma éditorial d’un processus PHP avec des étapes entre une base de données locale et une API externe, incluant des nouvelles tentatives et des compensations

Un processus métier peut accomplir une partie de son travail, puis échouer avant d’arriver à son terme. Par exemple, une application PHP crée un compte dans sa base de données, mais ne parvient pas à le provisionner auprès d’un service externe. Dans ce cas, relancer le flux sans autre précaution peut produire des effets en double ; le déclarer en échec peut laisser les systèmes dans un état incohérent. Les compensations dans les processus distribués avec PHP permettent de définir comment réagir à ces résultats partiels, sans supposer que plusieurs systèmes participent à une transaction atomique unique.

La question importante n’est pas seulement de savoir comment annuler ce qui s’est passé. Certains effets ne peuvent pas être exactement annulés, et une compensation peut elle aussi échouer. La conception doit rendre visible l’état réel de l’opération, permettre de la récupérer et indiquer quand une intervention humaine est nécessaire.

Les transactions locales, les nouvelles tentatives et les compensations résolvent des problèmes différents

Les transactions locales, les nouvelles tentatives et les compensations résolvent des problèmes différents — guía visual de DedicatedPHP

Une transaction de base de données protège les modifications effectuées dans les limites de cette base de données. Si le processus enregistre plusieurs lignes et qu’une opération échoue avant le COMMIT, il peut exécuter un ROLLBACK pour annuler ces modifications. Mais ce rollback ne retire pas un e-mail déjà envoyé, n’annule pas automatiquement une requête déjà acceptée par une API et n’annule pas les modifications validées dans une autre base de données.

Une nouvelle tentative réexécute une action qui a pu échouer en raison d’un problème temporaire, comme un timeout ou une indisponibilité. Elle n’est appropriée que si les conséquences de sa répétition sont connues. La requête a pu parvenir au système externe même si la réponse a été perdue ; sans clé d’idempotence ou vérification de l’état, la nouvelle tentative pourrait créer un doublon.

Une compensation exécute une action métier qui contrebalance une étape déjà terminée : par exemple, désactiver un accès qui vient d’être créé. Ce n’est pas un rollback technique et cela ne garantit pas de retrouver exactement l’état antérieur. Une réconciliation, en revanche, compare ce qui est consigné localement à l’état des systèmes concernés et aide à détecter et résoudre les divergences. Elle peut être nécessaire même lorsque des nouvelles tentatives et des compensations sont prévues.

Modélisez chaque opération comme une séquence d’étapes observables

Avant d’écrire des gestionnaires d’exceptions, décrivez le flux sous forme d’étapes indépendantes. Pour chacune, précisez le système qu’elle modifie, le résultat qui constitue une réussite, la manière d’identifier l’opération, si elle peut être répétée sans risque et quelle solution de repli existe si elle ne peut pas être menée à bien. Définissez également quels effets doivent rester cohérents ; cette règle métier constitue l’invariant qui guidera la récupération.

Enregistrez dans l’application un journal de l’opération et de ses étapes. Un modèle simple peut comprendre un identifiant d’opération stable, l’état général, le nom de l’étape, le résultat, le nombre de tentatives, les horodatages et la dernière erreur pertinente. Des états tels que en attente, en cours, terminée, en échec, compensation en cours et révision requise permettent de distinguer ce qui est connu de ce qui n’a pas encore été confirmé.

Enregistrez la modification locale et le travail à effectuer de manière atomique lorsqu’ils doivent rester liés. Une file de travaux peut ensuite traiter l’appel externe ; le pattern boîte d'envoi permet notamment d’éviter que la modification en base de données soit validée alors que la notification destinée à déclencher l’étape suivante est perdue. Il n’élimine pas les défaillances réseau : le consommateur doit toujours tolérer les messages répétés et les résultats incertains.

Évitez de laisser une transaction de base de données ouverte pendant un appel réseau. Les appels externes peuvent avoir une latence élevée ou rester bloqués, tandis qu’une transaction prolongée retient des ressources sans rendre les deux systèmes atomiques. Validez le travail local selon une séquence explicite et consignez les informations nécessaires pour poursuivre ou compenser.

Concevez des compensations explicites, limitées et idempotentes

Pour chaque étape produisant un effet, déterminez s’il existe une compensation et à quel moment l’exécuter. La compensation n’a pas besoin d’être l’inverse exact de l’action. Si un compte a déjà reçu une notification, il est impossible d’effacer le fait que le message a été lu ; il peut être nécessaire d’enregistrer une correction ou de signaler l’annulation. Si une action est irréversible ou entraîne des conséquences juridiques ou financières, la bonne réponse peut être d’interrompre le flux et de faire remonter le cas.

  • Idempotence : exécuter deux fois la même compensation ne doit pas produire d’effets supplémentaires. Utilisez un identifiant d’opération stable ou une clé idempotente si le système le permet, et consignez le résultat.
  • Ordre : définissez les étapes à compenser et leur ordre. En général, on tente de les annuler dans l’ordre inverse de leur exécution, mais les dépendances et les règles métier peuvent imposer un autre ordre.
  • Limites : définissez les erreurs qui autorisent de nouvelles tentatives, leur nombre maximal et le moment où il faut s’arrêter. Prévoyez des délais d’attente entre les tentatives et évitez les nouvelles tentatives indéfinies.
  • Incertitude : si le délai d’attente d’une API expire, vérifiez son état ou utilisez une clé d’idempotence avant de répéter une création ou d’exécuter une compensation potentiellement incorrecte.

La logique de compensation doit correspondre à une opération métier identifiable, et non à un bloc générique qui efface des données dès qu’il intercepte une exception. Conservez l’historique nécessaire pour expliquer ce qui s’est passé et vérifier que la réponse respecte les règles du produit.

Exemple hypothétique : création d’un compte dans deux systèmes

Supposons un flux qui crée un compte local et demande à un fournisseur externe d’activer un espace de travail. L’application attribue un identifiant d’opération et enregistre la création comme étant en attente. Dans une transaction locale, elle enregistre le compte et un travail destiné à demander le provisionnement. Un worker PHP traite ce travail et consigne l’identifiant externe ainsi que le résultat de la requête.

Si le fournisseur confirme l’activation, le flux marque l’opération comme terminée. S’il renvoie une erreur temporaire connue, le travail peut faire l’objet de nouvelles tentatives limitées, avec la même clé d’idempotence. Si la réponse est incertaine, l’application commence par vérifier si l’espace de travail existe. Si le fournisseur confirme sa création, mais qu’une exigence locale ultérieure empêche de terminer la création du compte, le système peut demander sa désactivation à titre de compensation et en enregistrer le résultat.

Si la désactivation échoue, l’opération ne doit pas être déclarée comme annulée. Elle reste dans un état visible de récupération en attente ou de révision, avec les identifiants local et externe, le nombre de tentatives et l’erreur. L’équipe des opérations a besoin d’une procédure pour vérifier l’état auprès du fournisseur, décider de l’action autorisée et consigner l’issue. Cet exemple est hypothétique : les actions concrètes dépendent des capacités de l’API et des règles métier.

Quand la compensation échoue aussi : visibilité et révision

Un flux distribué ne peut pas garantir que chaque action sera automatiquement annulée. Prévoyez une issue explicite lorsque le nombre maximal de nouvelles tentatives est atteint, que la réponse externe est ambiguë ou que la compensation échoue. Conservez l’état sans masquer l’incohérence et évitez de présenter l’opération à l’utilisateur comme terminée si elle ne l’est pas encore.

Fournissez au support ou à l’équipe des opérations une vue indiquant l’état de chaque étape, la dernière erreur, les identifiants de corrélation et les actions autorisées. La révision humaine doit être soumise à des contrôles d’accès et laisser une trace d’audit de la décision. Exposez des métriques et des alertes pour détecter les opérations bloquées, les compensations en échec et les divergences qui dépassent le délai opérationnel acceptable. Une tâche de réconciliation périodique peut comparer les enregistrements locaux à ceux du système externe et créer des cas à traiter pour résoudre les écarts.

Testez les défaillances partielles et déterminez si une compensation est nécessaire

Testez les défaillances partielles et déterminez si une compensation est nécessaire — guía visual de DedicatedPHP

Les tests doivent couvrir davantage que le parcours nominal. Simulez des erreurs avant et après chaque effet, des réponses tardives, des timeouts dont l’issue est inconnue, des messages en double, des interruptions de processus et des échecs pendant la compensation. Vérifiez l’état persistant après le redémarrage du worker et confirmez que les nouvelles tentatives ne créent pas de comptes ni d’autres opérations en double.

  • L’identifiant de l’opération et les résultats de chaque étape sont-ils conservés ?
  • Un message répété produit-il le même résultat sans effets en double ?
  • L’état externe est-il vérifié lorsque la réponse à une requête est incertaine ?
  • La compensation est-elle exécutée dans l’ordre prévu et peut-elle être répétée sans risque ?
  • Un échec de compensation laisse-t-il l’opération visible et l’envoie-t-il dans le circuit de révision ?
  • Est-il possible de réconcilier ensuite l’état local avec le système externe ?
  • Les tests vérifient-ils les invariants métier, et pas seulement les codes de réponse ?

Si le flux concerne un seul système et qu’une transaction locale protège toutes ses modifications, cette transaction et une récupération simple peuvent suffire. Si les effets externes peuvent être répétés avec des garanties connues, des nouvelles tentatives idempotentes et une réconciliation peuvent suffire. Lorsqu’une étape validée doit être contrebalancée pour préserver une règle métier, définissez une compensation. Cette complexité supplémentaire en vaut la peine lorsque le coût d’états partiels non détectés dépasse celui de la tenue du journal, des tests et de la procédure opérationnelle.

Vous souhaitez appliquer ces idées à votre projet ?Parlons de votre plateforme PHP.
Afficher les services associés