Skip to content

Trouble Shooting

Nini1551 edited this page May 13, 2024 · 15 revisions

Déconnexion de la cache Redis

Observation du problème

Après une certaine période d'inactivité sur la page "Woody Stuff", la connexion à la cache Redis est perdue. En conséquence, les requêtes associées à la cache telles que le "Heavy computation" et le "Get last product" renvoient une erreur 500 (Internal Server Error).

Analyse des symptômes, identification et explication du problème

Cette partie présente comment le problème a été identifié. Pour chaque outil / commande utilisée, un sous-chapitre présentera cette outil et ce qu'il nous a permit d'analyser.

Docker service ls

docker service ls permet d'afficher des informations concernant l'ensemble des services du Docker swarm actuel. Les informations affichés pour chaque service sont leur identifiant, leur nom, leur mode actuel, le nombre de replicas total du service comparé au nombre de replicas en ligne, le nom de l'image utilisée et les ports ouverts.

Dans notre cas, aucun service n'a crashé. Le problème ne vient donc pas des services ou des conteneurs Docker.


service-ls

Traces Wireshark

Dans cette trace Wireshark, on peut voir qu’au début, les requêtes effectuées (ici le "Heavy computation") aboutissent avec succès. Le code de réponse 200 (OK) nous indique que les données ont été récupérées dans la cache sans problème.

On peut également remarqué qu’au bout d’un certain temps, les réponses aux requêtes changent et contiennent le code 500 (INTERNAL SERVER ERROR) indiquant qu’il y a un problème avec le serveur cache Redis. Les requêtes ne sont donc plus une réussite.

WIreshark_requests_fail

Dans ce détail de l’un des paquets dont la requête n’a pas aboutie, on peut clairement voir que le serveur renvoie un code de réponse 500 suite à la requête effectuée sur l’URI "http://swarm.l1-1.ephec-ti.be/api/misc/heavy?name=".

Wireshark_fail

Docker service logs

docker service logs <service_name> affiche l'historique des logs d'un certain service. La commande permet ainsi de voir les potentielles erreurs afin de déterminer de quel service provient le problème.

  • Redis

En analysant le service stack_redis, aucune erreur n'est détectée. Le service de cache est d'ailleurs prête à accepter les connexions.

service-logs-redis

  • API

En analysant le service stack_api, un problème est détecté. L'API ping renvoit un code HTTP 200 avec en suivi un message d'erreur provenant du code build-in du package de cache redis de python. L'erreur est TimeoutError: [Errno 110] Connection timed out. Il en est suivi une réponse HTTP de code 500 de l'API misc. Une erreur serveur a donc bien lieu.

L'erreur provient d'une déconnexion du service d'API à la cache Redis.

service-logs-api-1

service-logs-api-2

Docker service inspect

docker service inspect <service_name> affiche sous forme d'objet JSON des informations détaillées à propos du service précisé. Le service API étant directement lié avec la cache Redis avec un network, les deux services seront ici inspectés pour voir si la connection concorde bien.

  • Redis

En analysant les clés "Networks" et "Endpoint/VirtualIPs" de l'inspection du service stack_redis, qui correspondent respecivement aux réseaux du service et aux IPs virtuels utilisés par ce même service au sein du réseau, on observe l'existence d'un réseau identifié "ik69pip48tds72ig74ok6vyve" pour lequel le service est identifié en tant que "redis" à l'adresse 10.0.180.2/24.


service-inspect-redis

  • API

Au sein de l'inspection du service stack-api, il est bien confirmé la connexion avec le même réseau identifié "ik69pip48tds72ig74ok6vyve" mais cette fois-ci à l'adresse 10.0.180.5/24. Les deux adresses appartiennent bien au même réseau IPv4 (10.0.180.0/24). Le service est danc ce réseau nommé "api".

Le problème ne vient pas ici de la connexion entre les services.


service-inspect-api-ip

service-inspect-api-networks

Initialisation de la connexion à la cache Redis

Ces deux lignes de code sont utilisées pour configurer et initialiser une connexion à la cache Redis.

La première ligne récupère la valeur de la variable d’environnement REDIS_HOST. Cette variable contient le nom d’hôte du serveur Redis.

La seconde ligne permet d’initialiser la connexion à la cache Redis via le nom d’hôte récupéré juste avant. La connexion est établie via le port par défaut de Redis (6379). La variable « cache » pourra ensuite être utilisé pour mettre ou récupérer des données en cache ou d’autre opérations.


python_main_redis

Correction du problème et validation de la solution

Pour résoudre le problème de connexion entre notre service API et le service cache Redis, nous avons effectué quelques ajustements stratégiques dans notre processus d'initialisation de la connexion.

Auparavant, l'API initialisait la connexion à Redis dès son démarrage. Cependant, cette approche présentait des inconvénients, notamment une utilisation de ressources potentiellement inutile lorsque la connexion n'était pas immédiatement requise. Pour remédier à cela, nous avons opté pour une initialisation différée de la connexion, en laissant la variable de connexion (appelée "cache") être initialement vide.

Désormais, la connexion à Redis est établie uniquement lorsque nécessaire, ce qui optimise l'utilisation des ressources et améliore la réactivité globale du système.

En plus de ce changement fondamental, nous avons introduit deux nouvelles fonctions pour gérer la connexion à la cache Redis :

  1. is_redis_available() : Cette fonction est chargée de vérifier la disponibilité et la connectivité de la cache. Elle effectue une vérification pour s'assurer que la connexion à Redis peut être établie avec succès.

  2. connect_to_redis() : Cette fonction tente de se connecter à la cache Redis et vérifie que l'opération se déroule sans problème. Si la connexion réussit, la fonction retourne True. Cependant, si la connexion échoue, elle réessaye jusqu'à trois fois avant de retourner False, garantissant ainsi une tentative de connexion robuste et résiliente.


python-main-redis-fixed-1

Afin d'optimiser les performances de notre API sur la route "/api/misc/heavy" qui utilise la fonction get_heavy, nous avons apporté des ajustements significatifs pour intégrer une gestion efficace de la connexion à la cache Redis.

La fonction get_heavy est responsable de récupérer le paramètre "name" de la requête HTTP de l'API, puis effectue un calcul intensif avant de renvoyer un résultat composé de l'heure actuelle et d'une valeur retournée par une autre fonction, utilisant "name" comme paramètre.

Pour garantir une connexion fiable à la cache Redis, nous avons modifié notre approche de la manière suivante :

  1. Tout d'abord, nous appelons la fonction redis_connect pour établir la connexion à Redis. Nous vérifions ensuite si la connexion a réussi.

  2. Si la connexion à la cache Redis réussit, nous recherchons la valeur calculée dans la cache. Si la cache renvoie une valeur, nous la récupérons et la retournons immédiatement comme réponse au client, évitant ainsi le calcul lent et réduisant la charge sur le système.

  3. Cependant, si la cache ne retourne rien ou si nous n'avons pas réussi à nous connecter à la cache, nous procédons au calcul lent comme avant. Après avoir obtenu le résultat du calcul, nous tentons d'écrire ce résultat dans la cache pour une utilisation ultérieure, puis renvoyons la réponse au client.


python-main-redis-fixed-2

Pour améliorer la gestion des produits dans notre API, nous avons effectué des ajustements significatifs dans la fonction add_product, qui est responsable de traiter les demandes sur la route "/api/products".

Désormais, une fois que la fonction add_product récupère la valeur du produit à partir de la requête HTTP, elle entreprend une démarche proactive pour garantir la fraîcheur des données stockées dans la cache Redis.

Voici comment cela fonctionne :

  1. Lorsque la fonction add_product récupère la valeur du produit à partir de la requête HTTP, elle tente immédiatement de se connecter à Redis en utilisant la fonction redis_connect.

  2. Si la connexion à Redis réussit, cela signifie que nous avons accès à la cache et nous pouvons donc procéder à la mise à jour des données.

  3. Dans ce cas, la fonction add_product efface toute référence préexistante à ce produit dans la cache. Cela garantit que seule la référence du produit le plus récent est stockée dans la cache.

  4. Une fois que la mise à jour de la cache est effectuée avec succès, la fonction add_product termine son exécution, assurant ainsi l'intégrité des données dans la cache Redis.


python-main-redis-fixed-3

Pour optimiser la gestion des produits dans notre API, nous avons revu la fonction get_last_product, utilisée sur la route "/api/products/last", afin de garantir une connexion préalable à la cache Redis avant d'effectuer toute opération.

Voici le processus que nous avons mis en place :

  1. Lorsque la fonction get_last_product est invoquée, elle tente d'abord de se connecter à la cache Redis.

  2. Si la connexion à la cache réussit, la fonction get_last_product recherche alors le dernier produit dans la cache. Si un produit est trouvé dans la cache, il est immédiatement retourné au client.

  3. Cependant, si la connexion à la cache échoue ou si aucun produit n'est trouvé dans la cache, la fonction get_last_product effectue une opération plus lente dans la base de données pour récupérer le dernier produit.

  4. Une fois que le dernier produit est récupéré avec succès dans la base de données, il est ajouté à la cache pour une utilisation future.

  5. Enfin, la fonction get_last_product retourne le dernier produit récupéré, que ce soit à partir de la cache ou de la base de données, au client.


python-main-redis-fixed-4

En adoptant cette approche cohérente à travers les multiples routes de notre API, nous nous assurons désormais que la connectivité à la cache Redis est établie avant toute tentative de requête, garantissant ainsi des performances optimales et une disponibilité constante des données pour nos utilisateurs.

Traces Wireshark

Pour vérifier que les changement sont correctement implémentés, on effectue de nouvelles traces Wireshark afin de vérifier si la connexion avec le serveur de cache Redis reste stable.

Nous avons ici réalisé plusieurs requêtes à des intervalles différents pour vérifier que la connexion avec le serveur de cache Redis ne se coupe plus au bout d’un certain temps. Toutes les requêtes sont une réussite. Le code de réponse 200 (OK) nous indique que les données ont été récupérées dans la cache sans problème.

Wireshark_requests_success

Dans le détail de ce paquet, on peut clairement voir que le serveur renvoie un code de réponse 200 suite à la requête effectuée sur l’URI "http://swarm.l1-1.ephec-ti.be/api/misc/heavy?name=".

Ces résultats montrent que les changements qui ont été apportés ont permis de résoudre le problème de déconnexion qui était rencontré auparavant. La connexion avec le serveur de cache Redis reste stable.

Wireshark_success