Conception du système : préparez-vous au pire
Lors du développement d'une nouvelle application ou d'une nouvelle fonctionnalité, l'une des choses importantes auxquelles vous devez prêter attention est la capacité de votre application à s'auto-récupérer en cas d'échec. Votre code peut être excellent et répondre aux exigences de l'entreprise MAIS, est-il assez bon pour le faire également en cas d'échecs inattendus ? La résilience de votre application est-elle suffisante pour surmonter automatiquement ces défaillances ?
Parfois, vous ne serez pas implicitement chargé de mettre en œuvre des mécanismes de résilience et de secours dans le cadre des exigences de l'entreprise, car les scénarios qu'il est censé gérer peuvent être très techniques et axés sur le développement. Les mécanismes liés à la famille des exigences non fonctionnelles.
De quels « échecs inattendus » parlons-nous ?
Eh bien, divers scénarios peuvent se produire et conduire votre application à un état de panne/instable. Par exemple:
- Timeout / Service indisponible d'une API externe que votre application utilise
- Bugs dans un tiers utilisé par votre application
- Échec des opérations de la base de données
- Charge imprévue sur l'application qui entraîne une dégradation des performances et donc un refus des requêtes entrantes
- Erreur inattendue entraînant une perte de données
- les processus de longue durée gérés par l'application sont bloqués dans leur état en raison de la perte de données
Avoir une application sans les mécanismes de récupération automatique et de secours pertinents ne durera pas à long terme. Ces "échecs inattendus" finiront par se produire, cela nuira à votre SLA et à l'expérience client. Cela peut entraîner une perte de données qui sera difficile à récupérer et vous obligera à intervenir manuellement et à faire quelques magies afin de résoudre le problème. Pensez maintenant aux applications qui gèrent des centaines de milliers de requêtes par minute, une petite panne peut avoir un impact important qui peut être difficile à réparer manuellement et coûtera beaucoup de temps et d'argent à votre organisation.
Vous devez donc vous préparer au pire.
Passons en revue certains scénarios courants et proposons des solutions qui peuvent augmenter le niveau de résilience de votre application et ses capacités de secours afin que vous puissiez dormir en paix la nuit :)
Mécanisme de nouvelle tentative
Surtout lorsqu'il s'agit d'opérations d'E/S telles que l'envoi de requêtes http, la lecture/écriture à partir d'une base de données ou d'un fichier, etc., le risque que vous rencontriez une erreur est courant. Par exemple : service indisponible, erreur de serveur interne, délai d'attente de l'opération, etc.
Beaucoup de ces erreurs peuvent être transitoires et s'arrêter en quelques secondes, ce qui signifie qu'il y a de fortes chances que la prochaine tentative après quelques secondes fonctionne.
Alors… pourquoi échouer votre flux d'affaires juste à cause d'un petit problème temporaire ? Réessayer! Utilisez un mécanisme de nouvelle tentative sur l'opération qui garantira que si le problème est temporaire, votre application le surmontera automatiquement.
Faites attention aux éléments suivants :
- Pouvoir configurer et limiter le nombre de tentatives d'exécution de votre application. Un nombre infini / élevé de tentatives peut bloquer votre application et refuser d'autres demandes car elle sera occupée par des tentatives sans fin
- mettre en œuvre le bakeoff exponentiel. Augmentez de façon exponentielle l'intervalle entre les nouvelles tentatives.
Cela augmentera les chances que le problème temporaire (sur le serveur/base de données/tiers) soit résolu et que votre prochaine tentative réussisse. De plus, l'utilisation d'un intervalle exponentiel donne également de la grâce au tiers qui peut être sous charge et ne peut donc pas servir ses clients. L'exécution de nouvelles tentatives immédiates sans délai peut aggraver le problème. - Assurez-vous de comprendre sur quelles erreurs il devrait y avoir de nouvelles tentatives et sur lesquelles non. Toutes les erreurs ne peuvent pas être résolues par une nouvelle tentative. Par exemple, disons que votre requête http a échoué avec le code d'état 'Bad request' (400). Cela signifie que quelque chose avec les données de la demande est erroné et a été rejeté par le serveur. Peu importe le nombre de tentatives effectuées par votre application, elle se terminera par le même résultat : un échec.
Faire de nouvelles tentatives sur ce type d'erreurs est redondant et aura un impact sur les performances de votre application. Vous devez faire la distinction entre les différentes erreurs et décider quelle nouvelle tentative peut être effectuée.
Il existe plusieurs bibliothèques open source qui vous fournissent déjà toutes les fonctionnalités pertinentes du mécanisme de nouvelle tentative afin que vous n'ayez pas à l'écrire à partir de zéro. Par exemple, dans .NET, vous avez une bibliothèque appelée Polly.
N'oubliez pas de poser la question, que dois-je faire si toutes les tentatives échouent ? Il s'agit d'une question commerciale et la réponse peut être modifiée entre les scénarios.
Définissez le délai d'attente pour vos processus
Certaines applications gèrent des processus de longue durée. Le processus a un état qui indique la progression. L'avancement du processus dépend parfois d'opérations longues ou d'une réponse asynchrone d'un tiers. Étant donné que les choses peuvent mal tourner (et elles tournent toujours mal), votre processus peut être bloqué à jamais dans un état non final et rester "ouvert". Par exemple, le tiers a eu un échec avec le renvoi de la réponse.
Ok, donc ils resteront ouverts. Quel est le gros problème?
Il est fort probable que d'autres systèmes dépendent de vos processus et qu'ils attendent leur résultat. Votre application bloquera l'ensemble du flux d'activité, ce qui aura un impact sur les autres services SLA et l'expérience utilisateur.
De plus, ces processus ouverts compliqueront les enquêtes et l'analyse des données car ils se comportent différemment et peuvent nuire à vos statistiques.
Je ne veux pas ça ! Comment puis-je le résoudre ?
Tout d'abord, définissez quelle est la durée appropriée et acceptable pendant laquelle votre processus peut rester dans un état non final. Il s'agit d'une question commerciale qui doit être déterminée par le SLA auquel vous êtes obligé et par l'évaluation du temps total raisonnable qui devrait être nécessaire pour les opérations à l'intérieur du processus. Par exemple, si mon processus dépend d'un tiers dont le temps de réponse moyen est de 10 minutes, vous pouvez définir un délai d'attente de 20 à 30 minutes.
Maintenant, vous pouvez sauter pour la mise en œuvre. L'implémentation de base peut consister à créer une opération planifiée qui s'exécute toutes les X minutes et recherche des processus ouverts pendant plus de Y minutes (valeur du délai d'attente). S'il est trouvé, il suffit de déplacer le processus dans un état final, peut être un état d'échec/d'annulation.
De cette façon, vous avez la possibilité d'échouer/d'annuler gracieusement le processus. Cela se fait de manière gérable et offre à votre application la possibilité de notifier le résultat pertinent à ses clients en attente.
Remarquer! le modèle n'a pas résolu le problème lui-même MAIS il vous a permis de le gérer automatiquement et d'exécuter le flux de secours. Être capable de communiquer qu'il y a un problème et respecter le SLA promis.
Regardons un scénario réel qui utilise ce modèle :
mon équipe chez Payoneer est responsable du processus d'approbation automatique des documents des clients. Le processus d'approbation d'un document est un processus hors ligne construit en plusieurs étapes et implique une intégration avec des fournisseurs externes qui fournissent des résultats d'analyse via une communication asynchrone.
Notre système implémente le modèle de délai d'attente et, à l'expiration du délai, le système déplace les documents du client pour qu'ils soient examinés manuellement par un représentant au lieu d'attendre le résultat automatique.
De cette façon, nous n'impactons pas le SLA que nous avons devant le client concernant le temps nécessaire pour examiner ses documents.
Ne relayez pas sur le rappel/webhook uniquement - Mettez en œuvre le mécanisme d'interrogation
Lorsqu'il s'agit d'une communication asynchrone, la réponse est renvoyée au client sous forme de rappel. Il peut s'agir du serveur déclenchant un webhook avec les données ou du serveur publiant un événement via un bus d'événements/courtier de messages.
Il existe des scénarios où le message n'a pas été reçu par le client ou a été reçu mais n'a pas été traité correctement et a donc été abandonné, ce qui entraîne une perte de données. Dans ces scénarios, le client attend toujours la réponse mais le serveur l'a déjà envoyée et ne l'enverra plus.
dans la section précédente, nous avons mentionné le modèle de délai d'attente qui ne résout pas le problème, mais vous permet de l'échouer gracieusement et d'activer un flux de secours gérable. En implémentant un mécanisme d'interrogation, vous serez en mesure de résoudre automatiquement certains des problèmes et de rester sur le "chemin heureux" du flux d'affaires.
Au lieu d'attendre simplement que le serveur vous fournisse la réponse de manière asynchrone par push, implémentez un processus planifié qui sera exécuté toutes les X minutes et recherchera les requêtes en attente depuis plus de Y minutes. Pour ces requêtes, votre application enverra une requête synchrone au serveur pour obtenir les données de réponse. Si la requête d'origine a fini d'être traitée par le serveur, celui-ci pourra vous envoyer le résultat. Dans ce cas, votre application poursuivra le flux métier de la même manière que si les données avaient été reçues par le webhook. Sinon, la demande d'origine est toujours en cours de traitement par le serveur et il n'y a rien à renvoyer. Vous avez juste besoin de continuer à attendre.
En implémentant ce mécanisme, vous avez pu résoudre automatiquement les problèmes inattendus liés à la communication asynchrone et ainsi éviter de faire échouer le flux ou d'intervenir manuellement afin de restaurer les données du serveur.
Remarquer! Tous les services que vous intégrerez n'auront pas la capacité de vous fournir des points de terminaison pour l'interrogation en plus de la communication asynchrone qu'ils offrent. S'ils ne l'ont pas, cela vaut la peine de se demander si cela peut être fait à l'avenir.
Il est vrai qu'il y a une surcharge de performances avec cette solution MAIS en choisissant les bons intervalles, elle sera transparente et l'avantage qu'elle procure en cas de panne est inestimable.
Éviter le verrouillage du fournisseur
Il est courant que l'application utilise des fournisseurs externes afin d'atteindre ses objectifs commerciaux. Il n'est pas possible que chaque organisation développe chaque élément de logique en interne, surtout si la logique est complexe et éloignée de l'activité principale de l'organisation. Par conséquent, vous préférerez probablement vous intégrer à un fournisseur qui sera en mesure de vous fournir une partie de la logique dont vous avez besoin pour compléter votre flux commercial.
Ainsi, après une recherche et un POC, vous avez trouvé un fournisseur adapté à vos besoins.
L'intégration s'est bien déroulée et… VOILA ! votre flux d'affaires est opérationnel et vous servez des centaines de milliers de clients par jour.
Maintenant, permettez-moi de vous poser une question. Que se passera-t-il si un jour ce fournisseur externe cesse de fonctionner ? que se passera-t-il si elle fait faillite ? S'il a été piraté et que le service ne sera pas disponible pendant une longue période ? Ou peut-être ne serez-vous tout simplement pas satisfait de la performance du fournisseur ou peut-être de la façon dont il est géré, car chaque mois, de nouveaux changements de rupture obligent votre équipe de développement à travailler 24 heures sur 24 pour ajuster l'intégration ?
Surtout lorsqu'il s'agit de flux commerciaux principaux et sensibles, ce scénario est critique et peut paralyser une partie de votre organisation et avoir un impact énorme sur vos clients.
Revenons au processus d'approbation automatique des documents du client géré par mon équipe chez Payoneer, dans le cadre du processus, nous utilisons des fournisseurs externes afin d'extraire le texte pertinent du document et de le traiter. Nous avons choisi de nous intégrer à 2 fournisseurs qui nous fournissent ce résultat et ont la priorité entre eux, donc si l'un échoue ou a beaucoup de faux positifs, nous pouvons passer à l'autre sans aucun temps d'arrêt du flux commercial. Bien sûr, cela nous donne également la certitude que si quelque chose devait arriver à l'un d'entre eux, nous en aurons toujours un autre sur qui nous appuyer.
Bien sûr, vous n'êtes pas toujours obligé de créer cette sauvegarde. C'est une question d'optimisation des ressources et de degré d'importance du flux d'affaires avec lequel vous traitez.
Modèle de disjoncteur
Contrairement au modèle de nouvelle tentative qui suppose de résoudre les défaillances transitoires, le modèle de coupure de circuit est là pour empêcher l'application d'invoquer une opération qui a une forte probabilité d'échouer.
Pensez que vous utilisez un tiers via l'API REST qui a actuellement un bogue qui fait échouer toutes les demandes. Disons que l'ETA pour le correctif est de 4 heures.
Avec le modèle de nouvelle tentative, l'application essaiera plusieurs fois d'invoquer la requête qui échouera finalement. Ces nouvelles tentatives inutiles auront un impact sur les performances de l'application et du tiers.
Au lieu de cela, le modèle de coupure de circuit permettra à l'application d'échouer rapidement et d'essayer de se rétablir progressivement lorsque le problème sera résolu. Cela évite de générer des erreurs et une dégradation des performances.
Alors, comment ça marche?
Le mécanisme agit comme un proxy pour l'opération que vous souhaitez exécuter.
Il a 3 états :
- Fermé - Signifie que l'opération est autorisée à être exécutée régulièrement.
Le nombre d'échecs est compté dans cet état. Si le nombre de pannes dépasse le seuil défini dans un intervalle de temps défini, le mécanisme passera à l'état ouvert. - Ouvert - Signifie que le mécanisme empêchera l'exécution de l'opération car elle a connu de nombreux échecs et qu'il préférera donc échouer rapidement sans essayer de l'exécuter. Dans cet état, nous avons un délai d'attente. Lorsqu'il est atteint, le mécanisme passe à l'état semi-ouvert.
- À moitié ouvert - Signifie qu'un nombre limité de demandes d'exécution de l'opération est autorisée et transmise. Cet état sert à vérifier que le problème a été résolu. Si ce nombre limité d'opérations est exécuté avec succès, le mécanisme suppose que le problème a été résolu et fait passer l'état du mécanisme à fermé. Sinon, il revient à Open car le problème existe toujours.
Terminaux dédiés pour une intervention manuelle simple et efficace
Cela dit, parfois nous ne sommes pas préparés avec une solution automatisée pour tous les cas qui peuvent survenir. Parfois, notre solution automatisée ne viendra qu'après avoir rencontré le scénario pour la première fois et compris comment le résoudre par l'automatisation. Par conséquent, dans ces cas, une intervention manuelle est inévitable. La plupart du temps, ces interventions manuelles vous obligent à passer par les "entrailles" du service comme : modifier directement les valeurs via la base de données ou restructurer manuellement un message et l'envoyer directement via le courtier de messages.
Ici aussi, nous pouvons être préparés à l'avance afin que cette intervention manuelle soit gérable, sûre et efficace. Créez simplement une API interne dans votre application qui permettra ces opérations manuelles courantes.
Par exemple, créez un point de terminaison qui vous permettra d'annuler un processus géré par votre application. Ou créez un point de terminaison qui permettra de republier un résultat d'un ancien processus afin qu'un autre système puisse le consommer à nouveau s'il a été perdu ou n'a jamais été déclenché.
En créant cette API interne, bien qu'elle nécessite quelqu'un pour la déclencher, elle présente de nombreux avantages :
- L'opération est gérable par l'application. L'application est propriétaire de la modification et en est consciente.
- L'opération a été testée par QA car elle faisait partie du développement. Par conséquent, vous réduisez les risques d'erreurs
- parfois, l'opération n'est pas aussi simple que de simplement mettre à jour la valeur dans la base de données. Parfois, il y a aussi des actions secondaires qui doivent être faites. Toutes ces actions secondaires peuvent être gérées sous l'API interne pour une utilisation facile.
- Gain de temps
De conclure
Votre candidature DOIT toujours être préparée au pire !
Plus votre application saura s'auto-récupérer et disposer de solutions de secours, plus ses performances augmenteront, l'impact sur l'expérience client sera faible, plus vite vous pourrez surmonter les pannes et faire gagner un temps et de l'argent précieux à votre organisation.
Lors de la conception de votre application / fonctionnalité, vous devez y prêter attention. Vous devez vous poser les bonnes questions et identifier les points faibles afin de préparer le traitement pertinent et éviter les mauvaises surprises en production.