Réponse A: Anthropic Claude Opus 5
SERVICE DE NOTIFICATION EN TEMPS RÉEL — PLAN DE CONCEPTION DU SYSTÈME
- RÉCAPITULATIF DES EXIGENCES ET ESTIMATION DE LA CAPACITÉ
Mathématiques du trafic :
- 100M d'utilisateurs actifs quotidiens x 10 événements/jour = 1 milliard d'événements déclenchant des notifications par jour.
- Moyenne = 1 milliard / 86 400 ≈ 11 600 événements/sec. Pic à 5x ≈ 58 000 événements/sec.
- Multiplicateur de diffusion (fan-out) : la plupart des événements (like, commentaire, suivi, DM) ciblent exactement un destinataire, donc le fan-out est d'environ 1:1. Cependant, les événements provenant de comptes à forte audience (une céléb...
Afficher la réponse complète ▼
SERVICE DE NOTIFICATION EN TEMPS RÉEL — PLAN DE CONCEPTION DU SYSTÈME
- RÉCAPITULATIF DES EXIGENCES ET ESTIMATION DE LA CAPACITÉ
Mathématiques du trafic :
- 100M d'utilisateurs actifs quotidiens x 10 événements/jour = 1 milliard d'événements déclenchant des notifications par jour.
- Moyenne = 1 milliard / 86 400 ≈ 11 600 événements/sec. Pic à 5x ≈ 58 000 événements/sec.
- Multiplicateur de diffusion (fan-out) : la plupart des événements (like, commentaire, suivi, DM) ciblent exactement un destinataire, donc le fan-out est d'environ 1:1. Cependant, les événements provenant de comptes à forte audience (une célébrité publie) peuvent se diffuser à des millions de destinataires ; traitez-les comme un chemin distinct de « diffusion en bloc ». Supposons une livraison de pointe effective de 80 000 à 100 000 notifications/sec, y compris les campagnes de masse.
- Stockage : historique des 100 dernières notifications par utilisateur. Supposons environ 400 octets par ligne -> 100 x 400 octets = 40 Ko/utilisateur -> 100 millions d'utilisateurs ≈ 4 To d'historique « chaud », plus un facteur de réplication de 3 ≈ 12 To. Facilement partageable.
- Connexions : supposons que 20 à 25 % des utilisateurs actifs quotidiens soient connectés simultanément au pic ≈ 20 à 25 millions de connexions WebSocket actives. Avec environ 100 000 connexions par nœud passerelle (Linux optimisé, epoll), cela représente 200 à 250 nœuds plus une marge.
Cibles non fonctionnelles :
- Latence p99 de bout en bout inférieure à 2 secondes pour les utilisateurs en ligne (mesurée de l'ingestion de l'événement à l'accusé de réception du client).
- Disponibilité de 99,9 % = environ 43 minutes d'indisponibilité par mois.
- Livraison au moins une fois avec déduplication côté client/serveur idempotente.
- ARCHITECTURE DE HAUT NIVEAU
La conception est un pipeline piloté par les événements avec une séparation claire entre le chemin d'ingestion (rapide, durable, optimisé pour l'écriture) et le chemin de livraison (spécifique au canal, avec tentatives).
Flux :
Services producteurs (Service de publication, Service de graphe social, Service de commentaires, Service de messagerie)
-> API de notification / Passerelle (gRPC + REST, authentification, limitation de débit, validation de schéma, clé d'idempotence)
-> Sujet Kafka notification.events (partitionné par recipient_user_id si connu, sinon par actor_id)
-> Service de diffusion (résout les destinataires, développe les événements de célébrités, applique les règles d'agrégation)
-> Sujet Kafka notification.deliveries (un message par destinataire)
-> Service de préférences et de politique (recherche en ligne via cache : opt-ins de canal, heures de silence, mise en sourdine/blocage, résumé vs instantané)
-> Routeur / Répartiteur écrit dans des sujets par canal :
deliver.websocket, deliver.mobile_push, deliver.web_push, deliver.email
-> Travailleurs de canal :
Répartiteur WebSocket -> Recherche dans le registre de connexion -> Nœud de passerelle en temps réel -> client
Travailleur Push Mobile -> APNs (iOS) / FCM (Android)
Travailleur Push Web -> Protocole VAPID/Web Push via les services de push des navigateurs
Travailleur Email -> SES / SendGrid, avec rendu de modèles et regroupement de résumés
-> En parallèle, un consommateur d'écriture d'historique persiste chaque livraison dans le magasin d'historique des notifications et incrémente les compteurs de non lus.
Transversal : Service de modèles, Registre d'appareils, Registre de connexion, Magasin de déduplication, Files d'attente de lettres mortes, Pile d'observabilité, et un plan d'administration/analyse consommant les mêmes flux Kafka.
- COMPOSANTS CLÉS ET RESPONSABILITÉS
Passerelle API de notification
- Point d'entrée unique pour les producteurs internes (gRPC, schémas protobuf dans un registre) et pour les API de lecture client (REST/GraphQL pour l'historique, marquer comme lu, préférences).
- Responsabilités : authentification (service à service mTLS, JWT pour les clients), limitation de débit/quotas par locataire et par producteur, validation des requêtes, acceptation de la clé d'idempotence, et ajout immédiat et durable à Kafka. Elle renvoie rapidement 202 Accepted ; elle n'effectue aucun travail de livraison en ligne.
Ingestion d'événements / Kafka
- Journal durable et rejouable.
notification.eventsavec environ 256 partitions, facteur de réplication 3, min.insync.replicas=2, acks=all, rétention 7 jours pour la relecture et la récupération d'incidents. - Fournit une contre-pression et un tampon naturels pour le pic 5x, découplant les producteurs des fournisseurs tiers lents.
Service de diffusion (Fan-out Service)
- Résout un événement en une liste de destinataires. Pour les événements 1:1, c'est un passage direct. Pour les événements 1:N (nouveau post aux abonnés, activité de fil de discussion de groupe), il interroge le service de graphe social et émet des messages de destinataires par lots.
- Stratégie de diffusion hybride : basée sur le push (écriture par destinataire) pour les comptes normaux ; basée sur le pull/paresseuse pour les comptes au-dessus d'un seuil d'abonnés (par exemple, 1 million et plus), où un seul « marqueur de flux » est écrit et les destinataires sont matérialisés à la lecture. Cela évite qu'un événement de célébrité ne crée une tempête d'écriture de 50 millions de messages sur le chemin critique.
- Limite le débit des expansions en bloc sur un sujet Kafka distinct de faible priorité afin qu'elles ne privent jamais les notifications interactives.
- Applique l'agrégation/coalescence : « Alice et 24 autres personnes ont aimé votre publication » au lieu de 25 lignes, en utilisant une fenêtre glissante courte (par exemple, 30 à 60 secondes) indexée par (destinataire, post_id, type) dans Redis.
Service de préférences et de politique
- Stocke et sert les préférences de canal par utilisateur, les opt-ins par catégorie, les heures de silence avec fuseau horaire, les paramètres par appareil, les listes de mise en sourdine/blocage, et les indicateurs de consentement légal (RGPD/CAN-SPAM).
- Le chemin de lecture est mis en cache dans Redis avec invalidation en écriture directe ; cible de recherche p99 inférieure à 5 ms. Source de vérité dans un magasin relationnel.
- Applique le plafonnement de fréquence (par exemple, max N push/heure par utilisateur) pour protéger l'expérience utilisateur et les quotas des fournisseurs.
Routeur / Répartiteur
- Mappe une livraison approuvée en éléments de travail spécifiques au canal. Décide « dans l'application + WebSocket uniquement si en ligne ; sinon push mobile » en utilisant le registre de connexion, et planifie des résumés par e-mail au lieu d'envois instantanés lorsque les préférences l'indiquent.
Passerelle en temps réel (couche WebSocket)
- Niveau d'état détenant des connexions WebSocket persistantes (solution de repli : Server-Sent Events, puis interrogation longue).
- À la connexion : authentifier, enregistrer (user_id, device_id) -> gateway_node_id dans le registre de connexion (Redis Cluster, TTL + rafraîchissement par battement de cœur), puis pousser tout le backlog non lu.
- À la livraison : le répartiteur recherche le nœud, envoie via un flux gRPC interne ou un canal Kafka/Redis Pub/Sub par nœud ; la passerelle écrit sur le socket et attend un accusé de réception du client.
- Battements de cœur toutes les 30 secondes ; les battements de cœur manqués suppriment l'entrée du registre et les notifications futures reviennent automatiquement au push mobile.
Travailleurs de canal
- Travailleur Push Mobile : regroupe vers APNs (HTTP/2 multiplexé, authentification par jeton) et FCM. Gère les limites de concurrence par fournisseur, le backoff exponentiel avec gigue, et supprime les jetons d'appareil invalides/non enregistrés du registre d'appareils.
- Travailleur Push Web : protocole Web Push avec clés VAPID et chiffrement de charge utile.
- Travailleur Email : rend les modèles, prend en charge les résumés instantanés, horaires ou quotidiens ; gère les rebonds/plaintes via les webhooks du fournisseur et maintient une liste de suppression.
- Tous les travailleurs sont des consommateurs idempotents indexés par notification_id et écrivent le statut terminal dans un sujet
delivery.status.
Écrivain d'historique et Chemin de lecture
- Consommateur qui persiste les notifications et maintient les compteurs de non lus dans Redis (compteur faisant autorité, réconcilié périodiquement à partir du magasin).
- L'API de lecture sert les 100 dernières notifications du magasin d'historique, avec un cache Redis de la première page par utilisateur pour des lectures typiques inférieures à 10 ms.
Services de support
- Service de modèles : modèles versionnés et localisés avec interpolation de variables ; découple les changements de copie des déploiements de code.
- Registre d'appareils : jeton d'appareil, plateforme, version de l'application, locale, fuseau horaire, dernière connexion.
- Magasin de déduplication : Redis avec un TTL de 24h sur (producer_id, idempotency_key) pour que « au moins une fois » se comporte comme « effectivement une fois ».
- Planificateur : pour les notifications retardées/planifiées et les fenêtres de résumé (files d'attente par compartiment temporel dans des ensembles triés Redis ou un planificateur dédié comme une échelle de sujets de délai Kafka).
- DLQ + Relecteur : messages poison garés et rejouables après corrections.
- MODÈLE DE DONNÉES ET CHOIX DE BASES DE DONNÉES
a) Historique des notifications — Cassandra (ou ScyllaDB / DynamoDB)
Table : notifications_by_user
- Clé de partition : user_id
- Clé de clustering : created_at DESC, notification_id
- Colonnes : type, actor_id(s), target_type, target_id, aggregated_count, preview_text, image_url, deep_link, read_at, channels_sent, created_at
- TTL : 90 jours ; l'application limite les lectures à 100 lignes.
Justification : le modèle d'accès est une clé de partition unique et bien connue avec une tranche ordonnée par le temps — exactement le point fort de Cassandra. Elle offre une scalabilité d'écriture linéaire (nous avons besoin de 60 à 100 000 écritures/sec soutenues), une réplication sans maître multi-régions pour la disponibilité, une cohérence réglable (écritures QUORUM/LOCAL_QUORUM, lecture LOCAL_ONE pour le flux), et un TTL natif pour la rétention. Nous n'avons pas besoin de jointures ou de transactions multi-lignes ici, donc un magasin relationnel n'ajouterait que des difficultés de partitionnement et un risque d'amplification d'écriture.
b) Compteurs de non lus et première page « chaude » — Redis Cluster
- Compteur entier
unread:{user_id}; liste JSON mise en cachenotif:page0:{user_id}.
Justification : les compteurs sont lus à chaque ouverture d'application (QPS extrêmement élevé, faible valeur par lecture) et doivent être à la milliseconde près. Redis absorbe cela à moindre coût ; les compteurs Cassandra sont comparativement coûteux et sujets aux erreurs. Les compteurs sont réconciliés de manière asynchrone, donc la dérive se corrige d'elle-même.
c) Préférences utilisateur et jetons d'appareil — PostgreSQL (partitionné par user_id) avec cache en lecture directe Redis
Tables : user_preferences(user_id, category, channel, enabled, quiet_hours_start, quiet_hours_end, timezone, updated_at), devices(device_id, user_id, platform, push_token, locale, last_seen, active), suppression_list(email, reason, created_at).
Justification : ces données sont de faible volume, fortement lues, relationnelles (utilisateurs -> appareils -> paramètres par catégorie), et bénéficient des transactions et des contraintes pour la correction et l'auditabilité (les enregistrements de consentement ont un poids de conformité). Le volume est suffisamment faible (environ 100 millions de lignes) pour être facilement partitionné et mis en cache presque entièrement.
d) Registre de connexion — Redis Cluster
conn:{user_id}-> ensemble de {device_id, gateway_node_id, connected_at}, TTL 90s rafraîchi par battement de cœur.
Justification : éphémère, rotation extrêmement élevée, doit être rapide ; la durabilité n'est pas nécessaire car une entrée perdue dégrade gracieusement vers le fallback push.
e) Déduplication / idempotence — Redis avec TTL, sauvegardé par rien (la perte ne risque qu'un doublon rare).
f) Modèles et configuration — PostgreSQL + stockage d'objets pour les actifs, mis en cache en périphérie.
g) Analyse — événements diffusés de Kafka vers un lac de données (S3/Parquet) et un entrepôt (Snowflake/BigQuery) pour le reporting des taux de livraison, des taux d'ouverture et de latence ; ClickHouse pour les tableaux de bord opérationnels quasi en temps réel.
- RECOMMANDATIONS DE LA PILE TECHNOLOGIQUE
- Langage/runtime : Go pour la passerelle, la passerelle en temps réel et les travailleurs (les goroutines et la faible mémoire par connexion conviennent à des millions de sockets) ; Java/Kotlin acceptable pour l'agrégation intensive par Kafka Streams.
- Messagerie : Apache Kafka (géré : MSK/Confluent) comme colonne vertébrale ; sujets distincts par canal et par classe de priorité. Kafka Streams ou Flink pour l'agrégation/coalescence fenêtrée.
- Transport en temps réel : WebSocket sur TLS avec SSE et fallbacks d'interrogation longue ; équilibrage de charge NLB/L4 avec vidage de connexion ; le routage collant n'est pas requis car le registre stocke l'identité du nœud.
- Mise en cache : Redis Cluster (ou Elasticache/MemoryDB) pour les compteurs, les préférences, le registre, la déduplication, les limites de débit.
- Magasins de données : Cassandra/ScyllaDB pour l'historique ; PostgreSQL (Aurora) pour les préférences/appareils ; S3 + entrepôt pour l'analyse.
- Fournisseurs de push : APNs, FCM, Web Push (VAPID) ; e-mail via SES avec SendGrid comme fournisseur secondaire derrière une couche d'abstraction de fournisseur pour le basculement.
- Infrastructure : Kubernetes avec HPA/KEDA évoluant sur le décalage du consommateur Kafka (pas seulement le CPU), maillage de services Envoy/Istio pour mTLS et les tentatives, Terraform pour l'IaC.
- Observabilité : traçage OpenTelemetry (ID de trace propagé de l'événement producteur à l'accusé de réception client), métriques Prometheus + Grafana, journaux structurés dans Loki/ELK, alertes PagerDuty sur les SLO.
- Bibliothèques de résilience : disjoncteurs et « bulkheads » par fournisseur externe, limiteurs de débit à jetons, backoff exponentiel avec gigue.
- STRATÉGIES DE SCALABILITÉ, LATENCE ET DISPONIBILITÉ
Scalabilité
- Tous les composants sans état (API, diffusion, routeur, travailleurs) s'adaptent horizontalement ; le nombre de partitions Kafka est le plafond de parallélisme, donc provisionnez les partitions pour 5 à 10 fois le pic actuel dès le premier jour (la repartition est opérationnellement pénible).
- Mise à l'échelle automatique sur le décalage du consommateur avec KEDA afin qu'un pic 5x déclenche une mise à l'échelle en quelques secondes ; conservez un tampon chaud de 30 à 40 % de marge car la mise à l'échelle n'est pas instantanée.
- Partitionnement par user_id de manière cohérente entre Cassandra, Postgres et Redis afin que les données d'un seul utilisateur soient co-localisées et que l'atténuation des partitions chaudes soit uniforme.
- Voies prioritaires : les notifications interactives (DM, commentaire sur votre publication) utilisent un sujet de haute priorité avec des groupes de consommateurs dédiés ; la diffusion en bloc/marketing/célébrité utilise une voie à faible priorité limitée. Cela garantit le SLO de 2 secondes pour les notifications que les utilisateurs remarquent réellement.
- Gestion des célébrités/clés chaudes via la diffusion hybride push/pull décrite ci-dessus, plus des clés de partition salées pour les cibles extrêmement chaudes.
Faible latence (p99 inférieure à 2 secondes)
- Le chemin critique est intentionnellement court : ajout API -> Kafka -> diffusion -> succès du cache de préférences -> recherche dans le registre -> écriture WebSocket. Toutes les recherches sont dans Redis (moins de 5 ms) ; aucune écriture de base de données synchrone ne bloque la livraison.
- La persistance de l'historique et l'analyse sont asynchrones, hors du chemin de livraison.
- Producteurs Kafka optimisés avec linger.ms=5 et compression (lz4) pour équilibrer le regroupement et la latence ; les consommateurs utilisent la validation manuelle après traitement.
- Déploiement multi-régions avec les utilisateurs épinglés à la région la plus proche pour réduire le temps de trajet aller-retour ; les connexions WebSocket se terminent aux bords régionaux.
- Les fenêtres d'agrégation sont configurables par type et désactivées pour les types critiques en termes de latence comme les DM.
- Des sondes synthétiques continues mesurent la latence réelle de bout en bout par région et par canal.
Haute disponibilité (99,9 % et plus)
- Aucun point de défaillance unique : multi-AZ pour chaque niveau, Kafka RF=3 avec min.insync.replicas=2, écritures Cassandra RF=3 avec LOCAL_QUORUM, Postgres avec réplique de secours synchrone et basculement automatisé.
- Multi-régions actif-actif pour les niveaux temps réel et livraison ; Cassandra réplique de manière asynchrone entre les régions, Postgres utilise des répliques de lecture régionales avec une région d'écriture désignée.
- Dégradation gracieuse : si le chemin WebSocket est défaillant, retour au push mobile ; si le cache de préférences est en panne, retour à Postgres puis à des valeurs par défaut conservatrices ; si les écritures Cassandra échouent, continuer la livraison en temps réel et rejouer les écritures d'historique depuis Kafka par la suite.
- Tentatives avec backoff exponentiel plus gigue, tentatives plafonnées, puis DLQ avec alertes et un outil de relecture.
- Disjoncteurs par fournisseur externe afin qu'une panne APNs ne puisse pas épuiser les threads de travail et bloquer l'e-mail.
- Livraison au moins une fois plus déduplication notification_id côté serveur et client ; les clients dédupliquent également à la reconnexion lors de la relecture du backlog non lu.
- Pratiques de fiabilité : exercices de chaos/game day (tuer un nœud de passerelle et vérifier le fallback push), tests de charge à 5x le pic, déploiements blue-green et canary, drapeaux de fonctionnalités pour les interrupteurs par canal, rejet de la pression de la circulation de faible priorité avant la circulation de haute priorité.
- Goulots d'étranglement et compromis
Goulots d'étranglement probables
- Fournisseurs tiers (APNs/FCM/e-mail) : le plafond le plus difficile, car le débit ne dépend pas de nous. Atténuer avec la mise en pool de connexions sur HTTP/2, le regroupement, les limiteurs de débit par fournisseur, le basculement multi-fournisseurs pour l'e-mail et le lissage basé sur des files d'attente des pics.
- Diffusion de célébrités : une seule publication peut générer des dizaines de millions de livraisons. Atténué par le push/pull hybride, les voies de diffusion en bloc limitées et l'agrégation.
- Niveau de connexion WebSocket : mémoire et descripteurs de fichiers par nœud, plus les reconnexions « thundering herd » après un déploiement ou un problème réseau. Atténuer avec des limites de noyau optimisées, une reconnexion échelonnée avec gigue et backoff exponentiel appliqué côté client, et un vidage lent des connexions pendant les déploiements.
- Clés chaudes Redis : clé d'agrégation d'une publication virale ou d'un compteur partagé. Atténuer avec le salage de clés, les caches locaux en mémoire avec des TTL courts et le partitionnement côté client.
- Amplification d'écriture Cassandra et pression de compaction à 100 000 écritures/sec. Atténuer avec la compaction par fenêtre temporelle (TWCS) adaptée aux données TTL de séries temporelles, et en ne stockant pas du tout les types de notifications de faible valeur dans l'historique.
- Désalignement des partitions Kafka si le partitionnement est basé sur une clé de faible cardinalité ; toujours partitionner par recipient_user_id sur le sujet de livraison.
Compromis explicites
- Au moins une fois plutôt qu'exactement une fois : exactement une fois entre des fournisseurs externes hétérogènes est impraticable et coûteux. Nous acceptons les doublons rares et les résolvons avec des clés d'idempotence et une déduplication côté client — moins cher et beaucoup plus disponible.
- Cohérence éventuelle pour les compteurs et l'historique plutôt que cohérence forte : un badge non lu momentanément erroné est acceptable ; une latence de plusieurs secondes pour le garantir ne l'est pas. Les travaux de réconciliation limitent la dérive.
- Persistance polyglotte plutôt qu'une seule base de données : cela augmente la surface opérationnelle (trois magasins de données plus Redis) mais chaque charge de travail obtient le bon moteur. Un seul cluster Postgres deviendrait le goulot d'étranglement à 1 milliard d'écritures/jour ; un seul cluster Cassandra rendrait les préférences et l'audit de consentement gênants.
- Diffusion basée sur le push pour la plupart des utilisateurs, basée sur le pull pour les célébrités : complexité de code accrue, mais c'est la seule façon d'éviter les tempêtes d'écriture illimitées.
- L'agrégation améliore l'expérience utilisateur et réduit considérablement le volume de livraison, mais ajoute jusqu'à une fenêtre de latence pour les types agrégables. Nous maintenons des fenêtres courtes et exemptons les DM.
- Le déploiement multi-régions actif-actif augmente le coût et introduit des subtilités de cohérence inter-régions, justifié par les cibles de disponibilité et de latence. Si le budget était limité, une posture mono-région avec secours à chaud répondrait toujours à 99,9 % mais avec un RTO plus long.
- Kafka plutôt qu'une file d'attente gérée plus simple (SQS) : plus de charge opérationnelle, mais nous avons besoin de relecture, de partitions ordonnées par clé, de plusieurs groupes de consommateurs indépendants et d'une rétention de 7 jours pour la récupération d'incidents.
- Stocker seulement les 100 dernières notifications avec un TTL de 90 jours échange la complétude de l'archivage contre le coût et la performance de lecture ; les données à long terme résident dans le lac d'analyse si jamais nécessaire.
- PLAN DE DÉPLOIEMENT (PHASÉ)
Phase 1 : API d'ingestion + Kafka + écrivain d'historique + API de lecture (notifications dans l'application uniquement). Établit la colonne vertébrale durable.
Phase 2 : Passerelle en temps réel, registre de connexion, compteurs de non lus, livraison WebSocket avec fallback push.
Phase 3 : Travailleurs push mobile et push web, registre d'appareils, service de préférences et heures de silence.
Phase 4 : E-mail instantané + résumés, service de modèles, gestion de la suppression.
Phase 5 : Agrégation/coalescence, diffusion hybride de célébrités, plafonnement de fréquence.
Phase 6 : Multi-régions actif-actif, tests de chaos, validation de charge 5x, tableaux de bord SLO et budgets d'erreur.
- INDICATEURS CLÉS ET SLO
- Latence p50/p95/p99 de bout en bout par canal (SLO : p99 inférieure à 2 secondes pour WebSocket).
- Taux de succès de livraison par canal ; taux d'erreurs des fournisseurs et taux d'invalidation de jetons.
- Décalage du consommateur Kafka par sujet (signal principal de mise à l'échelle automatique et de pagination).
- Nombre de connexions WebSocket, taux de rotation et taux d'accusé de réception.
- Dérive du compteur de non lus détectée par la réconciliation.
- Profondeur et âge de la DLQ.
- Disponibilité par API mesurée du point de vue du client, suivie par rapport à un budget d'erreur mensuel.
Résultat
Votes gagnants
3 / 3
Score moyen
Score total
Commentaire global
La réponse A est une conception exceptionnelle et très concrète avec des estimations de capacité quantifiées, des chemins d'ingestion et de livraison clairement séparés, des modèles de données détaillés, des files d'attente prioritaires, un fan-out hybride, une résilience spécifique au canal et des mesures de disponibilité et de latence exploitables. Ses points forts sont les paramètres opérationnels explicites, la planification de la capacité WebSocket, les chemins de dégradation en cas de défaillance et une analyse inhabituellement approfondie des goulots d'étranglement et des compromis. La principale faiblesse est qu'elle n'utilise pas explicitement un modèle de publication d'événements outbox ou transactionnel dans les services sources, laissant une lacune potentielle entre la transaction commerciale d'origine et l'ingestion durable dans Kafka. Certains détails de cohérence multi-régions sont également traités à un niveau élevé.
Afficher le détail de l’évaluation ▼
Qualité de l’architecture
Poids 30%Le pipeline piloté par les événements durable, les topics de fan-out et de canal séparés, le routage des politiques, le registre de connexions, la passerelle en temps réel, l'enregistreur d'historique asynchrone et les files d'attente prioritaires forment une architecture logique avec des responsabilités claires. La principale lacune est l'absence d'une outbox explicite dans le service source, de sorte qu'une transaction commerciale pourrait théoriquement être validée sans que son événement de notification n'atteigne l'API d'ingestion. La relation entre un enregistrement d'historique logique et plusieurs livraisons de canaux pourrait également être énoncée plus précisément.
Complétude
Poids 20%Il aborde tous les domaines demandés et va au-delà avec des estimations de capacité, la taille des connexions, des schémas détaillés, le comportement des canaux, des contrôles de conformité, l'observabilité, des phases de déploiement et des SLO mesurables. Les omissions mineures incluent la publication transactionnelle source et une explication plus exacte de la manière dont le magasin applique une politique stricte des 100 derniers éléments plutôt que de simplement limiter les lectures et d'appliquer un TTL.
Analyse des compromis
Poids 20%La réponse examine explicitement et correctement la livraison au moins une fois par rapport à exactement une fois, la cohérence éventuelle par rapport à la cohérence forte, la persistance polyglotte, le fan-out par poussée par rapport à par extraction, la latence d'agrégation, le coût multi-régions, Kafka par rapport à des files d'attente plus simples, et les limites de rétention. Les compromis sont liés aux exigences et accompagnés de stratégies d'atténuation plutôt que d'être listés abstraitement.
Scalabilité et fiabilité
Poids 20%La conception quantifie le trafic moyen et de pointe, estime la capacité des WebSockets simultanés, met à l'échelle les consommateurs en fonction du décalage Kafka, réserve une marge de réserve à chaud, isole le trafic prioritaire, gère le fan-out des célébrités et spécifie la réplication multi-AZ, les paramètres de quorum, les nouvelles tentatives, les DLQ, les disjoncteurs, les chemins de secours, les tests de charge et les exercices de chaos. Certains comportements de données actifs-actifs régionaux et l'atomicité des événements source nécessitent plus de détails.
Clarté
Poids 10%Malgré sa longueur, l'organisation numérotée, le flux explicite, les composants nommés, les schémas et les sections séparées pour la mise à l'échelle, la disponibilité, les goulots d'étranglement, le déploiement et les métriques rendent le plan facile à naviguer. Quelques affirmations sont trop confiantes ou compressées, comme qualifier le partitionnement de 100 millions de lignes de trivial, et certaines sémantiques de canal/historique pourraient être formulées plus soigneusement.
Score total
Commentaire global
Une réponse exceptionnelle qui incarne une conception de système de niveau senior. Elle est complète, bien structurée, étayée quantitativement et démontre une compréhension approfondie des compromis et des réalités opérationnelles. La conception est très détaillée, avec des choix technologiques spécifiques et des stratégies concrètes pour la scalabilité et la fiabilité. L'inclusion d'un plan de déploiement progressif et d'une section dédiée aux métriques/SLO l'élève au-delà d'une conception purement théorique, lui donnant l'impression d'un document prêt pour la production.
Afficher le détail de l’évaluation ▼
Qualité de l’architecture
Poids 30%L'architecture est exceptionnellement solide, détaillée et bien articulée. Le flux piloté par les événements est clair, et la séparation des préoccupations entre les composants tels que l'API, le service Fan-out et les workers spécifiques aux canaux est excellente. L'inclusion d'une stratégie de fan-out hybride push/pull pour les comptes de célébrités démontre une compréhension sophistiquée de l'espace problématique.
Complétude
Poids 20%Cette réponse est exceptionnellement complète. Elle aborde chaque partie de la requête en détail et va au-delà en incluant une estimation détaillée de la capacité, un plan de déploiement progressif et une section dédiée aux métriques clés et aux SLO. Ce niveau de détail est ce que l'on attendrait du document de conception d'un ingénieur senior.
Analyse des compromis
Poids 20%La discussion sur les goulots d'étranglement et les compromis est excellente. Elle identifie non seulement les problèmes potentiels, mais liste également explicitement les compromis de conception effectués, tels que le choix de la livraison au moins une fois plutôt qu'exactement une fois et l'utilisation de la persistance polyglotte. Le raisonnement est précis, concis et démontre une perspective d'ingénierie mature.
Scalabilité et fiabilité
Poids 20%Les stratégies de scalabilité et de fiabilité sont à la fois détaillées et concrètes. Elle mentionne des approches spécifiques telles que l'autoscaling sur le décalage des consommateurs Kafka avec KEDA, l'utilisation de files d'attente prioritaires pour différents types de trafic et la mise en œuvre d'une échelle de dégradation gracieuse. L'inclusion de pratiques telles que le chaos engineering montre une approche proactive de la fiabilité.
Clarté
Poids 10%La réponse est exceptionnellement claire et bien structurée. L'utilisation de sections numérotées, d'un diagramme de flux textuel au début et de points concis rend la grande quantité d'informations techniques très facile à suivre et à assimiler. Le flux logique des exigences aux métriques est impeccable.
Score total
Commentaire global
La réponse A est un document de conception de niveau quasi-staff. Elle commence par des calculs de capacité concrets (événements/sec, dimensionnement du stockage, nombre de connexions WebSocket et estimations de nœuds), puis détaille une séparation claire entre ingestion et diffusion avec des détails de configuration spécifiques (nombre de partitions Kafka, paramètres de réplication, acks, rétention), un système hybride de diffusion/extraction (push/pull) pour les comptes de célébrités, des files d'attente prioritaires pour garantir le SLO de 2 secondes, des modèles de données par magasin avec justifications explicites, une échelle de dégradation progressive, des pratiques de chaos/journées de jeu, un plan de déploiement progressif et des métriques SLO. La section sur les compromis est exceptionnelle : chaque choix (au moins une fois vs exactement une fois, persistance polyglotte, Kafka vs SQS, coût de latence d'agrégation, coût multi-régions) est énoncé avec l'alternative et la raison pour laquelle il a été rejeté. Faiblesses mineures : la densité peut rendre la lecture ardue et le document n'aborde pas le pattern outbox pour la durabilité des événements côté producteur.
Afficher le détail de l’évaluation ▼
Qualité de l’architecture
Poids 30%Architecture exceptionnelle : séparation claire ingestion/diffusion, système hybride push/pull avec un seuil concret de followers, files d'attente prioritaires garantissant le SLO de 2s pour les événements interactifs, niveau de connexion dimensionné (20-25M sockets, ~100K par nœud), et topologie Kafka spécifique (256 partitions, RF=3, min.insync.replicas=2, rétention de 7 jours). Les responsabilités des composants sont précises et le chemin critique est délibérément exempt d'écritures synchrones en base de données.
Complétude
Poids 20%Couvre toutes les exigences de la consigne plus des extras : estimation de capacité avec calculs de stockage, modèles de données complets pour six magasins avec justifications, les quatre canaux, les digests, la conformité (RGPD/CAN-SPAM, listes de suppression), l'observabilité avec les SLO, un plan de déploiement progressif et une section dédiée aux goulots d'étranglement/compromis. Rien de la consigne n'est manquant.
Analyse des compromis
Poids 20%Section de compromis exceptionnelle : chaque décision nomme l'alternative et explique pourquoi elle a été rejetée (au moins une fois vs exactement une fois, persistance polyglotte vs base de données unique, Kafka vs SQS, diffusion vs extraction pour le fan-out, coût actif-actif vs veille active, latence d'agrégation vs volume). Les compromis sont intégrés dans les justifications tout au long du document, pas seulement dans une section dédiée.
Scalabilité et fiabilité
Poids 20%Concret et quantifié : justification du sur-provisionnement des partitions, autoscaling KEDA sur le décalage consommateur avec une marge de 30-40% en réserve, clés 'hot' salées, compaction TWCS pour le débit d'écriture, paramètres explicites de réplication et de quorum pour Cassandra/Postgres/Kafka, échelle de dégradation progressive, disjoncteurs par fournisseur, tests de chaos et validation de charge 5x, et outillage de rejeu des DLQ. Lie directement les mécanismes aux objectifs de 100M DAU / 5x pic / 99.9%.
Clarté
Poids 10%Excellente structure numérotée, des exigences au déploiement et aux métriques ; le diagramme de flux sous forme de texte rend le pipeline facile à suivre. La densité des détails rend parfois certaines sections lourdes, mais les titres et une mise en forme cohérente la rendent navigable.