00

Points de contrôle de pratique

Le rythme de l’entretien reste compact, pour que la page consacre son attention aux vraies décisions de conception.

  1. 01
    Clarifier le périmètre
  2. 02
    Besoins + échelle
  3. 03
    API + modèle de données
  4. 04
    Dessiner l’architecture
  5. 05
    Deep dive
  6. 06
    Décision de compromis
01

Les besoins qui façonnent la conception

Ne vous contentez pas d’énoncer les besoins : demandez-les. Chaque carte associe la contrainte de conception à une question de clarification que vous pouvez dire à voix haute avant de dessiner l’architecture.

Besoins fonctionnels

01Quelles fenêtres les classements exigent-ils — des plages arbitraires, ou heure/jour/mois fixes ?

Des fenêtres fixes uniquement : la dernière heure, le dernier jour, le dernier mois, et depuis toujours. Les plages de temps arbitraires sont hors périmètre.

02Jusqu'où K peut-il monter ?

Jusqu'à 1 000 chansons par classement.

03À quelle vitesse une nouvelle écoute doit-elle apparaître dans le classement ?

En une minute environ — quasi-temps réel, pas instantané.

04Deux chansons à égalité au rang K - qu'est-ce qui départage ?

Une départage fixe — le compte d'abord, puis l'id du morceau. La même requête renvoie la même liste à chaque fois, donc les classements ne scintillent jamais.

05Classements par région et par genre - maintenant ou plus tard ?

Plus tard. Le classement global part d'abord ; les classements par région et par genre viennent dans une phase ultérieure.

06Une chanson est retirée - quand quitte-t-elle le classement ?

Immédiatement — une chanson retirée doit disparaître de tous les classements d'un coup, sans jamais attendre que les comptes soient recalculés.

Hors périmètreLes plages de temps arbitraires (requêtes from/to) · Les classements personnalisés par utilisateur ou par région · La détection de fraude aux écoutes

Besoins non fonctionnels

01Pour quel débit d'événements l'ingestion doit-elle prévoir ?

Des centaines de milliers d'événements d'écoute par seconde au pic.

02À quelle vitesse une lecture de classement doit-elle répondre ?

Des dizaines de millisecondes — ouvrir un classement doit sembler instantané.

03Les chiffres doivent-ils être exacts ?

Demandez d'abord, car cela change le design. Le chemin principal ici, ce sont les comptes exacts. Ces chiffres peuvent alimenter le calcul des royalties, donc un surcompte systématique n'est pas acceptable.

04Si quelque chose plante en milieu d'heure, est-il acceptable de perdre quelques écoutes — ou chaque écoute doit-elle finir par être comptée ?

Rien ne doit être perdu. Chaque écoute doit finir par être comptée. Une brève baisse de fraîcheur pendant la reprise est acceptable.

05Et quand une chanson prend la moitié de toutes les écoutes ?

Un seul tube peut s'accaparer d'un coup une énorme part de toutes les écoutes. Cela ne doit pas ralentir le comptage ni laisser les classements périmés.

Continuez à demander — l’entretien est une conversation

Les vrais entretiens sondent bien plus qu’une liste propre. Ces questions de périmètre séparent ceux qui interrogent le problème de ceux qui le récitent.

  • Combien de temps les classements passés doivent-ils rester disponibles — quelqu'un peut-il tirer le classement quotidien de mars dernier, ou seulement les fenêtres actuelles ?
  • Les écoutes de bots et frauduleuses sont-elles dans le périmètre, ou filtrées en amont avant de nous parvenir ?
  • Quel fuseau horaire définit « un jour » pour le classement quotidien — UTC, ou l'heure locale de l'auditeur ?
  • Quelle est la durée de « depuis toujours » — se réinitialise-t-il jamais ?
  • Si un label conteste une position au classement, avons-nous besoin d'une piste d'audit du classement jusqu'aux écoutes brutes ?
02

Les chiffres qui forcent les décisions d’architecture

Traitez chaque estimation comme une pression qui justifie un composant : cache, file, partition, réplica, pool de workers ou chemin de repli.

01

Débit d'ingestion d'événements

Des dizaines de milliards d'écoutes par jour (un plafond de charge délibéré de 70B/day — un ordre au-delà des volumes de streaming actuels)70B ÷ 86,400 s ≈ 810K events/s

Seul un log partitionné peut absorber ça. L'agrégateur consomme ensuite chaque partition en parallèle.

02

Le gain de la pré-agrégation

L'agrégateur regroupe les comptes par chanson par minute avant d'écrireune chanson jouée 10,000×/min → 1 écriture au lieu de 10,000

La pré-agrégation dans le flux réduit les écritures en base de 3-4 ordres de grandeur pour les chansons chaudes.

03

Mémoire du comptage exact

On suppose ~100M chansons distinctes × compteur + clé ≈ 50 B100M × 50 B ≈ 5 GB per window

Le comptage exact est abordable ici. C'est pourquoi c'est le chemin principal, pas le sketch.

04

L'alternative du sketch

Count-Min Sketch, 4 lignes de hash × 2M buckets × 4 B≈ 32 MB per window vs 5 GB exact

Quand la mémoire par fenêtre compte (beaucoup de fenêtres, beaucoup de régions), le sketch utilise environ 150× moins d'état. Le coût est un surcompte borné.

05

Coût de rafraîchissement du classement

Top-1,000 maintenu avec un min-heap sur les mises à jour de comptesmise à jour du heap O(log 1,000) ≈ 10 comparaisons par chanson-minute comptée

Maintenir le classement en continu est bon marché. Le recalculer de zéro à chaque requête ne le serait pas.

Exemple de décision

Les chiffres

Huit cent mille écoutes par seconde arrivent. La question produit est minuscule : le top 1 000 des morceaux pour quatre fenêtres fixes, frais à la minute près.

Mon choix

Poser chaque écoute dans un log d'événements partitionné. Dans le processeur de stream, compter les écoutes exactes par morceau en lots d'une minute, puis rouler les minutes en heures et les heures en jours — chaque fenêtre garde ses propres comptes. Un petit heap par fenêtre garde le top 1 000 à jour en continu. L'API sert un snapshot en cache qui se rafraîchit chaque minute et se préchauffe aux frontières de fenêtre.

À éviter

Ce que je NE ferais PAS : compter les écoutes avec des incréments en base de données. À 810K incréments par seconde, c'est une tempête d'écritures, et le stream pré-agrégeant l'absorbe gratuitement. Je ne me tournerais pas non plus vers un Count-Min Sketch par défaut. Les comptes exacts coûtent environ 5 Go par fenêtre ici, ce qui est abordable, et des chiffres exacts laissent la porte ouverte à des usages de qualité royalties. Le sketch est le bon outil quand les fenêtres se multiplient en classements par région et en dizaines de fenêtres, où un surcompte borné est acceptable.

Changer si

Si le produit ajoute des classements par région et par genre (des centaines de fenêtres), je séparerais par popularité. Garder les morceaux chauds sur des compteurs exacts et mettre la longue traîne dans un Count-Min Sketch — les morceaux chauds restent exacts, et la traîne ne coûte presque rien.

03

Chemin d’architecture

D’abord une image complète, puis chaque chemin comme son propre schéma — le chemin d’écriture et le chemin de lecture portent un trafic différent et justifient des composants différents.

Image complète

Vue d’ensemble — chaque composant

Client AppsPlay Event Log(Kafka)WindowedAggregatorWindow CountsChart API +CacheTop-K Heap (perwindow)maintenir le classementBatch Reconcilerrecomptage nocturne
  • Les écoutes coulent de gauche à droite ; le heap par fenêtre garde la réponse des 1 000 meilleurs chaude en continu.
  • En pointillés = hors du chemin temps réel : un recomptage batch nocturne sur le journal d'événements corrige toute dérive.
  • Les retraits se font au moment du service : la Chart API filtre les chansons délistées via une denylist, si bien que le retrait est immédiat et n'attend jamais que les comptes soient recalculés.

Chemin 1

Chemin d'écriture — compter, puis classer

Player ClientEvent Log(partitioned)Aggregator(1-min batches)Window CountsTop-K Heap

La pré-agrégation dans le flux transforme 10 000 écoutes d'une chanson à succès en une seule écriture comptée ; le heap se met à jour à mesure que les comptes changent.

Chemin 2

Chemin de lecture — servir l'instantané

ClientChart APICache (perwindow)ChartSnapshot

Les lectures ne voient jamais les comptes bruts. Les instantanés se rafraîchissent chaque minute et se préchauffent aux frontières de fenêtre, si bien que la requête de début d'heure est aussi rapide que n'importe quelle autre.

04

API et modèle de données

Avant d’optimiser, rendez le contrat inspectable : endpoints, entités, propriété, retries et état.

GET/charts/top?window={hour|day|month|all}&k=100

rés200 [{ song_id, plays }] (≤1,000 entries)

Sert le dernier ChartSnapshot depuis le cache — cache key charts:{window}:{window_start}, préchauffé au basculement de fenêtre pour que la minute de frontière ne soit jamais lente.

STREAMplay-events topic (partitioned by song_id + hot-key salt)

résconsumed by the windowed aggregator

Le seul chemin d'écriture. Les producteurs font du fire-and-forget ; la durabilité vient du journal, pas du producteur.

Entités principales

PlayEvent

song_id · user_id · played_at

Enregistrement de flux en append-only ; le journal (avec rétention) est la source de vérité pour le replay.

WindowCount

song_id · window (hour/day/month/all) · window_start · plays

Comptes exacts par fenêtre, agrégés à des grains plus grossiers à mesure que les fenêtres se cumulent (heures → jours → mois).

ChartSnapshot

window · window_start · top_k: [song_id, plays][]

La réponse précalculée que sert l'API ; rafraîchie chaque minute, mise en cache avec un TTL.

05

Directions de deep dive

Choisissez une voie pour le dernier tiers de l’entretien. Chaque voie vous donne le sujet, la question de l’examinateur à laquelle répondre, et le mode d’échec à éviter.

Focus

Exact ou approximatif

Question

Quand un Count-Min Sketch est-il le bon choix ici, et à quoi renoncez-vous exactement — en chiffres ?

Réponse

Restez sur des comptes exacts. Ici, ça fait environ 5 Go par fenêtre, 100 M de chansons à ~50 octets, et c'est abordable. Ne sortez un Count-Min Sketch que lorsque les fenêtres se multiplient en des centaines de classements par région et par genre. Il réduit l'état 150x à 32 Mo, mais chaque compte peut être surestimé, jamais sous-estimé. Bien pour un classement, faux pour des royalties.

À éviter

Saisir le sketch par réflexe — à 5 GB par fenêtre, le comptage exact est abordable et strictement plus utile.

Focus

Une chanson dévore le flux

Question

Un single à succès prend 30 % de toutes les écoutes. Qu'advient-il de sa partition, et comment l'étalez-vous sans corrompre les comptes ?

Réponse

Salez la clé de partition. Ajoutez un petit suffixe aléatoire à la clé de la chanson à succès pour que ses écoutes se répartissent sur plusieurs partitions au lieu d'en surcharger une. Puis fusionnez ces comptes partiels en un seul total par chanson avant le classement. Sautez la fusion et le tube se scinde en plusieurs entrées, chacune avec une fraction de ses vraies écoutes.

À éviter

Saler la clé de partition mais oublier de refusionner les comptes salés dans un total unique par chanson.

Focus

La frontière de fenêtre

Question

Il est 00:00:01 et tout le monde demande le nouveau classement horaire. D'où vient cette réponse ?

Réponse

Depuis un snapshot déjà préchauffé. Avant la frontière, l'agrégateur finalise le top 1 000 de la fenêtre qui se ferme et l'écrit en cache. Donc la première requête après le basculement est un simple hit de cache, comme n'importe quelle autre. Calculez le classement sur cette première requête à la place et vous faites de la seconde la plus chargée de l'heure la plus lente.

À éviter

Calculer le classement à la première requête après le basculement — préchauffez l'instantané à la place.

Focus

L'agrégateur meurt

Question

Le stream processor plante dix minutes après le début d'une fenêtre horaire. Que montrent les classements, et comment les comptes se rétablissent-ils ?

Réponse

Les classements continuent de servir le dernier snapshot en cache. Ils se périment une minute ou deux, mais ne deviennent jamais vides. Un processeur de remplacement charge son dernier checkpoint et rejoue le journal d'événements depuis cet offset, en recomptant seulement la fin de la fenêtre courante. Rien n'est perdu, parce que chaque écoute atterrit dans le journal avant d'être jamais comptée.

À éviter

Compter dans la mémoire du processeur sans checkpoints — le replay depuis le journal est toute l'histoire de la sûreté.

Focus

Pourquoi pas une base time-series

Question

Les moteurs time-series à la Prometheus stockent des comptes dans le temps — pourquoi conviennent-ils mal ici ?

Réponse

Deux raisons, et la cardinalité est la première. 100 M d'ID de chansons comme valeurs de tag font exploser l'index et la mémoire bien avant que le débit d'écriture ne fasse mal. La forme de la requête est mauvaise aussi. Ces moteurs répondent bien à « une série dans le temps » et mal à « top 1 000 sur toutes les séries » — ce qui est justement la question à laquelle ce système existe pour répondre.

À éviter

Ignorer la cardinalité : 100M song IDs comme valeurs de tag, c'est exactement ce sur quoi les moteurs time-series s'étranglent.

Prêt à vous entraîner ?

Expliquez Top K Songs (Spotify) à voix haute et obtenez une évaluation IA de votre explication.

S’entraîner avec l’IA →