Concevoir LeetCode à grande échelle
LeetCode semble simple. Les utilisateurs parcourent les problèmes, écrivent du code et reçoivent des retours.
La réalité est différente. Derrière l'écran se cache un système distribué. Il doit gérer des millions d'utilisateurs et des milliers de soumissions de code chaque seconde. Il doit survivre à des pics de trafic massifs lors des concours.
Voici comment concevoir une plateforme de codage évolutive.
Fonctionnalités principales
- Parcourir et rechercher des problèmes par difficulté ou par tags.
- Écrire du code dans plusieurs langages.
- Soumettre des solutions et recevoir les résultats en quelques secondes.
- Participer à des concours hebdomadaires avec des classements en direct.
Exigences de montée en charge
- 10 millions d'utilisateurs inscrits.
- 2 millions d'utilisateurs actifs quotidiens.
- 100 000 utilisateurs simultanés pendant les concours.
- 20 000 soumissions de code par minute.
Architecture du système
Vous ne pouvez pas exécuter le code de manière synchrone. Si un utilisateur soumet du code, l'API ne doit pas attendre le résultat. Cela provoque des timeouts.
Au lieu de cela, utilisez une approche asynchrone :
- L'utilisateur soumet le code via l'API.
- Le système place la soumission dans une file d'attente de messages (Message Queue) comme Kafka ou RabbitMQ.
- Des workers d'exécution de code (Code Execution Workers) récupèrent les tâches de la file d'attente.
- Les workers exécutent le code dans un sandbox.
- Le service de résultats (Result Service) met à jour le statut.
Le sandbox est l'élément le plus critique. Exécuter du code arbitraire sur vos serveurs est dangereux. Vous devez utiliser des environnements isolés.
Les conteneurs Docker sont le meilleur choix car ils sont :
- Légers.
- Rapides à démarrer.
- Faciles à mettre à l'échelle horizontalement.
- Sécurisés lorsque vous limitez l'accès au CPU, à la mémoire et au réseau.
Stratégie de stockage des données
Utilisez différentes bases de données selon les besoins :
- Bases de données relationnelles (PostgreSQL/MySQL) : Utilisez-les pour les utilisateurs, les concours et les classements. Elles garantissent l'intégrité des données.
- Bases de données NoSQL (DynamoDB/MongoDB) : Utilisez-les pour les problèmes, les cas de test et les journaux de soumission. Elles s'adaptent mieux à la croissance des données.
- Mise en cache (Redis) : Utilisez-la pour les détails des problèmes et les informations sur les concours afin de réduire la charge de la base de données.
Classements en temps réel
Pendant les concours, les classements changent chaque seconde. Ne recalculez pas l'intégralité du classement chaque fois que quelqu'un soumet du code.
Au lieu de cela :
- Mettez à jour les scores de manière asynchrone.
- Utilisez les
Sorted Setsde Redis pour gérer les classements. - Poussez les mises à jour vers les utilisateurs via WebSockets.
Fiabilité et compromis
Dans cette conception, la disponibilité est la priorité. Un léger retard dans la mise à jour d'un classement est acceptable. Un plantage du système pendant un concours ne l'est pas. Nous choisissons la cohérence éventuelle (eventual consistency) pour maintenir la plateforme opérationnelle.
Comment concevriez-vous le moteur d'exécution de code ? Utiliseriez-vous Docker ou des microVM ?
Source : https://dev.to/himanshudevgupta/system-design-designing-leetcode-at-scale-45op
Communauté d'apprentissage optionnelle : https://t.me/GyaanSetuAi
