veilletech.fr
15 oct. Feed du jour
#08 CLOUDFLARE Article

Hyperdrive MySQL : GA, un bug de cache à 0,001 %

Un bug à un sur cent mille est un bug qui attend la production.

Hyperdrive, le pooleur de connexions de Cloudflare pour Workers, passe en disponibilité générale pour MySQL. Le billet détaille trois pièges de protocole : utf8 n'est pas du vrai UTF-8, COM_RESET_CONNECTION remet le jeu de caractères du serveur, et un cache partagé entre deux nœuds servait des octets au mauvais format, ce qui produisait environ 0,001 % de réponses vides.

4 min de lectureavancévidéo 1:15
Partager
Sommaire5 sections
  1. Ce qui se passe
  2. Deux pièges MySQL, transposables
  3. Le bug à 0,001 %
  4. Comment s'y prendre
  5. À retenir

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 :

  1. L'Endpoint écrit, le Client lit. Le Client renvoie au Worker les octets au format Hyperdrive sans les traduire.
  2. 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

Terminal
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.