Le problème des 100 téraoctets caché à la vue de tous
L'infrastructure DNS mondiale de Cloudflare, propulsée par la plateforme bien nommée Big Pineapple, fonctionne à une échelle étonnante. Elle gère en permanence plus de 250 milliards d'entrées de cache DNS, soutenant des services critiques comme 1.1.1.1, Gateway DNS et DNS Firewall pour d'innombrables utilisateurs dans le monde entier.
Malgré cette immense empreinte opérationnelle, les ingénieurs de Cloudflare ont découvert une inefficacité cachée et omniprésente. Une entrée de cache DNS typique, évaluée à 953 octets, contenait une proportion importante de memory overhead dédiée non pas aux données DNS elles-mêmes, mais à la gestion interne des structures de données. Les types Vec et String par défaut de Rust, par exemple, stockent un pointeur, une longueur et un champ de capacité.
Pour les enregistrements DNS mis en cache de manière statique, qui ne changent pas, ce champ de capacité devenait un fardeau inutile, consommant une mémoire précieuse. Cette inefficacité apparemment mineure s'est accumulée de manière spectaculaire sur l'ensemble du vaste réseau. Même un seul octet gaspillé par entrée sur 250 milliards d'enregistrements se traduit par plus de 250 gigaoctets de RAM superflue.
Ce gaspillage massif de mémoire équivalait au coût opérationnel de serveurs entiers, soulignant comment de petits détails négligés dans la conception des structures de données peuvent se transformer en dépenses d'infrastructure massives. La révélation de Cloudflare a souligné l'importance cruciale d'une gestion fine de la mémoire, même dans des langages de haut niveau comme Rust.
Supprimer la capacité : l'arme à double tranchant de Rust
Les structures de données de Rust privilégient souvent la flexibilité. Les types standard comme Vec et String gèrent efficacement les données dynamiques en allouant plus de mémoire que nécessaire immédiatement, réservant une capacity supplémentaire pour une croissance future. Chaque instance stocke implicitement trois informations : un pointeur vers ses données, sa longueur actuelle et cette capacité pré-allouée.
Cette stratégie est très efficace pour les données mutables, mais elle représente un pur gaspillage pour les enregistrements immuables. Les 250 milliards d'entrées de cache DNS de Cloudflare sont fixes une fois stockées ; elles ne s'étendent ni ne se contractent jamais. Le champ de capacité supplémentaire ne servait donc à rien au sein de "Big Pineapple", tout en consommant de précieuses ressources système.
Reconnaissant cette inefficacité, les ingénieurs ont remplacé Vec et String par des boxed slices (Box<[T]>) et des boxed strings (Box<str>). Ces types Rust spécialisés n'allouent que la mémoire exacte requise pour les données, éliminant totalement le champ de capacité. Ce changement apparemment minime a permis d'économiser 64 octets par entrée de cache individuelle.
Sur l'ensemble de la vaste flotte mondiale de Cloudflare, cette optimisation a instantanément récupéré plus de 15 To de RAM. Cela démontre comment une compréhension approfondie de la sémantique des structures de données, combinée aux exigences spécifiques de l'application, peut débloquer d'immenses gains d'efficacité, transformant la mémoire dormante en ressources actives et utilisables.
Au-delà des Box : l'art du compactage de données
Cloudflare est allé au-delà des pointeurs individuels pour les sections DNS comme answer, authority et additional, en adoptant une stratégie de données tightly packed. Ils ont consolidé ces sections dans un bloc de mémoire contigu unique, puis ont utilisé de minuscules décalages de deux octets pour naviguer précisément vers chaque partie. Cela a éliminé le surcoût des pointeurs, contribuant de manière significative à la réduction de 56 % de la taille des entrées de cache, passant de 953 octets à seulement 420 octets par entrée.
D'autres optimisations ont ciblé la redondance et les inefficacités structurelles au sein du cache. Cloudflare a cessé de stocker le nom du propriétaire s'il faisait doublon avec la requête, le reconstruisant à partir de la clé de cache uniquement lorsque nécessaire. En s'attaquant à la taille des enum Rust, qui utilisent par défaut la variante la plus grande, ils ont implémenté le boxing pour les types d'enregistrements volumineux et rares tels que NAPTR. Cela a permis aux enregistrements plus petits et plus courants, comme A et AAAA, d'occuper beaucoup moins de mémoire.
Le dernier changement majeur a consisté à stocker les données d'enregistrement sous forme de wire-format bytes bruts, préfixés par leur longueur, au lieu de structs Rust entièrement analysées. Cela permet de contourner la surcharge mémoire du système de typage riche de Rust pour les données ne nécessitant pas une analyse constante, réduisant ainsi davantage l'empreinte mémoire sur les 250 milliards d'entrées de cache de Big Pineapple. Pour une plongée plus approfondie dans ces techniques ingénieuses d'économie de mémoire, les lecteurs peuvent consulter How we saved 100 terabytes of memory by optimizing 1.1.1.1's DNS cache | Cloudflare Blog.
Cet article vous plaît ? Recevez-en un comme celui-ci chaque matin.
un e-mail par jour · désinscription en deux clics · aucun traqueur tiers
Le résultat : plus rapide et plus léger
Une réingénierie minutieuse des données a abouti à un résultat frappant : Cloudflare a réduit la taille typique de ses entrées de cache DNS de 953 octets à seulement 420 octets. Cela représente une réduction substantielle de 56 % de l'empreinte mémoire par entrée. Sur l'ensemble du parc mondial de Cloudflare, qui gère plus de 250 milliards d'entrées de cache, cette optimisation a permis de récupérer la somme impressionnante de 100 téraoctets de mémoire totale sans ajouter un seul nouveau serveur.
Il est crucial de noter que ces économies de mémoire ne se sont pas faites au détriment des performances ; au contraire, les opérations se sont considérablement accélérées. Les insertions dans le cache sont devenues 43 % plus rapides, passant de 625 000 à un nombre impressionnant de 893 000 entrées par seconde. De même, les recherches ont connu une amélioration de vitesse de 19 %, avec une latence passant de 828 nanosecondes à 670 nanosecondes. Ce scénario gagnant-gagnant rare a permis à la fois une efficacité significative des ressources et une vitesse opérationnelle accrue, défiant les compromis conventionnels.
Cloudflare déploiera stratégiquement les 100 téraoctets de mémoire libérés pour étendre la capacité de son cache DNS. Un cache plus grand et plus robuste permet à la plateforme "Big Pineapple" de résoudre une proportion encore plus importante de requêtes localement. Cela réduit directement le besoin de récupérer des données auprès des serveurs en amont, ce qui se traduit par une résolution DNS plus rapide pour les utilisateurs du monde entier et une amélioration visible de la vitesse internet globale pour tous.
Foire aux questions
Quelle quantité de mémoire Cloudflare a-t-il économisée grâce à cette optimisation ?
Cloudflare a récupéré environ 100 téraoctets (To) de RAM sur l'ensemble de son parc mondial, ce qui équivaut à la mémoire d'environ 130 serveurs, sans ajout de nouveau matériel.
L'optimisation de la mémoire a-t-elle ralenti le DNS de Cloudflare ?
Non, les optimisations ont eu l'effet inverse. Les insertions dans le cache sont devenues 43 % plus rapides et les recherches 19 % plus rapides, réfutant le compromis habituel entre utilisation de la mémoire et vitesse.
Quel langage de programmation Cloudflare a-t-il utilisé pour y parvenir ?
Cloudflare a utilisé le langage de programmation Rust, tirant parti de ses fonctionnalités de contrôle granulaire de la mémoire pour réécrire la structure des entrées de cache DNS.
Quel a été le principal changement effectué par Cloudflare pour économiser de la mémoire ?
Un changement clé a consisté à remplacer les types de données Rust standard comme Vec et String par Box<[T]> et Box<str>. Cela a éliminé un champ 'capacité' inutile pour les données en cache dont la taille ne change jamais, économisant ainsi 64 octets par entrée.
Comment Cloudflare a-t-il réduit la taille des entrées de cache DNS de 56 % ?
Ils ont mis en œuvre cinq changements clés au niveau de Rust : l'élimination des champs de capacité, le regroupement des sections de données dans un bloc unique avec des offsets, la suppression des informations redondantes, le boxing des variantes d'enum volumineuses et le stockage des données dans leur format wire-format brut.

