Я Кайл МакКормік, навчаюся на другому курсі Вустерського політехнічного інституту, і я щойно закінчив 11-тижневу літню практику в мобільній команді edX. Під час роботи в команді я зосереджувався в основному на роботі з бекендом, пов’язаною з продуктивністю. У цьому дописі я розповім про один із двох проектів, на які присвятив найбільше часу.
Кешування метаданих курсу
Одне з перших питань, яке я взяв за себе, — це з’ясувати, навіщо дзвонять нашій команді API реєстрації на курс користувача кінцевій точці, яка використовується для відображення головного екрана в нашому мобільному додатку, знадобилося більше секунди, щоб відповісти. Кінцева точка просто повертає список курсів, на які зареєстрований користувач, разом із деякими базовими метаданими курсу (назва, університет, дата початку тощо), тому було дивно, що це зайняло більше кількох сотень мілісекунд. Я почав своє дослідження, пройшовши код, який обчислив список реєстрації, використовуючи піінструмент зробити профілювання.
Однією з перших речей, які я помітив, було те, що для кожного запису на курс метадані для курсу завантажувалися з MongoDB тричі (двічі в дзвінках на зарахування.курс і один раз під час серіалізації), і він не кешувався між викликами кінцевої точки. У мене виникла підозра, що це була причина проблем із продуктивністю кінцевої точки, і я підтвердив це за допомогою інструменту «X-Ray Tracing» NewRelic.

Як ви можете бачити на зображенні вище, функція modulestore/mixed.py.get_course, який завантажує навчальні програми з MongoDB, споживав 90% часу відповіді. Це означає, що існує два способи оптимізації кінцевої точки: make get_course швидше, або мінімізуйте кількість звернень до нього, кешуючи дані, які повертає get_course. Хоча перше, безперечно, є тим, що потрібно зробити, це завдання вимагає серйозних змін у коді ядра платформи. Оскільки рішення для кешування простіше реалізувати – це лише шар поверх get_course дзвінок – я вирішив скористатися останнім рішенням. Крім того, оскільки кешування працює між запитами, це вдосконалення має переваги, навіть якщо get_course оптимізовано.
Щоб кешувати метадані курсу, я створив модель Django під назвою Огляд курсу. Коли вперше запитуються метадані для курсу, get_course називається екземпляром Огляд курсу створюється з його повернутого значення, а примірник зберігається в MySQL. Наступний запит метаданих курсу просто завантажить Огляд курсу екземпляр, для якого потрібен лише один запит MySQL (на відміну від 1-4 запитів MongoDB, які виконуються для кожного виклику get_course). Коли курс оновлюється в edX Studio, відповідний Огляд курсу екземпляр очищається, що змушує наступний запит отримати оновлені метадані шляхом виклику get_course знову.
Я оновив кінцеву точку API реєстрації на курс для мобільних пристроїв, щоб використовувати цю систему кешування (PR # 8484) і виконали навантажувальне тестування за допомогою Locust.io, щоб підтвердити, що відбулося покращення продуктивності. Переглядаючи виробничі дані на NewRelic приблизно під час випуску оновленого коду, вплив на продуктивність стає вражаюче очевидним.

Наведений вище графік показує середній час відповіді для мобільних API UserCourseEnrollmentsList кінцева точка в мілісекундах. Кінцеву точку було оновлено для використання кешування метаданих 1 липня. Середній час відгуку знизився з ~1100 мс до ~110 мс.
Додаткові програми кешування
Оскільки для цього потрібен подібний набір метаданих, я також оновив інформаційну панель веб-студента для використання системи кешування метаданих (PR # 8642), що призвело до менш драматичного, але все ж значного скорочення часу відповіді.

Наведений вище графік показує aсередній час відповіді для веб-панелі студентів edX у мілісекундах. Інформаційну панель було оновлено для використання кешування метаданих 17 липня. Середній час відгуку знизився з 600-800 мс до ~250 мс.
На додаток до цих двох варіантів використання, переваги системи кешування можна використовувати в будь-якому сценарії, де потрібні базові, незалежні від користувача метадані курсу. Наприклад, інший співробітник edX нещодавно оновив наш загальний API реєстрації для використання Огляд курсуs замість виклику get_course (PR # 8927), що призводить до 10-кратного зменшення часу відповіді 95-го процентиля для EnrollmentListView кінцева точка.
Висновок
Як згадав мій колега-стажер Бен його допис у блозі, програма стажування edX дійсно блищить тим, як вона вбудовує своїх стажерів у справжні команди розробників програмного забезпечення, ставлячись до них як до штатних працівників і надаючи їм ефективні проекти для роботи. Ніколи раніше не працюючи над проектом такого масштабу та такої кількості учасників, я дізнався неймовірно багато. Незважаючи на те, що мій термін роботи тут завершився, я сподіваюся й надалі брати участь у чудовій спільноті Open edX. Нарешті, я хотів би подякувати Німіші, Крісу, Кішору, Адаму, Дейву та решті команди edX за незрівнянний досвід стажування!
![]()