Comment une incompatibilité de capacités a cassé le cache MySQL dans Cloudflare Hyperdrive
Cloudflare a corrigé un bug rare dans la prise en charge MySQL d'Hyperdrive, où les caches CDN partagés renvoyaient des résultats vides en raison d'incompatibilités de capacités protocolaires entre les nœuds.
Traduit automatiquement depuis l'original anglais.
Cloudflare a annoncé la disponibilité générale de la prise en charge MySQL dans Hyperdrive, son pooler de connexions de base de données pour Workers. Cette version fait suite à une période bêta au cours de laquelle les ingénieurs ont identifié et résolu un subtil bug de mise en cache qui provoquait occasionnellement des réponses de requêtes vides. Ce correctif garantit que le service atteint son objectif de disponibilité de 99,99 % pour les charges de travail MySQL.
Que s'est-il passé ?
Hyperdrive a été lancé initialement avec la prise en charge Postgres, permettant aux Workers sans serveur de partager des connexions de base de données chaudes et de mettre en cache les résultats des requêtes à l'échelle mondiale. Lorsque la prise en charge MySQL est entrée en phase bêta l'année dernière, l'équipe visait à égaler la fiabilité de leur implémentation Postgres. Durant cette phase de test, des clients à fort volume ont signalé un taux d'erreur constant mais rare, où les requêtes renvoyaient des résultats vides au lieu des données attendues. Ce problème se produisait sur environ 0,001 % des demandes, ce qui rendait sa reproduction et son traçage difficiles.
L'enquête a révélé que le problème était spécifique aux instances Hyperdrive avec la mise en cache activée. Les ingénieurs ont tracé le problème jusqu'à la manière dont Hyperdrive gère le protocole client/serveur MySQL, plus précisément en ce qui concerne la négociation des capacités. Contrairement à Postgres, MySQL utilise des indicateurs de capacité (capability flags) pour déterminer comment les jeux de résultats sont formatés et terminés. Une divergence entre ce que le pilote du Worker attendait et ce qu'Hyperdrive mettait en cache a entraîné des réponses corrompues ou illisibles lorsque les nœuds partageaient un cache.
Pour atteindre la disponibilité générale, l'équipe a mis en œuvre vingt corrections de bugs, incluant des ajustements pour l'encodage des jeux de caractères et les comportements de réinitialisation des connexions. Le correctif le plus critique a porté sur la logique de mise en cache afin de garantir que tous les résultats de requêtes stockés utilisent un format canonique. Avec ces changements déployés, les erreurs de réponse vide ont disparu et le service a atteint ses objectifs de fiabilité.
Comment cela fonctionne
Hyperdrive utilise une architecture scindée composée d'un nœud Client et d'un nœud Endpoint. Le Client s'exécute près du Worker, gérant les demandes locales et la mise en cache, tandis que l'Endpoint se situe plus près de la base de données d'origine, gérant le pool de connexions. Les deux nœuds utilisent le cache local du CDN Cloudflare. Lorsque le Client et l'Endpoint résident dans le même centre de données, ils partagent ce cache pour améliorer les performances en servant les requêtes répétées sans solliciter la base de données.
Le protocole MySQL repose sur la négociation des capacités lors de la phase de connexion. Une capacité clé est CLIENT_DEPRECATE_EOF, qui modifie la façon dont les jeux de résultats sont terminés. Les serveurs modernes omettent un paquet EOF après les définitions de colonnes et remplacent le dernier EOF par un paquet OK si cette capacité est négociée. Cependant, certaines bibliothèques clientes, comme mysql2 pour Node.js, ne prennent pas en charge cet indicateur. Hyperdrive doit donc traduire les réponses entre le format du serveur d'origine et le format attendu par le Worker.
Le bug est apparu parce que la couche de mise en cache supposait qu'elle pouvait stocker les octets exacts envoyés au Worker. Si l'Endpoint mettait en cache une réponse utilisant les capacités internes d'Hyperdrive, mais que le Client la récupérait et envoyait ces octets bruts à un Worker attendant un format différent, le Worker interprétait mal les données. Inversement, si le Client mettait en cache des octets traduits, l'Endpoint pourrait les récupérer et échouer à les traiter correctement. Le correctif garantit que seule une version canonique de la réponse, encodée avec les capacités d'Hyperdrive, est jamais écrite dans le cache partagé.
Détails clés
- Hyperdrive pour MySQL est désormais disponible en version générale, ayant quitté la phase bêta.
- Le service vise une disponibilité de 99,99 % pour les connexions clientes depuis les Workers.
- Un bug rare faisait que 0,001 % des requêtes mises en cache renvoyaient des résultats vides ou restaient bloquées.
- Le problème provenait d'une gestion divergente de la capacité
CLIENT_DEPRECATE_EOF. - La correction du bug nécessitait de garantir un encodage canonique des octets dans le cache CDN partagé.
- Des correctifs supplémentaires ont traité l'encodage
utf8mb4et les réinitialisations d'étatCOM_RESET_CONNECTION.
Pourquoi c'est important
Pour les développeurs construisant sur des plateformes sans serveur, la gestion des connexions de base de données est un défi persistant. Établir de nouvelles connexions pour chaque demande ajoute une latence significative et consomme des ressources. Les poolers de connexions comme Hyperdrive atténuent ce problème en maintenant les connexions chaudes, mais introduisent une complexité dans la traduction des protocoles. Comprendre comment ces outils gèrent les protocoles réseau aide les ingénieurs à déboguer des problèmes obscurs qui surviennent lorsqu'on mélange différentes bibliothèques clientes et versions de bases de données.
Cet incident met en lumière les risques liés à l'hypothèse d'une uniformité protocolaire entre différents systèmes de bases de données. Bien que Postgres et MySQL utilisent tous deux SQL, leurs protocoles réseau sous-jacents diffèrent considérablement. Les ingénieurs intégrant des infrastructures multi-protocoles doivent être conscients que les couches de mise en cache peuvent devoir normaliser les formats de données pour prévenir la désynchronisation. Ignorer ces nuances peut entraîner des pannes intermittentes difficiles à reproduire dans les environnements de développement locaux.
De plus, la dépendance aux caches partagés dans les systèmes distribués exige des garanties strictes de cohérence. Lorsque plusieurs composants lisent et écrivent dans le même cache, ils doivent s'accorder sur le schéma de données. Dans ce cas, l'absence d'un format canonique signifiait que le cache devenait une source de corruption plutôt qu'un gain de performance. Cette leçon s'applique largement à tout système utilisant une mise en cache intermédiaire pour la traduction de protocoles.
Ce que vous pouvez faire
- Passez à la version disponible en général d'Hyperdrive pour MySQL afin de bénéficier des correctifs de stabilité.
- Vérifiez que votre bibliothèque cliente MySQL prend en charge
utf8mb4pour éviter les problèmes d'encodage de caractères avec les emojis ou les caractères spéciaux. - Vérifiez si votre pilote client prend en charge
CLIENT_DEPRECATE_EOFpour comprendre comment les jeux de résultats sont terminés dans votre application. - Surveillez votre application pour détecter des résultats de requêtes vides intermittents si vous utilisez un pilote MySQL personnalisé ou ancien.
- Utilisez l'interface CLI
wranglerpour créer de nouvelles instances Hyperdrive pour les bases de données compatibles MySQL comme PlanetScale. - Examinez votre logique de réinitialisation de connexion pour vous assurer que l'état de session ne fuite pas entre les connexions.



