Mi nombre es Utkarsh Jadhav, y soy estudiante de maestría en Ciencias de la Computación en la Universidad Northeastern, aquí en Boston. Pasé las últimas trece semanas en edX trabajando con el equipo de la plataforma. El equipo de la plataforma es responsable de construir la infraestructura para los productos Open edX, y trabaja en particular para hacer que el código se ejecute más rápido. En esta publicación, me gustaría destacar uno de mis principales proyectos, Call Stack Manager. Es una herramienta que realiza un seguimiento de pilas de llamadas únicas de funciones, métodos y clases de Django Model.
Módulo de estudiante de material didáctico (CSM) yHistorial del módulo estudiantil del material didáctico (CSMH) en el código de la plataforma edx son responsables de mantener el estado de usuario de los estudiantes que intentan problemas y sus respectivas calificaciones. El sitio web edx.org recientemente llegó a 5 millones de estudiantes; el tamaño creciente de CSM y CSMH está causando serias preocupaciones sobre fallas en el espacio y la base de datos. Dividir áreas de código monolítico como LMS y CMS en edx-platform es una necesidad vital a medida que aumenta la base de código y el volumen de la base de datos.
Cliente de estado de usuario de edX (eUSC)
Originalmente, la estructura del cliente de estado de usuario era la siguiente:

Estructura anterior de User State Client que muestra la comunicación entre la plataforma edx y la base de datos MySQL a través de CSM/CSMH
Toda la estructura estaba bajo edx/edx-plataforma. El cliente de estado de usuario se comunicaba directamente con las tablas de MySQL nombradas módulo_estudiante_cursos y Courseware_studentmodulehistorya través de DjangoORM.
Cuando comencé mi pasantía, el equipo ya conocía las desventajas de esta arquitectura. Para solucionar esto, ayudé a implementar la arquitectura propuesta que se muestra en la siguiente figura:

Estructura propuesta de User State Client que muestra una capa edx-user-state-client entre la plataforma y el backend de la base de datos
En esta nueva arquitectura, edx-usuario-estado-cliente actúa como una capa entre edx-platform y los backends de la base de datos. Hay muchas ventajas de usar una estructura de este tipo:
- eUSC actuará como una interfaz única en un nivel abstracto a través del cual se realizarán todas las llamadas a la base de datos. Tal patrón eventualmente ayudará en la comunicación efectiva con la base de datos.
- eUSC también permitirá cambiar fácilmente entre diferentes backends. Esta API permitirá elegir backends, incluso para tareas distribuidas.
Como primer paso para crear esta estructura, creé un repositorio llamado edx/edx-usuario-estado-cliente. Este repositorio contiene la interfaz XBlockUserStateCliente, que se encarga de todas las llamadas realizadas por las clases del modelo Django con la base de datos.
Administrador de pila de llamadas
El XBlockUserStateCliente es responsable de hacer todas las llamadas a la base de datos. Sin embargo, teniendo en cuenta el inmenso tamaño del código base de edX, el uso de extensiones de terceros (por ejemplo, XBlocks) y muchas llamadas realizadas a la base de datos en varios lugares, vale la pena captar las llamadas a la base de datos que no se realizan a través de la interfaz. XBlockUserStateCliente.
Para abordar esta necesidad, desarrollé una biblioteca llamada Administrador de pila de llamadas. Esta es una biblioteca que nos permite rastrear las llamadas que no se realizan a través de la interfaz y se comunican directamente con la base de datos. Call Stack Manager registra dichas llamadas en el registro LMS.
La biblioteca implementa dos decoradores principales:
- @rastrealo – que rastrea la entidad decorada
- @donottrack – que detiene el seguimiento de entidades decoradas por @rastrealo.
La necesidad principal de desarrollar esta biblioteca era rastrear llamadas de clases de modelo en CSM y CSMH, principalmente Módulo Estudiantil y EstudianteMóduloHistoria. La comunicación con las bases de datos en Django se realiza mediante clases definidas por el usuario que subclasifican el Clase Django 'Modelo'. Las clases modelo usan el API QuerySet para crear, recuperar y actualizar bases de datos. Las llamadas realizadas por la API de QuerySet se pueden anular mediante un administrador personalizado llamado Administrador de pila de llamadas – definido en la biblioteca Call Stack Manager. De esta forma, se manejó el caso particular del seguimiento de clases de Django Model que acceden directamente a la base de datos.
Mientras ejecutaba Call Stack Manager en su versión inicial, enfrenté los siguientes problemas:
- Los registros de llamadas se hacían de forma repetitiva, lo que saturaba el registro del LMS.
- Muchas llamadas que ya conocíamos fueron grabadas innecesariamente.
- Los registros de llamadas tenían marcos innecesarios, lo que los hacía largos y difíciles de leer.
Para abordar estos problemas, presenté un nuevo decorador, llamado @donottrack, que detiene el seguimiento del alcance de la función decorada con este decorador. En general, las llamadas a un método rastreado se pueden dividir en dos categorías: las que realiza la implementación de la nueva interfaz y las que no. Las llamadas que ya conocemos y esperamos, es decir, aquellas llamadas de la nueva implementación, pueden ignorarse, ya que solo estamos interesados en capturar llamadas que no conocemos. Por lo tanto, usamos este @donottrack decorador para ocultar cualquier llamada rastreada realizada por esa implementación. En este punto, las únicas llamadas que se rastrearán serán aquellas realizadas desde fuera de la nueva implementación.
De esta manera, las llamadas esperadas y conocidas realizadas a la base de datos (por ejemplo, a través de la interfaz XBlockUserStateCliente) no se registraron, lo que nos da una idea clara de lo que estaban haciendo las llamadas desconocidas. Además, los marcos duplicados en la pila de llamadas se filtraron mediante filtros de expresiones regulares. De esta forma, el número de llamadas registradas fue menor, más preciso y más fácil de leer.
Un ejemplo de una pila de llamadas mencionada anteriormente es la siguiente:
Registro de nueva pila de llamadas número 4 para:
Archivo “/edx/app/edxapp/edx-platform/lms/djangoapps/instructor/views/api.py”, línea 240, envuelto
función de retorno(*args, **kwargs)
Archivo “/edx/app/edxapp/edx-platform/lms/djangoapps/instructor/views/api.py”, línea 176, envuelto
función de retorno(*args, **kwargs)
Archivo “/edx/app/edxapp/edx-platform/lms/djangoapps/instructor/views/api.py”, línea 127, envuelto
return func(solicitud, *args, **kwargs)
Archivo “/edx/app/edxapp/edx-platform/lms/djangoapps/instructor/views/api.py”, línea 1896, en rescore_problem
instructor_task.api.submit_rescore_problem_for_student(solicitud, module_state_key, estudiante)
Archivo “/edx/app/edxapp/edx-platform/lms/djangoapps/instructor_task/api.py”, línea 110, en submit_rescore_problem_for_student
volver enviar_tarea(solicitud, tipo_tarea, clase_tarea, clave_uso.clave_curso, entrada_tarea, clave_tarea)
Archivo “/edx/app/edxapp/edx-platform/lms/djangoapps/instructor_task/api_helper.py”, línea 346, en submit_task
task_class.apply_async(task_args, task_id=task_id)
Archivo “/edx/app/edxapp/edx-platform/lms/djangoapps/instructor_task/tasks.py”, línea 80, en rescore_problem
devuelve run_main_task (entry_id, visit_fcn, action_name)
Archivo “/edx/app/edxapp/edx-platform/lms/djangoapps/instructor_task/tasks_helper.py”, línea 279, en run_main_task
progreso_tarea = task_fcn(id_entrada, id_curso, entrada_tarea, nombre_acción)
Archivo”/edx/app/edxapp/edx-platform/lms/djangoapps/instructor_task/tasks_helper.py”, línea 345, en perform_module_state_update
módulos_a_actualizar = StudentModule.objects.filter(course_id=course_id, module_state_key__in=usage_keys)
Durante el desarrollo de la biblioteca Call Stack Manager, tuve que resolver numerosos problemas básicos de nivel de Python, como el manejo efectivo de las clases de Django Model, la creación de clases de Django Model en tiempo de ejecución con el fin de probar, envolver funciones para que no pierdan su identidad. , manejando enfrentamientos con otros decoradores como @contrato en PyContracts, y muchos más.
Call Stack Manager como biblioteca general
El propósito principal del Call Stack Manager era rastrear las llamadas de Módulo Estudiantil y EstudianteMóduloHistoria. Además, podemos rastrear cualquier función de Python en cualquier nivel particular de código. El seguimiento se puede detener cuando sea necesario. Con el uso de esta biblioteca, podemos desaprobar funciones no deseadas de manera efectiva mediante el seguimiento de llamadas desconocidas. Será interesante continuar con el desarrollo de esta herramienta como una herramienta genérica aplicable a cualquier proyecto de Django.
Conclusión
Creo firmemente que la solución generalizada de Call Stack Manager se puede usar como complemento o biblioteca estándar para el proyecto Django/Python con más adiciones y modificaciones.
Mirando hacia atrás en mi experiencia de pasantía, disfruté trabajando en la base de código de edX. Trabajar en un proyecto de código abierto a gran escala que tiene un gran impacto global es muy emocionante. EdX tiene un equipo de codificadores increíbles y trata a los pasantes como empleados de tiempo completo con la máxima exposición en todos los niveles. Me encontré trabajando en tecnologías de punta y participé en todo tipo de discusiones técnicas con el equipo de Platform. Trabajar en el nivel básico de Python y resolver problemas inusuales e inesperados fue especialmente gratificante. Me gustaría agradecer a John Eskew, Calen Pennington, Ali Mohammad, Brian Beggs, Miki Goyal, Adam Palay y Ned Batchelder por su ayuda y apoyo continuos. Trabajar en edX fue una oportunidad fascinante y desafiante que atesoraré en los años venideros.
![]()