Ce qui se passe
Hyperdrive, le pooleur de connexions de Cloudflare pour les Workers, était réservé à Postgres. Sa prise en charge de MySQL, en bêta depuis l'an dernier, est désormais en disponibilité générale. Cloudflare résume l'écart entre la bêta et la GA par « une vingtaine de corrections de bugs », et en raconte trois, dont un qui explique pourquoi un cache de requêtes ne se transpose pas d'un protocole à l'autre.
Deux pièges MySQL, transposables
utf8 n'est pas UTF-8. C'est un alias obsolète d'utf8mb3, limité à 1 à
3 octets par caractère, alors qu'UTF-8 complet en prend jusqu'à 4. Les emojis
écrits en base revenaient corrompus tant que la poignée de main de connexion
négociait utf8. La correction : négocier utf8mb4.
COM_RESET_CONNECTION ne revient pas au jeu de caractères négocié. La
commande efface l'état de session d'une connexion, ce dont un pooleur a besoin
pour qu'aucun état ne fuite d'un emprunt à l'autre. Mais elle remet la connexion
au jeu de caractères par défaut du serveur : sur un serveur dont le défaut
est latin1, une connexion négociée en utf8mb4 repasse en latin1 après le
reset. Il faut donc toujours redéclarer l'encodage juste après.
Le bug à 0,001 %
Le plus gros client MySQL de la bêta signalait un taux faible mais constant de réponses vides là où une réponse non vide était toujours attendue : environ 0,001 % des requêtes, ce qui rendait les traces de bout en bout presque introuvables. Un seul indice : le bug n'apparaissait que sur les instances avec le cache activé.
Hyperdrive a deux nœuds, Client (sur la machine du Worker, il gère le cache local) et Endpoint (au plus près de la base, il tient le pool de connexions). Chacun utilise le cache CDN du centre de données où il tourne : dans le même centre, ils partagent le cache, qui stocke les octets de la réponse, indexés par un hachage normalisé des messages de la requête.
Le cœur du problème : le protocole MySQL négocie des capacités à la connexion.
Le pilote Node.js mysql2 ne gère pas CLIENT_DEPRECATE_EOF, qui change la
façon de terminer un jeu de résultats (avec la capacité, le paquet EOF après les
définitions de colonnes disparaît, et l'EOF final devient un paquet OK). Hyperdrive
la gère, donc le Client réécrit les réponses pour le pilote du Worker.
Or le code de cache du Client avait été écrit pour Postgres, avec une hypothèse : on peut mettre en cache exactement les octets renvoyés au Worker. Avec MySQL, c'est faux, puisque ces octets sont dans le format des capacités du pilote, alors que l'Endpoint attend celui des capacités d'Hyperdrive.
Le cache partagé contenait donc des octets dans deux formats, sans que rien ne dise lequel était lequel. Les deux scénarios fautifs, quand Client et Endpoint partagent un cache :
- L'Endpoint écrit, le Client lit. Le Client renvoie au Worker les octets au format Hyperdrive sans les traduire.
- Le Client écrit, l'Endpoint lit. L'Endpoint transmet à un autre Client des octets déjà traduits ; celui-ci tente de les traduire à nouveau, et envoie un EOF prématuré ou se bloque.
Le correctif : écrire toujours une version canonique, au format des capacités d'Hyperdrive, quel que soit le nœud qui stocke. Les réponses vides ont disparu et le taux de succès des connexions client atteint 99,99 %. Ce chiffre est celui de Cloudflare, mesuré sur sa plateforme.
Comment s'y prendre
npx wrangler hyperdrive create HYPERDRIVE_NAME --connection-string="mysql://user:password@db-host.example.com:3306/dbname"Création possible depuis le tableau de bord ou wrangler, vers une base
compatible MySQL existante ou une nouvelle base PlanetScale. Quel que soit votre
pooleur, deux vérifications valent le détour : l'encodage de la connexion est
bien utf8mb4, et il est redéclaré après toute réinitialisation de session.
Source : Inside Hyperdrive: How we solved a rare Hyperdrive MySQL caching bug, blog Cloudflare, 14 octobre 2026.