- Lors de la conception d’un système, il est en pratique difficile de satisfaire simultanément une cohérence parfaite, une haute disponibilité, une faible latence et un débit élevé
- Il est important de trouver une conception adaptée en recherchant le bon point d’équilibre pour l’application
- Dans la conception du fil suivi / de la timeline de Bluesky, un compromis a été appliqué consistant à sacrifier partiellement la cohérence afin d’améliorer les performances d’écriture
- Résultat : une réduction de plus de 96 % de la latence P99, sans impact négatif pour les utilisateurs
Fanout de la timeline
- Lorsqu’un utilisateur publie un message sur Bluesky, celui-ci est indexé par le système, stocké dans la base de données, puis servi dans les réponses de l’API
- En parallèle, il passe par un processus de « fanout » qui insère ce message dans la table de timeline de chacun de ses abonnés
- Cela consiste à consulter la liste des abonnés, puis à effectuer des insertions en ordre inverse dans la table de timeline de chaque abonné
- Les tables de timeline sont partitionnées par utilisateur et stockées dans une base de données distribuée (ScyllaDB), puis répliquées sur plusieurs shards pour assurer une haute disponibilité
- Chaque utilisateur peut être affecté à un shard différent
- Pour économiser l’espace de stockage, les timelines qui dépassent une certaine longueur suppriment périodiquement les références aux anciens messages
Le problème des hot shards
- Bluesky compte environ 32 millions d’utilisateurs, et la base de données de timelines est répartie sur plusieurs centaines de shards
- Dans un système utilisé par des millions de personnes, il peut exister des utilisateurs avec un nombre extrêmement élevé de relations de suivi
- Exemple : des utilisateurs qui suivent plusieurs centaines de milliers de comptes
- Un même shard stocke les timelines de nombreux utilisateurs
- Si un utilisateur provoque un très grand nombre d’écritures, le shard concerné entre en surcharge (« hot shard »)
- Ces hot shards concentrent les opérations de lecture ou d’écriture, ce qui propage la latence aux autres utilisateurs hébergés sur le même shard
Accumulation de latence
- Si un utilisateur a 2 000 000 d’abonnés, une écriture séquentielle peut prendre plus de 20 minutes
- Pour réduire ce délai, le fanout peut être parallélisé, ce qui raccourcit la latence moyenne
- Mais la latence P99 (d’environ 15 millisecondes ou plus) peut se produire à répétition et retarder l’ensemble du traitement parallèle
- Lorsqu’un utilisateur a énormément d’abonnés, la latence P99 ou P99.9 peut faire grimper le temps total de fanout, dans le pire des cas, de plusieurs minutes à plusieurs dizaines de minutes
Timelines lossy
- Pour les utilisateurs qui suivent un nombre excessivement élevé de comptes, il est en pratique impossible d’afficher tous les messages exactement dans l’ordre
- Il est d’ailleurs difficile pour une personne de réellement consommer tous ces messages
- Bluesky a donc introduit une approche consistant à « ignorer » probabilistiquement certaines écritures dans la timeline des utilisateurs dont le nombre d’abonnements dépasse un certain seuil (par ex.
reasonable_limit)
- La formule utilisée est
loss_factor = min(reasonable_limit / num_follows, 1)
- Lors du fanout, une valeur aléatoire est générée, et si elle est supérieure à
loss_factor, l’écriture dans la timeline est omise
- Cela permet de limiter les écritures excessives sur la timeline d’un utilisateur donné et d’éviter une dégradation globale des performances du shard
À propos du cache
- Comme les écritures de timeline dépassent le million par seconde, interroger directement la base de données pour connaître le nombre d’abonnements d’un utilisateur à chaque écriture créerait une charge énorme
- À la place, les comptes ayant un nombre élevé d’abonnements sont mis en cache dans Redis sous forme de sorted set
- Les instances du service de fanout chargent ces informations de cache en mémoire toutes les 30 secondes
- Il devient ainsi possible de consulter rapidement les informations sur les utilisateurs à fort volume d’abonnements pendant le processus de fanout
- Comme il n’est pas nécessaire que ces données de cache soient parfaitement à jour, cette approche accepte une certaine imperfection en échange de meilleures performances et d’une meilleure scalabilité
Résultats
- Après l’introduction des timelines lossy, les hot shards ont pratiquement disparu de la base de données Timelines
- La latence P99 pour traiter une page de fanout a diminué de plus de 90 %
- Pour l’ensemble du travail de fanout, les traitements qui prenaient 5 à 10 minutes au P99 ont été ramenés à moins de 10 secondes
- Cela montre qu’il est possible de préserver la scalabilité à grande échelle tout en répondant pleinement aux attentes des utilisateurs du service, même en sacrifiant partiellement la cohérence
- L’architecture de timeline de Bluesky peut encore être améliorée, mais ce changement a déjà considérablement accru le débit d’écriture et la scalabilité
1 commentaires
Commentaires sur Hacker News
En tant qu’amateur de systèmes, je fais partie des gens qui aiment ce genre d’articles. Il est facile de tomber dans l’état d’esprit du « il faut que ce soit parfait »
Je me demande pourquoi ne pas implémenter la timeline de manière hybride selon la popularité du compte
Une solution intéressante à un problème intéressant. Merci du partage
Je m’interroge sur cette stratégie qui sacrifie la cohérence. Je me demande s’il y a des réflexions sur d’autres approches que le fan-out complet à la lecture ou à l’écriture
Il n’est pas nécessaire de fournir dans un ordre parfaitement chronologique tout ce que publient les milliers de personnes que chacun suit, mais il semble raisonnable de fournir suffisamment de contenu pour qu’il y ait toujours du contenu frais dans la timeline
Je me demande comment fonctionnerait l’idée de limiter le nombre d’abonnés pour éviter le problème des hot shards
AWS a une approche générale élégante pour ce problème
Les comptes qui suivent des centaines de milliers d’utilisateurs sont clairement des bots qui aspirent le contenu. Je les bloquerais, point final
Quand on va directement sur le profil d’un utilisateur pour voir toutes ses publications, il arrive que des posts qui devraient être dans la timeline n’y soient pas
Je me demande pourquoi ils ont implémenté le fan-out de façon à ce que chaque « page » bloque la récupération de la suivante