C'est une question classique qui m'a été posée récemment lors d'une interview Comment appeler plusieurs services Web tout en conservant une sorte de gestion des erreurs au milieu de la tâche. Aujourd'hui, dans le calcul haute performance, on évite les engagements en deux phases. J'ai lu un article il y a de nombreuses années sur ce qu'on appelait le «modèle Starbuck» pour les transactions: Pensez au processus de commande, de paiement, de préparation et de réception du café que vous commandez chez Starbuck ... Je simplifie à l'extrême les choses, mais un modèle de validation en deux phases suggérez que l'ensemble du processus consisterait en une seule transaction d'emballage pour toutes les étapes impliquées jusqu'à ce que vous receviez votre café. Cependant, avec ce modèle, tous les employés attendraient et arrêtaient de travailler jusqu'à ce que vous ayez votre café. Vous voyez l'image?
Au lieu de cela, le «modèle Starbuck» est plus productif en suivant le modèle du «meilleur effort» et en compensant les erreurs dans le processus. Tout d'abord, ils s'assurent que vous payez! Ensuite, il y a des files d'attente de messages avec votre commande attachée à la tasse. Si quelque chose ne va pas dans le processus, comme si vous n'avez pas obtenu votre café, ce n'est pas ce que vous avez commandé, etc., nous entrons dans le processus de compensation et nous nous assurons que vous obtenez ce que vous voulez ou vous rembourser, c'est le modèle le plus efficace pour une productivité accrue.
Parfois, Starbuck gaspille un café, mais le processus global est efficace. Il y a d'autres astuces à penser lorsque vous créez vos services Web, comme les concevoir de manière à ce qu'ils puissent être appelés un nombre illimité de fois tout en fournissant le même résultat final. Donc, ma recommandation est:
Ne soyez pas trop fin dans la définition de vos services web (je ne suis pas convaincu du battage médiatique des micro-services ces jours-ci: trop de risques d'aller trop loin);
Async augmente les performances, alors préférez être asynchrone, envoyez des notifications par e-mail chaque fois que possible.
Construisez des services plus intelligents pour les rendre "rappelables" un nombre illimité de fois, en les traitant avec un uid ou un taskid qui suivra l'ordre de bas en haut jusqu'à la fin, en validant les règles métier à chaque étape;
Utilisez des files d'attente de messages (JMS ou autres) et détournez-les vers des processeurs de gestion des erreurs qui appliqueront des opérations à la «restauration» en appliquant des opérations opposées, d'ailleurs, travailler avec un ordre asynchrone nécessitera une sorte de file d'attente pour valider l'état actuel du processus, alors considérez cela;
En dernier recours, (car cela peut ne pas arriver souvent), mettez-le dans une file d'attente pour le traitement manuel des erreurs.
Revenons au problème initial qui a été signalé. Créez un compte et créez un portefeuille et assurez-vous que tout a été fait.
Disons qu'un service Web est appelé pour orchestrer l'ensemble de l'opération.
Le pseudo code du service Web ressemblerait à ceci:
Appelez le microservice de création de compte, transmettez-lui des informations et un microservice de création de compte ID de tâche unique 1.1 vérifiera d'abord si ce compte a déjà été créé. Un identifiant de tâche est associé à l'enregistrement du compte. Le microservice détecte que le compte n'existe pas, il le crée et stocke l'ID de la tâche. REMARQUE: ce service peut être appelé 2000 fois, il effectuera toujours le même résultat. Le service répond par un "reçu contenant des informations minimales pour effectuer une opération d'annulation si nécessaire".
Appelez la création du portefeuille, en lui donnant l'ID de compte et l'ID de tâche. Disons qu'une condition n'est pas valide et que la création du portefeuille ne peut pas être effectuée. L'appel retourne avec une erreur mais rien n'a été créé.
L'orchestrateur est informé de l'erreur. Il sait qu'il doit abandonner la création du compte, mais il ne le fera pas lui-même. Il demandera au service de portefeuille de le faire en transmettant son «reçu d'annulation minimal» reçu à la fin de l'étape 1.
Le service de compte lit le reçu d'annulation et sait comment annuler l'opération; le reçu d'annulation peut même inclure des informations sur un autre microservice qu'il aurait pu appeler lui-même pour faire une partie du travail. Dans cette situation, le reçu d'annulation peut contenir l'ID de compte et éventuellement des informations supplémentaires nécessaires pour effectuer l'opération inverse. Dans notre cas, pour simplifier les choses, disons que c'est simplement supprimer le compte en utilisant son identifiant de compte.
Maintenant, disons que le service Web n'a jamais reçu le succès ou l'échec (dans ce cas) que l'annulation de la création du compte a été effectuée. Il appellera simplement à nouveau le service d'annulation du compte. Et ce service ne devrait normalement jamais échouer car son objectif est que le compte n'existe plus. Il vérifie donc s'il existe et voit que rien ne peut être fait pour l'annuler. Il revient donc que l'opération est un succès.
Le service Web renvoie à l'utilisateur que le compte n'a pas pu être créé.
Ceci est un exemple synchrone. Nous aurions pu le gérer d'une manière différente et placer le cas dans une file d'attente de messages destinée au service d'assistance si nous ne voulions pas que le système récupère complètement l'erreur ". J'ai vu cela se faire dans une entreprise où ce n'est pas assez des hooks pouvaient être fournis au système dorsal pour corriger les situations. Le service d'assistance recevait des messages contenant ce qui avait été effectué avec succès et disposait de suffisamment d'informations pour corriger les choses, tout comme notre reçu d'annulation pouvait être utilisé de manière entièrement automatisée.
J'ai effectué une recherche et le site Web de Microsoft a une description de modèle pour cette approche. C'est ce qu'on appelle le modèle de transaction de compensation:
Modèle de transaction compensatoire