Le contenu de ce site a été traduit à l'aide de l'intelligence artificielle (IA) ou d'une technologie de traduction automatique, et peut contenir des erreurs.

Skip to content

Comment le cache de Roblox a maintenu un débit de 1,38 milliard de requêtes par seconde au-delà des limites de Redis

Gérer une multiplication par dix du trafic en privilégiant la fiabilité et l'efficacité

Dans l'environnement de jeu multijoueur à très grande échelle de Roblox, les utilisateurs lisent et écrivent des données à un volume et à une vitesse qui dépasseraient largement les modèles de mise en cache standard. Un utilisateur ne remarquerait peut-être pas un délai de 200 ms sur une page web standard qui met en cache des données statiques, mais un délai de 200 ms pour récupérer des données d'interaction ou des métadonnées de serveur sur Roblox pourrait bloquer le gameplay et perturber le monde simulé du jeu. Roblox fonctionne sur un écosystème de données imprévisibles, à haute vélocité et éphémères qui doivent être récupérées en quelques millisecondes pour préserver l'immersion. 

Notre couche de mise en cache est conçue pour agir comme un énorme amortisseur entre des millions d'utilisateurs simultanés et notre stockage persistant. Elle gère une charge de pointe de 1,38 milliard de requêtes par seconde (QPS), avec des clusters logiques individuels dépassant les 100 millions de QPS. Dans cet article, nous vous dévoilerons comment nous avons conçu un service de mise en cache fédéré capable de dépasser les limites des nœuds Redis® pour offrir un débit massif, tout en gérant une augmentation de trafic sans précédent (multipliée par 10) sur notre plus grand cluster.

Lecture/écriture totale du cache sur tous les nœuds de mise en cache.

Dépasser les limites du secteur pour répondre à des demandes sans cesse croissantes

Chez Roblox, notre service de mise en cache repose sur la technologie Redis®, et un cluster Redis® peut potentiellement prendre en charge jusqu’à 1 000 nœuds. Cependant, la limite empirique et reconnue par le secteur est bien inférieure, de l’ordre de 400 à 500 nœuds.1 Cela s’explique par le protocole Gossip décentralisé de Redis®, qui utilise la communication entre nœuds pour partager les informations de santé, d’état et de mappage des shards. Dans les clusters de plus de quelques centaines de nœuds, les échanges du protocole Gossip commencent à consommer des ressources CPU et réseau substantielles. Chaque nœud doit traiter les mises à jour de statut provenant d’un nombre toujours croissant de pairs, détournant ainsi des ressources de sa tâche principale qui consiste à servir des données avec une faible latence. Cette surcharge du plan de contrôle peut entraîner une instabilité et des performances imprévisibles, ce qui explique pourquoi les fournisseurs de cloud géré imposent des limites de nœuds. Il s’agit d’un plafond pratique qui ne peut être dépassé sans compromettre la fiabilité.

Cette limite pratique par cluster unique n’est pas suffisante pour prendre en charge une plateforme fonctionnant à l’échelle de Roblox, où le plus grand service backend exigeait plus de 100 millions de QPS de l’un de nos clusters Redis® logiques en période de pointe, contre 10 millions trois mois auparavant.

Nous avons adopté une architecture fédérée pour surmonter cette limitation architecturale, en plaçant un proxy inverse client devant plusieurs clusters Redis® plus petits et indépendants. Nous utilisons une limite de 400 nœuds par cluster pour garantir des performances prévisibles. 

Du point de vue de nos milliers d’applications et de services, le proxy inverse client présente ces clusters disparates comme un service de mise en cache unique, unifié et évolutif. Cette fédération nous permet d’évoluer horizontalement presque sans limite en ajoutant simplement davantage de clusters à la fédération, contournant ainsi entièrement les contraintes du protocole Gossip. Aujourd'hui, notre plus grand déploiement de mise en cache unique comprend plus de 6 000 nœuds Redis®, soutenus par plus de 15 clusters Redis® indépendants — une échelle rendue possible uniquement par ce modèle fédéré.

Schéma général de l'architecture fédérée.

Le processus de migration en trois étapes

Passer d'une configuration traditionnelle à une architecture fédérée tout en gérant des centaines de clusters de production actifs a constitué un défi de taille. À la fin de l'année 2024, bon nombre de ces clusters approchaient déjà notre limite de 400 nœuds, rendant urgente la transition vers un modèle à plus grande capacité. Le principal défi résidait dans le fait que les instances Redis® standard fonctionnent de manière isolée et n'ont aucune connaissance inhérente des autres clusters, ce qui nécessitait une méthode personnalisée pour répliquer les données pendant la migration.

Pour résoudre ce problème, nous avons développé un pipeline de migration transparent en trois étapes, conçu pour assurer la transition des services sans aucune intervention du client ni modification côté application.

Le workflow de migration

  1. Double écriture : nous avons configuré le proxy inverse client pour effectuer des écritures doubles, en envoyant simultanément les données entrantes à la fois vers le cluster d'origine et vers la nouvelle destination fédérée.
  2. Parité et basculement en lecture : une fois que le processus de double écriture a rempli le nouveau cluster avec des données identiques, toutes les données étant soumises à un délai de validité (TTL), nous avons transféré le trafic de lecture de l'ancien cluster vers la nouvelle architecture fédérée.
  3. Mise hors service : après avoir confirmé la stabilité du nouveau cluster, nous avons cessé toutes les opérations d'écriture sur le cluster d'origine et l'avons mis hors service.

Nous avons réussi à migrer nos services les plus critiques vers un modèle fédéré prenant en charge plus de 6 000 nœuds, dépassant largement les limites d’un cluster Redis® unique. 

Gérer la croissance explosive de Grow a Garden

À partir d’avril 2025, nous avons été confrontés à un défi de capacité d’une ampleur que peu de services ont jamais rencontrée. Le catalyseur a été le succès phénoménal de Grow a Garden. En l’espace de quelques semaines seulement après son lancement, Grow a Garden a commencé à battre non seulement les records de Roblox, mais aussi les records historiques de l’industrie du jeu vidéo en matière d’utilisateurs simultanés (CCU). En seulement trois mois, l’audience du jeu a dépassé notre précédent record de CCU pour un seul jeu, qui était de 2,8 millions de joueurs, pour atteindre un pic de plus de 21 millions. Cette croissance explosive a multiplié par 10 le trafic vers notre plus grand cluster Redis® et par 3 le trafic vers la mise en cache globale.

Roblox exploite ses systèmes de mise en cache sur des machines physiques dans ses propres centres de données, et cette croissance sans précédent ne faisait pas partie de notre plan de capacité à long terme. Pour survivre à cette vague déferlante et maintenir la stabilité de la plateforme, nous avons dû améliorer radicalement l’efficacité de notre parc existant tout en donnant la priorité à la fiabilité. 

Améliorer l'efficacité pendant la vague de trafic

Tout d’abord, nous devions identifier les goulots d’étranglement les plus critiques de notre système afin de hiérarchiser les efforts d’optimisation. Roblox Caching s’exécute sur deux pools de matériel distincts : l’un pour les nœuds backend Redis® et l’autre pour Envoy. Notre parc Redis® est principalement limité par la mémoire, tandis que le proxy client est limité par la puissance de calcul. 

Pour notre parc Redis® limité par la mémoire, nous avons mis en place une nouvelle politique de planification afin de mieux équilibrer l'utilisation de la mémoire entre nos groupes de machines physiques. Nous avons également ajusté la taille des conteneurs pour réduire la fragmentation de la mémoire. 

Pour améliorer l'utilisation du CPU, nous avons mis en œuvre deux optimisations clés de notre système de cache :

  1. Auto-scaling du proxy client : nous avons ajusté la politique d'auto-scaling de notre parc de proxys clients dépendant de la puissance de calcul. En réduisant l'écart autorisé par rapport à la cible CPU, nous avons obtenu une utilisation moyenne du CPU plus élevée pour les tâches sans modifier le seuil de scale-up.
  2. Contrôles de santé du cluster interne : nous avons optimisé les contrôles de santé internes afin d'éliminer les pulsations inutiles, tout en conservant l'intervalle de détection d'origine et en économisant des ressources de calcul.

Ces changements ont permis d'améliorer l'utilisation du CPU de près de 10 %. Enfin, nous avons regroupé ces deux charges de travail très différentes au sein du même pool de machines grâce à l'isolation au niveau des conteneurs cgroup, ce qui a réduit nos besoins globaux en capacité de 25 %.

Une ingénierie au service d'une fiabilité à toute épreuve

La fiabilité est restée notre priorité absolue, même alors que nous nous efforcions de répondre à une demande record. 

Observabilité de bout en bout

Nous nous sommes d'abord attachés à comprendre les défaillances potentielles en investissant massivement dans notre pile d'observabilité pour une surveillance complète et de bout en bout de l'état de santé de notre système de mise en cache. En suivant les succès côté serveur et les défaillances côté client tout au long du cycle de vie des requêtes, nous avons rapidement détecté et diagnostiqué les problèmes, prévenant souvent leur impact sur les utilisateurs. Par exemple, nous avons ajouté la détection des clés chaudes, qui échantillonne le trafic en temps réel pour identifier les données excessivement consultées et qui sollicitent la couche de mise en cache. Lorsque le système signale une clé chaude, nous pouvons limiter de manière proactive le trafic vers ces clés, tant du côté serveur que du côté client, afin d’éviter une surcharge du système.

Gestion des défaillances

Dans tout système distribué de cette envergure, des défaillances partielles surviennent parfois. Nous avons conçu notre système pour gérer ces défaillances de manière élégante et minimiser leur impact.

  • Pannes partielles dans les requêtes par lots : nous avons repensé l'architecture du système pour gérer les pannes partielles dans les requêtes par lots. Auparavant, l'échec d'une seule requête dans un lot fragmenté entraînait l'échec de l'ensemble du lot. Désormais, les requêtes peuvent aboutir même si certaines clés ne peuvent pas être récupérées.
  • Atténuation du blocage en tête de file : les systèmes de mise en cache sont censés être rapides. Même un ralentissement mineur du réseau peut entraîner une accumulation des requêtes et des délais d'expiration, un phénomène connu sous le nom de « blocage en tête de file ». Nous avons mis en place des mécanismes de réessai intelligents et des stratégies de mise en pool des connexions à la fois au niveau de la couche client et de la couche proxy afin d'empêcher qu'une seule requête lente n'affecte les autres.
  • Injection de pannes et de latence : pour tester la résilience, nous injectons régulièrement des pannes contrôlées (pannes de nœuds, pannes de racks, latence réseau, erreurs API) en production à l’aide de notre infrastructure d’injection de pannes. En analysant le comportement du système dans ces conditions, nous identifions et corrigeons de manière proactive les faiblesses, garantissant ainsi une fiabilité continue.

Résilience côté client

Pour éviter que notre système de mise en cache ne soit submergé par des pics de trafic soudains, nous avons mis en place une résilience robuste face aux défaillances côté client. Ce mécanisme fonctionne au niveau de l'application, garantissant qu'aucun service ne peut monopoliser les ressources du cache.

  • Limitation de débit pour le contrôle de flux : notre client dispose d'une limite sur le trafic sortant total, garantissant qu'il n'envoie pas plus de requêtes que le service backend ne peut en traiter. Les requêtes sont régulées lorsque cette limite est dépassée, ce qui empêche le service d'être submergé.
  • Mécanismes de réessai et de délai d'expiration : nos bibliothèques clientes utilisent des mécanismes de réessai soigneusement ajustés, comprenant des délais d'expiration et un budget de réessais, afin d'éviter les problèmes de « thundering herd ». Cela limite l'ampleur des répercussions, libère des ressources et améliore la réactivité globale du système en empêchant les blocages indéfini et les réessais simultanés de le saturer.

L'avenir de la mise en cache chez Roblox

Le parcours de mise en cache de Roblox a commencé avec des clusters Redis® bare-metal gérés directement par chaque propriétaire d'application, puis est passé à des clusters Memcached gérés par notre équipe de mise en cache, pour finalement évoluer vers un modèle Redis®-as-a-service, dans lequel un cluster Redis® est fourni en tant que service géré. Bien que ce modèle se soit révélé très efficace à notre échelle actuelle, nous reconnaissons ses limites en termes d'efficacité du service et d'isolation des ressources pour une plateforme aussi dynamique que Roblox.

À mesure que la plateforme continue de se développer, nous prévoyons d’investir massivement dans un service de mise en cache multi-locataires de nouvelle génération basé sur ValKey. Cette architecture est conçue pour réduire considérablement les coûts en partageant des ressources communes via la multi-location, tout en garantissant l’isolation des données, des ressources et des pannes pour chaque locataire. Notre objectif est d’améliorer considérablement les performances, l’évolutivité, la fiabilité et la rentabilité de notre infrastructure de mise en cache afin de répondre aux exigences d’expériences de plus en plus massives sur Roblox.

Remerciements : 

Cette réussite est le fruit d'un travail collaboratif. Merci aux autres membres de l'équipe qui y ont largement contribué : Utkarsh Singh, Karthick Ramachandran, Vineesha Kasireddy, Weiji Hu et Leon Gao.

1AWS limite ElasticCache à 500 nœuds.

*Redis est une marque déposée de Redis Labs Ltd. Tous les droits y afférents sont réservés à Redis Labs Ltd.