Comment Roblox a fait évoluer sa plateforme Kafka pour traiter plus de 18 000 milliards de messages par jour
Optimisation de bout en bout pour des systèmes efficaces

Au cours des deux dernières années, nous avons multiplié par plus de 2,5 chaque année la capacité de notre plateforme interne de file d’attente de messages, pour atteindre plus de 18 000 milliards de messages par jour. Nous traitons actuellement plus de 300 millions de messages par seconde tout en maintenant une disponibilité de 99,995 %. Cette plateforme de file d’attente de messages, construite sur Apache Kafka, est devenue un élément essentiel de la manière dont nous transférons les données au sein de l’entreprise. Elle prend en charge des charges de travail telles que les événements d’analyse, les pipelines de modération, les systèmes de mise en relation, et bien d’autres encore. Ces chiffres sont impressionnants, mais ce qui importe le plus, c’est la manière dont nous y sommes parvenus.
L’impact de l’optimisation de bout en bout
Une idée fausse courante concernant les systèmes à grande échelle est que la capacité relève principalement d’un problème matériel. D’après notre expérience, les améliorations les plus significatives sont obtenues en considérant l’évolutivité comme un problème d’ingénierie de bout en bout.
Les équipes de Roblox ont développé diverses plateformes s’appuyant sur la file d’attente de messagerie, notamment des pipelines d’entraînement d’IA, des abstractions de traitement de flux et des systèmes de télémétrie. En collaborant avec les responsables de ces plateformes, nous contribuons à garantir le respect des meilleures pratiques, telles que l’activation du traitement par lots et de la compression. Cela permet d’obtenir des systèmes efficaces sans que les utilisateurs finaux aient à s’occuper des détails de configuration.

Il est essentiel de s’aligner avec les équipes qui utilisent la plateforme sur les priorités métier, les indicateurs de performance et les critères de réussite ; cela contribue à favoriser la collaboration entre les équipes. Les indicateurs clés varient selon les couches du système : alors que nous pouvons optimiser les serveurs Kafka pour traiter efficacement de grands volumes de données avec moins de machines, les utilisateurs finaux peuvent souhaiter optimiser la latence de bout en bout à la milliseconde près, ou le temps total nécessaire à leur tâche batch pour terminer l’écriture des données.
Tout le trafic n’est pas identique
L’une des principales leçons que nous avons tirées est que les plateformes à grande échelle sont plus fiables lorsqu’elles ne traitent pas tout le trafic de la même manière. Certaines charges de travail exigent une latence très faible et une protection renforcée lors des pics de trafic, comme nos systèmes de mise en relation, qui fonctionnent avec une latence de bout en bout inférieure à 10 millisecondes. D’autres génèrent des volumes extrêmement élevés mais sont plus tolérants au retard, comme nos pipelines d’ingestion d’événements analytiques, qui peuvent fonctionner sans heurts même avec une latence de bout en bout de plusieurs secondes. Si les deux sont traités de la même manière, le trafic à plus fort volume peut évincer le trafic plus critique pour l’activité, en particulier pendant les pics de charge.
Pour résoudre ce problème, nous regroupons le trafic en fonction des exigences de chaque cas d’utilisation afin de garantir que chacun soit correctement pris en charge. Le compromis entre latence de bout en bout et débit dans Kafka est l’une des principales considérations lors de la mise en place de nouveaux cas d’utilisation. Cela vaut pour l’ensemble du chemin des données, du producteur au serveur puis au consommateur : chacun de ces maillons dispose de configurations pouvant être ajustées pour optimiser soit le débit, soit la latence. Les configurations côté client peuvent être ajustées pour des cas d’utilisation individuels, mais celles côté serveur sont moins flexibles, car il n’est pas viable de maintenir un cluster dédié pour chaque cas d’utilisation distinct.
Nous appliquons cette approche d’optimisation de chaque cluster en fonction des cas d’utilisation qu’il dessert tout au long de la pile, jusqu’à la couche matérielle.
Le matériel reste un élément critique
Nous avons commencé par faire tourner Kafka sur des machines équipées de disques durs en RAID10. À mesure que le volume de trafic augmentait, la capacité d’E/S disque de nos machines est devenue le principal goulot d’étranglement limitant le débit de Kafka par serveur. Pour y remédier, nous avons procédé à deux optimisations. Nous avons converti les brokers pour qu’ils utilisent autant que possible des SSD, déplaçant ainsi le goulot d’étranglement de la capacité d’E/S disque vers le débit réseau ou l’espace disque (car les SSD que nous avons acquis offraient moins d’espace disque). Cela a multiplié par 10 le débit d’E/S disque. Nous avons également commencé à convertir nos disques durs restants du RAID 10 vers le RAID 0. Cela a à peu près doublé la capacité d’E/S disque, car chaque écriture en RAID 0 ne nécessite qu’une seule écriture sur disque au lieu de deux.
En contrepartie, comme chaque réplique de message est écrite sur un seul disque par broker au lieu de deux, le passage du RAID10 au RAID0 peut comporter un risque en termes de fiabilité. Pour préserver cette fiabilité, nous avons travaillé sur des cas d’utilisation critiques afin d’augmenter les accusés de réception et la réplication des données, notamment en effectuant des réplications vers plusieurs régions.
Tirer pleinement parti des capacités de stockage et de débit de chaque machine a nécessité un travail supplémentaire. Nous avons optimisé nos allocations de mémoire et nos configurations de gestion du cache de pages afin que Kafka puisse mettre les messages en mémoire tampon et servir les lectures ou les données récentes à partir de la mémoire, en n’accédant au disque et en consommant de précieux IOPS que lorsque cela est nécessaire, par exemple lors de la lecture de données historiques.
Nous effectuons également des tests de performances pour cerner les limites matérielles et déterminer les contraintes logicielles à mettre en place. Concrètement, cela implique de définir des limites de lecture et d’écriture pour les brokers Kafka, qui varient en fonction du matériel sur lequel tournent les clusters. Nos machines équipées de SSD peuvent gérer un débit plus élevé par broker, tandis que celles dotées de disques durs (HDD) sont mieux adaptées aux cas d’utilisation nécessitant la conservation à long terme de données historiques.
Concevoir pour la fiabilité
La plateforme de files d’attente est un composant essentiel de l’infrastructure de Roblox, et il est crucial d’en garantir la fiabilité. Les tests préalables au lancement ainsi que les tests continus de scénarios de défaillance (parfois appelés « exercices de simulation de situation réelle ») ont joué un rôle déterminant pour valider que notre infrastructure est résiliente face à divers modes de défaillance, tels que la défaillance d’un hôte unique, la défaillance d’un rack, les problèmes de réseau et bien d’autres encore. La plateforme interne de tests de chaos de notre équipe chargée de la fiabilité a permis de mettre en place et de rationaliser le processus de simulation de divers scénarios de défaillance. Ces tests aident à identifier les améliorations ou les régressions du système et à atteindre l’excellence opérationnelle en validant régulièrement les alertes et en s’assurant que les opérateurs maîtrisent les étapes à suivre pour gérer divers problèmes, même les plus rares.
Nous avons également conçu et mis en œuvre des processus de basculement capables de rétablir rapidement le fonctionnement des files d’attente pour les cas d’utilisation dépendants. Même en cas de défaillance totale d’un cluster individuel, nous sommes en mesure de basculer les clients vers un cluster opérationnel disposant d’une capacité suffisante. Afin de garantir la disponibilité d’une capacité inutilisée suffisante, nous maintenons un cluster Kafka de secours. Cela permet d’éliminer tout risque de problèmes liés à des « voisins bruyants » lors du transfert du trafic entre les clusters actifs.
Le diagramme ci-dessous présente les étapes générales de l’exécution d’un basculement. Tout d’abord, un opérateur identifie la portée du basculement. Ensuite, les sujets et les listes de contrôle d’accès (ACL) sont créés sur le cluster de secours via une opération automatisée en masse. Enfin, l’opérateur met à jour les configurations de notre service de plan de contrôle, que les clients interrogent pour déterminer si une reconnexion est nécessaire. L’illustration ci-dessous concerne des cas d’utilisation génériques en temps réel. Nous disposons de stratégies et d’outils supplémentaires pour d’autres cas d’utilisation et scénarios, notamment la mise en miroir des sujets à l’aide de Mirror Maker 2, des outils de réintégration des données, et bien plus encore.

Roblox applique des exigences strictes en matière de sécurité, qui constitue également un élément essentiel de la fiabilité. Nous avons mis en œuvre des fonctionnalités de sécurité standard telles que le chiffrement en transit (avec mTLS), le chiffrement au repos (avec disque chiffré), des exigences relatives à la durée de conservation des données, ainsi que l’authentification et l’autorisation via les entités Kafka et les listes de contrôle d’accès (ACL). Nous procédons également à une rotation automatique des identifiants de nos utilisateurs afin de renforcer encore davantage la sécurité.
Par ailleurs, nous avons intégré une gestion personnalisée au sein de notre plan de contrôle afin de faciliter l’application rapide des correctifs du système d’exploitation et le vidage des racks. Lors de l’application des correctifs du système d’exploitation, nous conservons les données sur le disque afin que les machines corrigées puissent rapidement retrouver leur état de fonctionnement normal après avoir réintégré le cluster Kafka. De cette manière, nous pouvons appliquer des correctifs à l’ensemble de notre parc en moins de 30 jours, même si celui-ci continue de s’agrandir. Grâce à des outils tels que Cruise Control, nous répliquons nos données en tenant compte de la configuration des racks, de sorte que des racks entiers puissent être mis hors ligne pour la maintenance et l’application de correctifs réseau sans affecter la disponibilité des données.
La voie à suivre
La prochaine étape pour notre plateforme de files d’attente consiste à la rendre encore plus simple pour les développeurs, tout en continuant à améliorer son élasticité et son isolation. Nous investissons dans de nouvelles abstractions, notamment des proxys de producteurs et de consommateurs, ainsi que des connecteurs entièrement gérés. Ces systèmes permettront de dissocier davantage les équipes applicatives de la complexité de l’infrastructure et rendront nos plateformes plus faciles à adopter et à gérer à grande échelle. Par exemple, un proxy consommateur permettra de mettre en œuvre le modèle de consommateurs concurrents, ce qui permettra à la plateforme de prendre en charge un éventail plus large de cas d’utilisation et de dissocier la mise à l’échelle des processeurs de celle des partitions Kafka. Cela s’avère particulièrement utile pour des cas d’utilisation tels que le traitement de pics massifs de notifications, qui ne nécessitent pas d’ordre particulier et peuvent bénéficier d’une augmentation temporaire du nombre de processeurs sans être soumis à des objectifs de livraison de bout en bout impératifs.
La mise à l’échelle de Kafka ne se résume pas à augmenter le nombre et la taille des clusters. Il s’agit de mettre en place la discipline d’ingénierie, les abstractions et le modèle opérationnel qui permettront à Roblox de poursuivre sa mission : connecter chaque jour un milliard de personnes dans un esprit d’optimisme et de civilité.
Remerciements : Merci à Danny Yuan et George Li pour leurs contributions significatives à la plateforme de file d'attente Roblox.


