Je suis Kyle McCormick, étudiant en deuxième année au Worcester Polytechnic Institute, et je viens de terminer un stage d'été de 11 semaines dans l'équipe mobile d'edX. Pendant mon temps dans l'équipe, je me suis concentré principalement sur le travail de backend lié à la performance. Dans cet article, je vais parler d'un des deux projets sur lesquels j'ai passé le plus de temps.
Mise en cache des métadonnées de cours
L'un des premiers problèmes que j'ai abordés a été de comprendre pourquoi les appels à notre équipe API d'inscription aux cours utilisateur endpoint, qui est utilisé pour afficher l'écran principal de notre application mobile, prenait plus d'une seconde pour répondre. Le point de terminaison renvoie simplement une liste de cours auxquels un utilisateur est inscrit, ainsi que certaines métadonnées de cours de base (nom, université, date de début, etc.), il était donc surprenant que cela prenne plus de quelques centaines de millisecondes. J'ai commencé mon enquête en parcourant le code qui a calculé la liste d'inscription, en utilisant pyinstrument faire du profilage.
L'une des premières choses que j'ai remarquées était que pour chaque inscription à un cours, les métadonnées du cours étaient chargées à partir de MongoDB à trois reprises (deux fois dans les appels à inscription.cours et une fois pendant la sérialisation) et il n'était pas mis en cache entre les appels au point de terminaison. Je soupçonnais que c'était la cause des problèmes de performances du terminal, ce que j'ai confirmé à l'aide de l'outil "X-Ray Tracing" de NewRelic.

Comme vous pouvez le voir sur la trace ci-dessus, la fonction modulestore/mixed.py.get_course, qui charge les didacticiels depuis MongoDB, consommait 90 % du temps de réponse. Cela impliquait qu'il y avait deux manières d'optimiser le point final : get_course plus rapidement, ou de minimiser le nombre d'appels en mettant en cache les données renvoyées par get_course. Bien que le premier soit certainement quelque chose qui doit être fait, c'est une tâche qui nécessite des modifications majeures du code de la plate-forme principale. Étant donné qu'une solution de mise en cache est plus simple à mettre en œuvre - c'est juste une couche sur le get_course appel – j'ai opté pour cette dernière solution. De plus, comme la mise en cache fonctionne entre les requêtes, il s'agit d'une amélioration qui présente des avantages même si get_course est optimisé.
Pour mettre en cache les métadonnées d'un cours, j'ai créé un modèle Django appelé Présentation du cours. La première fois que les métadonnées d'un cours sont demandées, get_course est appelée, une instance de Présentation du cours est créé à partir de sa valeur de retour et l'instance est enregistrée dans MySQL. La prochaine requête pour les métadonnées du cours chargera simplement le Présentation du cours instance, qui ne nécessite qu'une seule requête MySQL (par opposition aux 1 à 4 requêtes MongoDB qui sont exécutées pour chaque appel à get_course). Lorsqu'un cours est mis à jour dans edX Studio, les Présentation du cours l'instance est effacée, ce qui force la requête suivante à récupérer les métadonnées mises à jour en appelant get_course nouveau.
J'ai mis à jour le point de terminaison de l'API Mobile User Course Enrollment pour utiliser ce système de mise en cache (RP #8484), et effectué des tests de charge à l'aide de Locust.io pour confirmer qu'il y avait une amélioration des performances. En examinant les données de production sur NewRelic au moment de la publication du code mis à jour, l'impact sur les performances est remarquablement apparent.

Le graphique ci-dessus montre le temps de réponse moyen des API mobiles Liste des inscriptions aux cours utilisateur point final en millisecondes. Le point de terminaison a été mis à jour pour utiliser la mise en cache des métadonnées le 1er juillet. Le temps de réponse moyen est passé de ~1100 ms à ~110 ms.
Applications supplémentaires de la mise en cache
Parce qu'il nécessite un ensemble similaire de métadonnées, j'ai également mis à jour le tableau de bord des étudiants Web pour utiliser le système de mise en cache des métadonnées (RP #8642), ce qui a entraîné une réduction moins spectaculaire mais toujours significative du temps de réponse.

Le graphique ci-dessus montre l'untemps de réponse moyen pour le tableau de bord étudiant edX basé sur le Web en millisecondes. Le tableau de bord a été mis à jour pour utiliser la mise en cache des métadonnées le 17 juillet. Le temps de réponse moyen est passé de 600 à 800 ms à environ 250 ms.
En plus de ces deux cas d'utilisation, le système de mise en cache peut être utilisé dans n'importe quel scénario où des métadonnées de cours de base, indépendantes de l'utilisateur, sont requises. Par exemple, un autre employé d'edX a récemment mis à jour notre API d'inscription générale pour utiliser Présentation du courss au lieu d'appeler get_course (RP #8927), entraînant une diminution de 10 fois du temps de réponse au 95e centile pour le InscriptionListView point final.
Conclusion
Comme mon collègue stagiaire Ben l'a mentionné dans son article de blog, le programme de stages edX brille vraiment par la façon dont il intègre ses stagiaires dans de véritables équipes d'ingénierie logicielle, les traitant comme des employés à temps plein et leur donnant des projets percutants sur lesquels travailler. N'ayant jamais travaillé sur un projet de cette envergure et avec un tel nombre de contributeurs, j'ai énormément appris. Bien que mon mandat ici ait pris fin, j'espère rester impliqué dans la formidable communauté Open edX. Enfin, je tiens à remercier Nimisha, Chris, Kishore, Adam, Dave et le reste de l'équipe edX pour une expérience de stage incomparable !
![]()