Nos instances Cloud SQL constituent un élément central de l’architecture de construction de l’index Glean. Entre autres, elles servent de dépôt pour l’ensemble de nos données crawlées, qui finissent par alimenter l’index servi. Elles stockent également d’autres informations, comme les réponses créées par les utilisateurs de Glean. Lorsque nous avons lancé Glean, nous enregistrions tout cela dans Google Big Table, puis nous avons migré vers Cloud SQL pour des raisons de coûts. Historiquement, toutes nos instances Cloud SQL étaient des instances MySQL5. Nous avons récemment décidé de migrer ces instances vers MySQL8, après que GCP a commencé à prendre en charge MySQL8.  ;
Motivation
Au-delà de l’utilisation de la version la plus récente, la principale motivation de cette migration était les améliorations JSON introduites avec MySQL8. Avant MySQL8, certains upgrades mettaient près de 48 heures à se terminer, car certaines commandes Alter Table sur des colonnes JSON prenaient énormément de temps. Nous pensions que MySQL8 nous permettrait d’accélérer significativement ces opérations, notamment en rendant possible la mise à jour du JSON « in place ».  ;
Principaux défis
- Comme il s’agissait d’une montée de version majeure, nous devions créer une toute nouvelle instance MySQL8, puis y copier les données de l’ancienne instance. Le volume de données à copier était très important dans certains de nos déploiements, atteignant plusieurs téraoctets.
- Pendant la copie des données, la base source devait rester disponible pour les opérations courantes.
- Selon la façon dont nos services se connectaient à la base, au moment du basculement de l’ancienne DB vers la nouvelle DB, certains services devaient être redémarrés, tandis que d’autres devaient être redéployés. Nous avons également mis en place, pour certains services, la détection des changements dans les paramètres de connexion GCP afin de rafraîchir leurs pools de connexions SQL en mémoire, supprimant ainsi le besoin de redémarrage ou de redéploiement.  ;
- Comme le basculement pouvait impliquer le redéploiement de certains services, il devait se faire sous la supervision de quelqu’un. Cela signifiait aussi que cela ne pouvait avoir lieu que lorsque la charge client sur le système était faible.  ;
Utilisation de GCP DMS
Compte tenu des contraintes ci-dessus, nous avons décidé d’utiliser GCP DMS pour migrer nos bases de données de MySQL5 vers MySQL8. DMS est un service recommandé par Google pour déplacer des charges SQL depuis une autre infrastructure vers des instances Cloud SQL managées par Google. Nous avons choisi le type de jobs de migration continuous, qui utilisent une phase de dump initial, puis une réplication primaire-secondaire pendant la phase CDC. Nous avons surveillé le retard de réplication et, lorsqu’il était faible, nous étions prêts à basculer de l’ancienne instance vers la nouvelle. Même si cette condition pouvait être remplie à n’importe quel moment de la journée, nous n’effectuions le basculement que la nuit et le week-end afin de minimiser l’impact sur le service client.  ;
Framework d’opérations asynchrones
Historiquement, nos upgrades étaient exécutés par le push_master qui surveille en continu les upgrades. Ils durent le plus souvent environ 2 heures. Mais compte tenu de la taille importante des données (point 1 ci-dessus), l’upgrade SQL devait durer plusieurs jours. Par ailleurs, nous devions upgrader près de 75 projets, avec 2 instances Cloud SQL chacun, ce qui rendait une mise à niveau manuelle fastidieuse. Nous avons décidé d’implémenter ce que nous appelons l’AsynchronousUpgradeframework pour résoudre ce problème.  ;
À très haut niveau, il s’agit d’une machine à états. L’état actuel de l’upgrade est stocké dans un bucket GCS, dans un fichier appelé « state file ». Un tick périodique se déclenche, charge le state file et envoie des requêtes GCP pour vérifier l’état actuel de la migration. Ensuite, en fonction de ces deux entrées, la machine à états peut mettre à jour l’état et exécuter une action. Par exemple, si l’état actuel des jobs de migration est « running », nous examinons le retard de réplication et, s’il est assez élevé, nous restons dans le même état. En revanche, si le retard de réplication est faible et que l’utilisation du système est basse (point 4 ci-dessus), nous décidons de changer d’état en appliquant un patch à la nouvelle instance, en basculant vers celle-ci, puis en déclenchant les redémarrages et redéploiements.
Toutes les opérations effectuées par ce framework doivent être idempotentes en cas de problèmes intermittents. Elles doivent donc laisser le système dans un état cohérent pour le tick suivant.  ;
Gains avec MySQL8 vs MySQL5
Même si une comparaison détaillée des performances de MySQL8 vs MySQL5 pour les workloads Glean pourrait faire l’objet d’un article à part, quelques premiers indicateurs incluent :
- Des gains significatifs de CPU pour un même workload. Par exemple, dans un cas, le CPU était autour de 30 %, alors qu’il oscillait auparavant autour de 50 %.
- Moins d’I/O de lecture disque.
- L’utilisation de la mémoire semble similaire. Nous pensons que MySQL essaie de maintenir la mémoire autour de 85 % de la mémoire disponible.  ;
- Nous avons également constaté une bien meilleure mise en cache de requêtes similaires.  ;
Quelques enseignements
Tout au long du projet, nous avons appris beaucoup de choses.  ;
- Il est difficile de réduire rapidement le retard de réplication s’il y a une forte charge sur la base source. Nous avions initialement décidé de laisser la base source tourner à plein régime pendant que la réplique rattrapait son retard. Nous pensions qu’en ajustant les paramètres de réplication parallèle et la sémantique des commits de transactions, nous parviendrions à réduire le lag. Mais nous avons vite constaté que cela ne fonctionnait pas dans notre cas. Nous avons dû mettre en pause l’activité sur la base source.  ;
- Temps de dump initial important : si les données sont concentrées dans quelques tables de la base source, le dump initial peut être très long. Nous avons eu un cas où environ 90 % des données se trouvaient dans une seule table, dans un projet spécifique. Lors de notre première tentative, le dump initial a pris plus de 7 jours. Comme la rétention maximale des logs de réplication sur la source pouvait être de 7 jours, le job n’obtenait jamais les mises à jour correctes. Heureusement, nous avons pu ignorer une partie de ces données via dumpFlags, puis les recrawler une fois le basculement vers la nouvelle instance effectué.  ;
- Le passage à l’échelle avec le modèle actuel, où nous avons un projet GCP distinct par client, peut s’avérer complexe pour de futures migrations. Nous devrions évoluer vers une architecture multi-tenant, avec des projets uniques pour davantage de clients.  ; ;  ;
Si construire ou utiliser un produit de recherche best-in-class vous intéresse, nous serions ravis d’échanger avec vous !









