Chez Glean, nous traitons quotidiennement des centaines de millions de documents. Dans un précédent article de blog, nous avons expliqué comment nous utilisons Apache Beam Streaming pour lire efficacement un corpus aussi volumineux depuis MySQL. Alors que nous nous y concentrions sur les détails du pipeline de streaming, cet article va entrer dans le détail de nos requêtes MySQL et du monitoring en place qui permet d’identifier, de diagnostiquer et de corriger les inefficacités du système. Nous passerons également en revue un exemple concret : comment nous avons débogué un pipeline au faible débit à cause de lectures lentes depuis MySQL.
Des axes d’amélioration
Sur l’un de nos plus gros déploiements, nous avons constaté qu’il nous fallait environ 3 jours pour traiter l’intégralité du corpus. Si la configuration de streaming décrite plus tôt offre une scalabilité horizontale massive, nous avons remarqué que l’utilisation CPU était loin de 100% sur ce déploiement. C’était le premier indice : il existait une inefficacité dans le système, optimisable sans augmenter les ressources de calcul.
Sur des déploiements multicœurs comme celui-ci, nous lançons plusieurs requêtes MySQL de lecture en parallèle, chacune récupérant un lot de k documents, afin d’augmenter le débit. Tous ces documents sont conservés dans un buffer en mémoire, depuis lequel ils sont récupérés pour être traités. Lorsque le nombre de documents dans ce buffer en mémoire est faible, un nouvel ensemble de requêtes MySQL parallèles est lancé pour récupérer davantage de documents.  ;
Sur ce déploiement en particulier, le buffer en mémoire était signalé comme vide assez souvent. Cela signifiait que notre pipeline traitait les documents plus rapidement qu’ils n’étaient récupérés. C’était le deuxième indice, et il nous a permis de circonscrire l’inefficacité au temps nécessaire aux lectures MySQL.
Supposons que nous ayons n cœurs dans un déploiement la requête MySQL pour récupérer un lot de k documents ressemblait à ceci :

Nous avons commencé à surveiller les latences de lecture pour cette requête et avons constaté que plus l’offset (n*k) était élevé, plus la requête était lente. C’était le troisième et dernier indice. Si le même schéma de requêtes MySQL était utilisé sur tous nos déploiements, ce qui était propre à celui-ci, c’était la valeur beaucoup plus élevée de n. En nous penchant sur la manière dont MySQL gérait cette requête, nous avons réalisé qu’avec des offsets plus grands, il récupérait au total (n+1)*k documents avant de supprimer les n*k premiers, ce qui expliquait l’augmentation du temps de requête. Pour forcer MySQL à effectuer des late row lookups et ne récupérer ainsi que les k documents requis, la requête MySQL a été modifiée comme suit :

Bug corrigé, débit triplé
Cela a réduit les latences MySQL sur une requête de test avec k = 1, n*k = 1150 d’environ 4 secondes à environ 0,1 seconde, et a permis de tripler le débit du pipeline (voir Figure 1). Cette hausse de débit se traduit par une réduction de l’obsolescence des contenus, des permissions et des statistiques, ce qui améliore notre classement afin d’offrir une meilleure expérience de recherche.

Nous espérons que cet article de blog donne un aperçu non seulement des types de défis techniques auxquels nous faisons face, mais aussi du monitoring en place pour les détecter et du processus suivi pour les déboguer. Si vous souhaitez travailler sur des problématiques similaires, consultez notre page carrières !









