Soif d'expertise ?

Abonnez-vous à notre newsletter Tea O'Clock !

Je m'abonne

Traitement des données en temps réel à grande échelle : Comment garantir l'intégrité des données

Albert De Watrigant
Publié le
18/6/2024
Avez-vous déjà voulu créer une architecture serverless cloud capable de streamer vos données, pour finalement vous rendre compte qu'il y avait des écarts de données entre l'entrée et la sortie de votre architecture ? Si c'est le cas, nous allons tenter de répondre à votre question dans cet article, en présentant notre cas d'utilisation d'une architecture capable de traiter 3 000 requêtes par seconde.

Si un grand nombre de cas d'usage data peuvent être résolus avec une solution de traitement par lots, certaines situations nécessitent nécessairement une solution de streaming. Avec Google Cloud, vous pouvez y parvenir grâce à des services sans serveur, ce qui vous permet de ne payer que pour les ressources que vous utilisez réellement. Dans cet article, nous allons explorer comment fonctionne une architecture Google Cloud : elle récupère des requêtes HTTPS en entrée, traite ces requêtes dès qu'elles arrivent et écrit la sortie de ces requêtes dans un entrepôt de données. Nous nous concentrerons sur la partie de notre architecture qui garantit le même nombre de messages que la sortie.

Pour clarifier les choses, voici ce que fait notre architecture :

Avec plus de détails et des outils GCP, voici à quoi cela ressemble :

Fonctionnement général

Dans notre architecture, les demandes entrantes sont récoltées par une fonction cloud, qui lit la demande, vérifie si le format est valide, puis transmet l'information via un message envoyé à un sujet Pub/Sub. Nous avons choisi d'utiliser une fonction cloud car le code à exécuter est très simple, et le service est capable d'augmenter son nombre d'instances en fonction de l'évolution du trafic entrant, sans que nous ayons à nous en préoccuper.

Nous avons donc créé un abonnement "Push" entre le sujet Pub/Sub et le Cloud Run. Nous avons choisi la configuration "Push" pour préserver la nature "streaming" de notre architecture. Le message est ensuite traité par le Cloud Run, qui peut prendre un peu plus de temps que le Cloud Function si nécessaire. Le Cloud Run écrit ensuite le résultat de son traitement dans une table BigQuery.

Points forts architecturaux

En séparant l'acquisition du traitement, nous nous assurons que la demande envoyée en entrée par l'utilisateur est traitée aussi rapidement que possible. Le traitement peut alors prendre un peu plus de temps pour faire son travail, car la latence est moins critique. En fait, si la charge de travail devient trop importante, l'abonnement Pub/Sub conservera les messages et les renverra jusqu'à ce que le Cloud Run les ait traités.

De cette manière, nous pouvons garantir que l'utilisateur ne subira aucune latence particulière, même pendant les pics de trafic. Nous pouvons également être sûrs que tous les messages "valides" seront traités par notre architecture, puisque Pub/Sub garantit qu'au moins une occurrence d'un message sera envoyée et reçue par le destinataire (le Cloud Run dans notre cas).

Problèmes rencontrés

Après quelques jours de tests, nous avons réalisé que notre architecture traitait effectivement tous les messages entrants, mais que certains messages étaient présents en plusieurs copies dans la table BigQuery. Cela signifie que le même message était traité plusieurs fois par notre Cloud Run. Comment cela se fait-il ? Après un certain nombre d'investigations, nous avons découvert que la majorité des doublons arrivaient dans la table BigQuery lors des pics de trafic. Lors de ces pics de requêtes, le nombre d'instances de Cloud Run augmente rapidement. Il est donc possible que l'abonnement Pub/Sub envoie le même message à différentes instances, puisque ce service garantit AU MOINS une livraison d'un message. Si vous souhaitez restreindre Pub/Sub à l'envoi d'un seul message, c'est possible, mais uniquement avec un abonnement "Pull", ce qui signifie que vous êtes face à une architecture Batch.

Pour résoudre le problème des messages en double dans notre base de données, nous avions deux possibilités : soit filtrer les messages lors du traitement, en supprimant ceux qui avaient déjà été traités, soit nettoyer la base de données à l'aide d'une requête SQL avec une certaine fréquence. Nous avons décidé de mettre en œuvre la première solution. Ce choix est dû au fait que nous voulions conserver autant que possible une approche en temps réel. Cependant, avec l'exécution d'une requête SQL, nous aurions été obligés de travailler par lots. De plus, une requête SQL régulière sur un grand volume de données aurait pu engendrer des coûts importants.

Solution

Pour résoudre ce problème, nous avons dû utiliser de nouveaux services GCP, que vous pouvez voir sur notre diagramme d'architecture.

Nous mettons en place une instance Redis appelée Memorystore sur Google Cloud. Ce service est utilisé comme un cache : dès qu'un message est traité avec succès, nous écrivons l'identifiant du message comme clé dans Memorystore. Ensuite, dès que de nouveaux messages arrivent, nous interrogeons l'instance Memorystore pour voir si l'identifiant du message est déjà présent dans la base de données. Si c'est le cas, nous ne traitons pas le message, car cela signifie qu'il a déjà été traité par Cloud Run. Si l'ID du message n'est pas présent dans Memorystore, nous traitons le message et écrivons l'ID dans l'instance, ce qui signifie que le message vient d'être traité.

Lorsque vous écrivez une clé dans Memorystore, vous pouvez également lui attribuer une date d'expiration. Dans notre cas, nous avons fixé cette valeur à 15 minutes, car il n'était pas nécessaire de conserver l'identifiant d'un message plus longtemps.

Nous avons également utilisé un VPC pour garantir une connexion sécurisée entre le Cloud Run et l'instance Memorystore. Toujours dans le but de renforcer la sécurité, nous avons activé l'exigence d'authentification de l'instance Memorystore, ce qui signifie que nous avons besoin d'une clé de sécurité pour pouvoir communiquer avec elle. Pour stocker cette clé en toute sécurité, nous l'avons placée dans le Secret Manager de Google Cloud, que nous appelons directement depuis le code de Cloud Run.

Pourquoi avons-nous utilisé Redis plutôt qu'une autre base de données ? Tout d'abord, nous voulions une base de données "clé:valeur", qui nous permet de récupérer une clé très rapidement. Ensuite, sachant que nous n'avions besoin de ces clés que pendant un certain temps, nous voulions une base de données qui nous permette d'entrer une date d'expiration pour les clés. C'est pourquoi nous avons choisi le service Memorystore de Google Cloud. Attention : l'utilisation de Memorystore a été utile dans notre cas car nous avions un grand volume de données, mais il est important de préciser que son utilisation doit être adaptée au cas d'usage, car dans sa configuration minimale, le service coûte 70$ par mois.

Résultats

Après plusieurs semaines de tests, notre architecture a réussi à traiter une moyenne de 1 500 requêtes par seconde, avec quelques pics à 3 000. Nous avons pu observer que notre système de vérification des doublons avec Memorystore n'augmentait pas du tout la latence des requêtes traitées par Cloud Run. Nous avons également constaté que notre système détectait entre 5 000 et 15 000 messages en double par jour en moyenne, avec des pics à 300 000 par jour, sur un total d'environ 70 millions de messages par jour. De plus, tous les messages ne sont plus envoyés qu'une seule fois à la base de données.

Améliorations possibles

En ce qui concerne notre architecture, plusieurs points peuvent être modifiés ou améliorés. 

Si votre première étape de collecte nécessite d'ajuster un grand nombre de paramètres (nombre de requêtes, nombre de CPU et de mémoire par instance, etc.) et que vous souhaitez utiliser Docker comme outil de déploiement, alors le remplacement de notre Cloud Function par un Cloud Run pourrait être plus intéressant pour votre cas d'usage.

Si vous souhaitez que votre point de collecte récupère les demandes d'utilisateurs externes géographiquement dispersés, envisagez de mettre en place un équilibreur de charge entre les utilisateurs et votre point de collecte (Cloud Run ou Cloud Function). De plus, avec un Load Balancer, vous pourrez facilement intégrer Cloud Armor (WAF de Google Cloud), ainsi que gérer vos noms de sous-domaines.

Enfin, si votre traitement de données est léger, voire inexistant, et que vous ne souhaitez pas utiliser Docker pour simplifier votre déploiement, vous pouvez remplacer notre Cloud Run par une Cloud Function.

Tous les articles

Articles similaires

Aucun élément trouvé.

Soif d'expertise ? Abonnez-vous à notre newsletter.

Découvrez les dernières actualités, articles, replays de webinars et événements fifty-five dans notre newsletter mensuelle Tea O’Clock.

Prénom*
Nom de famille*
Entreprise*
Langue préférée*
Email*
Merci !

Votre demande d'abonnement a bien été prise en compte.
Oups ! Un problème est survenu lors de la soumission du formulaire.