Maison  >  Article  >  développement back-end  >  Comment j'ai traité les entrées par demande - Avec Go !

Comment j'ai traité les entrées par demande - Avec Go !

Barbara Streisand
Barbara Streisandoriginal
2024-11-05 04:45:02634parcourir

How I processed over Entries per request - With Go!

Avant de commencer, un peu de contexte sur qui je suis et pourquoi c'est important dans ce cas. Je suis développeur de logiciels chez Notebook Manufacturing Company et je travaille ici depuis deux ans. Dans l'équipe dans laquelle je travaille actuellement, je suis le seul développeur, responsable de la création, de la surveillance et de la maintenance des pipelines de données, des automatisations et d'un système de surveillance avec Go et Grafana.
Autre point que je peux ajouter, l’année précédente, j’étais stagiaire. Et je suis autodidacte.

D'accord, mais pourquoi est-ce important ?

Eh bien, je n'avais pas de développeur senior, ni aucune forme de conseil interne qui pourrait me guider lorsque je faisais face à des obstacles ou à des problèmes que je ne parvenais pas à résoudre par moi-même, et c'est la principale raison pour laquelle j'écris. Je pense que c'était une chose amusante à faire et que je voulais partager. Ce ne sera pas un logiciel étonnant, une avancée ou quoi que ce soit du genre, mais un rappel qu'il est possible de construire des choses à partir de zéro. Vous n'avez pas besoin d'être un développeur 10x sur un million ou quelque chose comme ça.

P.S : j'utilise neovim d'ailleurs.

Un million de lignes

Ce chiffre peut ressembler à une exagération ou quelque chose comme ça, et même si j'aurais aimé que ce soit le cas, ce n'est pas le cas. Vous voyez, lorsque nous travaillons dans le contexte d'une usine, parfois nous ne prenons pas en compte le nombre d'articles nécessaires et la quantité de données générées lorsque vous devez suivre chaque point des nombreuses files d'attente de fabrication, pour chaque composant.
Que ce soit une petite vis, qu'il s'agisse d'un CPU de dernière génération, il doit être suivi, avec des horodatages !
Alors oui, la quantité de données générées est correcte, et ce n’est qu’un début. Voici les autres points intéressants qui rendent le problème cool (à mon avis) :

  • La source de ces données n'était pas compressée ou quoi que ce soit du genre, donc le temps moyen de requête était proche de 2 à 3 minutes dans une bonne journée. Cela pourrait durer plus de 5 minutes si la source était en retard (c'est un système Java plus ancien que moi.).
  • Concernant le format, je pouvais choisir entre traiter une réponse XML ou une réponse CSV, bien sûr j'ai pris la voie CSV. Je ne suis pas masochiste.
  • Et il fallait choisir l'intervalle de récupération des données, avec un minimum d'une heure (Non, je ne sais pas pourquoi, oui, j'ai essayé des intervalles mineurs).
  • La source n'avait pas de chargement paresseux ni de système de cache, donc deux requêtes égales généreraient la même réponse, avec des horaires différents.
  • Et enfin, pour pimenter les choses, c'est une affaire de Microsoft, connue sous le nom de SQL Server Reporting Services, avec ses propres réserves obscures. Oh, et la base de données que je dois utiliser est MSSQL, ce qui est pénible.

C'est tout le contexte, et maintenant la partie amusante commence.

La première itération - DMP

La façon dont j'aime aborder les logiciels est assez simple. Rendez-le moche et à peine fonctionnel -> Gardez-le moche et améliorez les fonctionnalités -> Toujours moche, mais mieux optimisé -> Joli, optimisé et fonctionnel -> Et puis votre responsable dit que le travail que vous avez fait est bien trop bon et que la source ne peut pas le gérer, provoquant des pannes. FML.

Quoi qu'il en soit, DMP signifie Dumb Mode Protocol, faites-le simplement fonctionner de la manière la plus stupide possible, ce qui signifie le faire pour qu'il fonctionne à peine.

Donc, pour le premier tour, mon objectif était simple, m'authentifier, faire la demande, analyser les données, envoyer à la base de données. Assez simple, non ? Et, sur papier, c'était le cas, jusqu'à ce que je découvre que la méthode d'authentification et d'autorisation que je devais utiliser était ntlmssp, qui est une méthode d'authentification défi-réponse dont je ne connaissais pas l'existence. En fait, j'ai dû accéder au code .NET 4 existant pour le trouver. Je n'ai jamais fait de C#.
Après avoir parcouru le code hérité plus ancien que moi, j'ai essayé de le comprendre parce que, pour une raison quelconque, celui qui l'a écrit pensait que c'était une bonne idée de le cacher dans 5 couches d'abstraction, de constructeurs et d'éléments POO. Pas une expérience amusante et humiliante. Et puis je l’ai fait fonctionner. Une heure plus tard, mon utilisateur a été bloqué car, apparemment, la source a une limitation de débit. MAIS PAS DE CACHE OU DE CHARGEMENT PARESSEUX.

Eh bien, après tout ça, il me suffit de passer les paramètres de requête et d'obtenir les données, plus de complications avec la source. Ok, regardons la documentation pour les paramètres de requête.

...

À ce stade, tout bien considéré, vous l’avez probablement deviné. Documentation? Est-ce une sorte de nourriture exotique qui n'est servie que dans la Silicon Valley ?
Quoi qu'il en soit, après m'être creusé la tête, j'ai décidé d'inspecter le site/interface de ce SQL Server Reporting Services, et, à ma joie, puis à ma haine, il y avait un moyen de connaître les filtres et leurs valeurs.

Seulement pour savoir que le filtre ("Tous") dans la page, pour sélectionner, disons, toutes les lignes de fabrication, n'est qu'une abstraction consistant à cocher toutes les cases. Ok, copions les filtres et mettons-les dans la chaîne de requête, encodons-les et soyons heureux !

CELA A MARCHE ! C'est du moins ce que je pensais.

Vous vous souvenez quand j'ai dit que mon utilisateur avait été bloqué ? Eh bien, il semble que le fait d'avoir les autorisations d'administrateur pour effectuer de telles tâches, étant autorisé par C-Suite à le faire (en fait, cela leur a été demandé), n'était pas suffisant pour me permettre de faire plusieurs demandes. Mon utilisateur a été bloqué, mais pour mon manager, cela revenait à dire "Rien ne s'est passé". Heureusement, j'ai réussi à le débloquer assez rapidement.

En attendant, j'ai décidé de travailler sur la façon dont j'aborderais la suite de ce projet. À ce stade, j'en avais déjà un échantillon, et cela était à la hauteur du titre du message. Voir les sept chiffres m'a fait me demander si j'étais capable de le faire, car je n'avais aucune expérience avec ce volume de données.

Pour mettre mes idées en forme, j'ai espéré dans excalidraw et j'ai conçu ce que je voulais faire. Je connaissais le concept des pools de travailleurs, mais je ne l'avais jamais mis en œuvre auparavant. J'ai donc lu des articles à ce sujet, vu quelques implémentations et demandé à un de mes amis qui m'a beaucoup aidé. C'est le senior que je n'avais pas.

Après l'avoir écrit en pseudo code, je me suis dit :

"Wow, c'est plutôt sympa, ça ne générera sûrement pas de fuites de mémoire avec tous ces canaux et goroutines, n'est-ce pas ?"

Ok, ce n'étaient peut-être pas ces mots exacts, mais c'était dans ce sens. Après en avoir fait la première itération, réussi à faire la requête (sans être bloqué) et à l'avoir chargée en mémoire, j'ai appliqué le concept de pool de travailleurs.

Et puis j'ai fait face au BSOD. Curieusement, c'était le jour même de la grève CrowdStrike, je ne pensais sûrement pas avoir causé de pannes majeures à la manufacture.
Et oui, je dois utiliser Windows pour travailler, mais ne vous inquiétez pas ! J'utilise WSL2.

Après avoir parcouru l'énorme piste de pile de mon premier débordement de pile, j'ai découvert l'erreur. Lors de l'envoi des données au canal consommateur, je n'ai pas pris en compte le fait que certaines données pouvaient contenir des erreurs, principalement en raison d'une violation de la clé primaire ou simplement parce que, peut-être, je ne gérais pas correctement les erreurs.

Leçon apprise, utilisez un canal d'erreur pour éviter des problèmes majeurs. Et gérez vos erreurs, que ce soit via une simple vérification de chaîne (laid, mais fonctionne), ou simplement en l'enregistrant au niveau supérieur, et soyez heureux. Comme les erreurs auxquelles j'ai été confronté n'étaient pas critiques, j'ai pu continuer.

La deuxième itération. Fuites de mémoire.

La quantité de fuites de mémoire que j'ai générées au cours de cette étape m'a fait penser que je faisais du C. Mais il s'agissait simplement de problèmes de compétences majeurs.
Quoi qu'il en soit, vous pensez peut-être :

"Comment avez-vous créé des fuites de mémoire dans un processus aussi simple ?"

C'est simple, j'apprenais et j'essayais.

Le principal problème au cours de cette étape était que, naïvement, je ne m'assurais pas que les données étaient correctement insérées, et que le nombre de violations des clés primaires que j'obtenais violait ma mémoire. C'était un problème que je savais comment résoudre, mettons les données en cache !
Attendez, comment puis-je créer un identifiant unique pour chaque ligne, étant donné que chacune d'entre elles est unique ?

Maintenant, c'est la partie qui va se moquer de moi, parce que, pour être honnête, c'est la partie qui me fait le plus craquer. J'ai simplement rejoint la ligne d'informations actuelle et je l'ai analysée dans une fonction de hachage, et ce hachage est devenu mes clés dans la carte.

"Pourquoi pas Redis ?" - Vous vous demandez peut-être.

C'est simple, la bureaucratie. Ce n'est pas une petite entreprise. En fait, beaucoup d’entre vous utilisent probablement un ordinateur portable qu’ils ont fabriqué. Le simple fait de demander une instance Redis prendrait au moins trois jours de travail, quatre réunions, un sacrifice pour le dieu de la bureaucratie et un document complet expliquant pourquoi et comment.
Alors, ouais, allons avec une simple carte de hachage et pré-initialisons-la avant la première exécution. Cela augmentera le temps de chargement global mais ce sera plus rapide que la demande.

En faisant cela, le processus global s'est amélioré comme s'il avait un nouveau moteur, les fuites de mémoire se sont arrêtées, les lots n'échouaient pas à chaque fois, le nombre d'erreurs et de déconnexions a également diminué, plutôt sympa, non ? N'est-ce pas ?

À présent, vous savez que quelque chose s'est confus.

La troisième itération. La vie pourrait être belle.

Un point que je n'ai pas pris en compte était le fait qu'en faisant des insertions par lots, il y avait la validation des données. Voici une représentation simple du flux.

Récupérer les données -> Vérifiez si le hachage des données existe dans le hashmap -> Lot et insertion

Et quel est le problème avec ça ? Eh bien, que se passe-t-il si une seule insertion dans mon lot échoue, réessaiera-t-il sans l'entrée ? Si oui, combien de tentatives puis-je effectuer sans encombrer le système et perdre les avantages de la mise en œuvre du pool de travailleurs ?
Une seule façon de le savoir ! Vérifions-le.
Un point que je peux ajouter est le fait que cette source renvoyait plus de 25 colonnes, j'ai donc dû faire attention à la quantité de données que j'insérais par lot pour ne pas dépasser 2 100 paramètres, ce qui est la limite MSSQL.

À ce stade, j'exécutais déjà les choses dans un conteneur Docker qui imite l'espace de production avec des ressources limitées. Pour ajouter du contexte, ce processus fonctionnait avec 1 Go de RAM et environ 0,5 CPU. J'aurais pu allouer plus de ressources, mais ce serait simplement me forcer brutalement à m'en sortir.
En exécutant cette nouvelle itération à l'intérieur du conteneur, en ajoutant des horodatages et en la déconnectant dans un fichier pour une analyse ultérieure. J'ai découvert une augmentation d'environ 5 minutes en raison du nombre de tentatives. Cela ne fonctionnerait pas, supprimer l'entrée « sale » n'était pas une option.

La quatrième itération. La vie EST belle.

Pour résoudre ce problème, j'ai augmenté le nombre de travailleurs. J'utilisais environ 50 travailleurs, et grâce à une supposition aléatoire d'un utilisateur Discord dans l'étagère supérieure de ThePrimagen, je l'ai augmenté à 1000 travailleurs et j'ai fait en sorte que chaque travailleur valide chaque entrée dans une transaction. En cas d'échec de la transaction, je l'ai simplement annulée.
En faisant cela, j'ai pu résoudre le problème principal et améliorer la vitesse globale de ce processus en général. Il était maintenant temps de le mettre en production et de le surveiller, car, vous savez, les gobelins de la production peuvent brouiller vos logiciels. (Ils sont également connus sous le nom de problèmes de compétences, mais ce nom est interdit.)

Sachant qu'en diminuant l'intervalle de récupération de ce système, là où il était demandé de le faire en temps quasi réel (ce qui signifie assez rapide pour qu'ils ne remarquent pas de retards), j'ai créé un nouveau conteneur, cette fois avec un peu plus robuste pour vérifiez s'il serait capable de supporter la charge et réglez l'intervalle sur 3 minutes. Compte tenu du temps moyen de récupération, cela voudrait dire que je pourrais avoir des chevauchements, mais je voulais vraiment voir ce qui allait se passer.

Je l'ai laissé fonctionner toute la nuit, en enregistrant les résultats pour les vérifier plus tard et, à ma grande surprise, avant la fin de la journée de travail, j'ai reçu un appel de mon manager. Attention, ce n'est pas un spécialiste de la technologie ou quelque chose comme ça.

"Hé, avons-nous déployé quelque chose qui interagit avec le [Nom du système que je ne peux pas divulguer] ?"

"Oui, j'ai déployé le système de récupération de données en temps réel qui était demandé. Pourquoi ?"

"Pouvez-vous, hum, l'arrêter ? Cela a provoqué une panne et nous ne pouvons pas travailler ici."_

Ceci, Gadies et Lentleman, est un tout autre niveau de rupture. J’ai littéralement arrêté plus de 20 lignes de production pendant environ une minute. Quoi qu'il en soit, j'ai arrêté le système et tout est revenu à la normale.
Le lendemain, on m'a demandé d'augmenter l'intervalle entre les récupérations à 30 minutes au lieu de 3, ok, très bien je suppose. Je ne vais pas mentir, j'étais un peu triste de ne pas pouvoir le voir fonctionner à vitesse maximale, mais bon, au moins, je l'ai fait fonctionner.

Avec ce nouveau système, le temps moyen de mise à jour des rapports utilisant ces données a diminué à 8 à 10 secondes, ce qui a amené les gestionnaires à demander de la même manière beaucoup plus de rapports de la même source. Le bon travail est récompensé par plus de travail !

Considérations

C'était une expérience amusante, principalement parce que cela m'a vraiment fait réaliser à quel point le Go est puissant. En utilisant moins de mémoire que Google Chrome, moins de stockage qu'une application Microsoft et moins de puissance CPU que la calculatrice Windows, j'ai pu améliorer un ancien processus qui le forçait littéralement à se frayer un chemin (il vérifiait littéralement chaque ligne de la base de données avant de l'insérer. Je ne le fais pas Je ne sais pas comment la personne précédente a pensé que ce serait une bonne idée.). C'était vraiment amusant.

Quoi qu'il en soit, n'hésitez pas à partager vos réflexions sur tout ce processus, quelle approche adopteriez-vous et qu'auriez-vous fait différemment. Comme je n'ai pas de collègues développeurs, je veux connaître plus d'opinions à ce sujet.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn