Conception d'un fil d'actualité : fan-out à l'écriture ou à la lecture

Concevoir un fil d'actualité : fan-out à l'écriture ou à la lecture, l'hybride des grands fils, caches de fil, classement, pagination et diagramme d'architecture.

· Fondateur et développeur

Publié le

Conception d'un fil d'actualité : fan-out à l'écriture ou à la lecture

Le fil d'accueil est la page la plus lue de tout produit social, et l'une des plus coûteuses à construire. Chaque utilisateur suit des centaines de comptes, chaque compte publie à son rythme, et le fil doit revenir en quelques centaines de millisecondes, classé, chaque fois que quelqu'un ouvre l'application.

La question centrale a deux réponses classiques, le fan-out à l'écriture et le fan-out à la lecture, et tout grand fil finit par mélanger les deux. Cet article les compare avec des chiffres, montre l'hybride et se termine par un diagramme des chemins complets de lecture et d'écriture.

Exigences

  • Un utilisateur voit les publications récentes des comptes qu'il suit, classées ou par ordre chronologique.
  • L'ouverture du fil prend bien moins d'une seconde au 99e centile.
  • Une nouvelle publication apparaît dans les fils des abonnés en quelques secondes.
  • Un défilement infini sans doublons ni trous.
  • Le système continue de fonctionner quand un compte a des dizaines de millions d'abonnés.

Les lectures dominent. En 2013, Twitter servait environ 300 000 lectures de timeline par seconde pour environ 6 000 écritures de tweets par seconde, selon cette description de son architecture. Concevez d'abord pour la lecture.

Ordres de grandeur

Hypothèse Valeur
Utilisateurs actifs par jour 10 millions
Ouvertures du fil par utilisateur et par jour 10, soit environ 1 160 lectures/s en moyenne
Nouvelles publications par jour 1 million, environ 12 publications/s
Abonnés moyens par auteur 200, soit environ 2 300 écritures de fan-out/s
Taille d'un fil en cache 800 ID de 8 octets, environ 64 Go pour tous les utilisateurs

Les moyennes semblent inoffensives. Pas les valeurs extrêmes : un auteur avec 20 millions d'abonnés crée 20 millions d'écritures de fan-out avec une seule publication, soit un dixième du travail de fan-out quotidien de tous les autres. C'est cette valeur extrême que la conception ci-dessous doit gérer.

Fan-out à l'écriture (push)

Quand quelqu'un publie, une tâche de fond récupère ses abonnés et insère l'ID de la publication en tête du fil en cache de chacun. Lire le fil revient alors à une seule recherche : prendre les ID les plus récents de sa propre liste et récupérer les publications.

  • Force : les lectures sont rapides et prévisibles.
  • Coût : une publication crée autant d'écritures que l'auteur a d'abonnés. Un compte de 30 millions d'abonnés transforme une publication en 30 millions d'écritures en cache, et ces abonnés la voient tard. La même description évoque des publications des plus gros comptes qui mettaient plusieurs minutes à atteindre tous les abonnés.
  • Gaspillage : vous écrivez dans les fils d'utilisateurs qui n'ouvriront pas l'application cette semaine.

Gardez chaque fil en cache court. Twitter plafonnait alors les timelines d'accueil à 800 entrées dans Redis ; les publications plus anciennes viennent de la base de données quand quelqu'un défile aussi loin.

Fan-out à la lecture (pull)

Ne faites rien quand quelqu'un publie. Quand un utilisateur ouvre son fil, récupérez les publications récentes de tous les comptes qu'il suit, fusionnez-les et classez-les.

  • Force : les écritures sont bon marché, et les gros comptes ne coûtent rien de plus.
  • Coût : chaque lecture du fil touche des centaines d'auteurs. À 300 000 lectures par seconde, cela fait beaucoup de requêtes pour un travail en grande partie répété.

L'hybride

La plupart des grands fils combinent les deux :

  • Pousser les publications des comptes ordinaires dans les fils en cache des abonnés.
  • Ne pas faire de fan-out pour les comptes au-dessus d'un seuil d'abonnés, et récupérer leurs publications récentes à la lecture.
  • À la lecture, fusionner le fil en cache avec les publications récupérées, puis classer.

Le seuil est un réglage, pas une loi. Placez-le là où le coût des écritures de fan-out commence à dépasser celui des lectures supplémentaires, et revoyez-le quand la distribution de vos abonnés évolue. Les utilisateurs qui ne suivent aucun gros compte ne paient jamais le chemin pull.

Classement

Un fil chronologique trie par date. Un fil classé fonctionne en trois étapes : rassembler les publications candidates (la liste fusionnée ci-dessus, plus des recommandations si vous en avez), noter chaque candidate avec des signaux comme la fraîcheur, la fréquence des interactions de l'utilisateur avec l'auteur et l'engagement sur la publication, puis filtrer (comptes bloqués, publications déjà vues, quasi-doublons).

Gardez le classement hors du chemin de fan-out. Le fan-out stocke des candidates ; le classement a lieu à la lecture avec les signaux les plus récents. Ainsi, une modification du modèle de classement ne demande aucun remplissage des fils en cache.

Suppressions, blocages et comptes privés

Les fils contiennent des ID : supprimer une publication est donc une seule écriture dans le stockage des publications, et le service de fil écarte les ID qui ne se résolvent plus. Les blocages et les comptes qui passent en privé demandent le même filtre à la lecture, car retirer un ID de millions de fils en cache est lent et source d'erreurs.

Pagination

Ne paginez jamais un fil avec des offsets. De nouvelles publications arrivent en haut pendant que l'utilisateur défile, donc la « page 2 » se décale et affiche des doublons. Renvoyez un curseur qui encode la position, comme l'ID ou le score du dernier élément servi, et demandez « les éléments après ce curseur ». Pour les fils classés, conservez brièvement la liste classée de la session afin que les pages suivantes restent cohérentes avec la première.

Le diagramme d'architecture

Un service de publications stocke les posts et publie des événements. Des workers de fan-out lisent le graphe social et vérifient si l'auteur dépasse un million d'abonnés. Si non, l'ID va dans les caches des abonnés ; si oui, le post est lu à la lecture. Un service de fil fusionne le tout.

13 composants · 13 connexions

Ouvrir dans Archboard
Contenu du diagramme
  • User
  • Post service
  • Posts store
  • Post events
  • Fan-out workers
  • Social graph
  • Author over 1M followers?
  • Feed cache
  • Pulled at read time
  • Feed service
  • Ranking
  • Media CDN
  • POST /posts
  • post.created
  • followers
  • no: push post ID
  • yes
  • GET /feed
  • cached timeline
  • big accounts
  • images and video
Un fil d'actualité hybride. Les publications de la plupart des auteurs sont poussées dans le fil en cache de chaque abonné ; celles des comptes très suivis sont récupérées à la lecture, fusionnées avec le fil en cache et classées avant de renvoyer la page.

Comment le lire :

  • Chemin d'écriture (en haut) : le service de publications stocke la publication et publie un événement. Les workers de fan-out lisent le graphe social et vérifient le nombre d'abonnés de l'auteur.
  • La décision : pour la plupart des auteurs, les workers poussent l'ID de la publication dans le cache de fil de chaque abonné. Pour les comptes au-dessus du seuil, rien n'est poussé ; leurs publications sont récupérées à la lecture.
  • Chemin de lecture (en bas) : le service de fil lit le fil en cache de l'utilisateur et les publications récentes des gros comptes dans le stockage des publications, les fusionne et les envoie au classement.
  • Les médias passent par un CDN, jamais par le service de fil.
  • Le bloc Markdown garde les règles empiriques à côté de la conception.

Ce qu'il faut mettre sur le diagramme

  • Les deux chemins, bien séparés. Écritures et lectures ont des problèmes de montée en charge différents.
  • Le seuil de fan-out. Écrivez le chiffre sur la décision, même s'il change plus tard.
  • Ce qui est stocké où. Les caches de fil contiennent des ID de publications, pas des publications ; le stockage des publications contient le contenu.
  • Où se fait le classement. Le lecteur doit voir qu'il a lieu à la lecture.

Le cache au centre de cette conception mérite son propre diagramme ; voyez l'article sur les stratégies de cache. L'article sur l'architecture événementielle couvre le côté événements et consommateurs, et l'exemple de diagramme de conception de système applique les mêmes conventions à un autre système.

Le construire dans Archboard

  1. Connectez-vous avec Google ou GitHub et créez un diagramme.
  2. Ajoutez une Personne du pack Modèle C4, puis des nœuds Conteneur pour le service de publications, les workers de fan-out, le service de fil et le classement.
  3. Ajoutez des nœuds Base de données du pack Modèle C4 pour le stockage des publications et le graphe social, Kafka et Redis du pack Données pour les événements de publication et les caches de fil, et un CDN du pack Cloud.
  4. Ajoutez une Décision et un Début / Fin du pack Logigramme pour le seuil d'abonnés.
  5. Dessinez le chemin d'écriture en haut et le chemin de lecture en dessous, et ajoutez un bloc Markdown du pack Base avec vos règles empiriques.

L'exemple utilise 13 nœuds de diagramme et reste dans les limites de l'offre Free.

FAQ

Le fan-out à l'écriture ou à la lecture, lequel est le meilleur ? Aucun des deux seul. Le fan-out à l'écriture donne des lectures rapides mais explose pour les comptes très suivis ; le fan-out à la lecture est bon marché à l'écriture mais lent à la lecture. Les grands fils utilisent un hybride.

Que doit stocker le cache de fil ? Des ID de publications, les plus récents en premier, plafonnés à quelques centaines par utilisateur. Récupérez le contenu séparément, pour que modifications et suppressions n'obligent pas à réécrire chaque fil.

Comment gérer la publication d'une célébrité ? Ne faites pas de fan-out pour les comptes au-dessus d'un seuil d'abonnés, et récupérez leurs publications récentes quand les abonnés ouvrent leur fil.

Comment doit fonctionner la pagination du fil ? Avec un curseur fondé sur le dernier élément servi, et non avec des numéros de page ou des offsets, pour que les nouvelles publications ne créent pas de doublons pendant le défilement.

Où se fait le classement ? À la lecture, sur la liste fusionnée des candidates. Le garder hors du fan-out permet de changer le classement sans reconstruire les fils en cache.

Commencez gratuitement — un siège, sans carte bancaire.

Articles associés

← Retour au blog

Ouvrez un canevas en une minute

Connectez-vous avec Google ou GitHub. Gratuit pour un siège. Passez à un forfait supérieur quand l’équipe vous rejoint.