El reto
Una universidad norteamericana operaba más de 30 sistemas académicos y administrativos contra Canvas LMS mediante una sincronización por lotes nocturna que se había convertido en un frágil proceso de 6 horas. Cada vez que Canvas añadía una función, el proceso se rompía; cada vez que aumentaba la matrícula, alcanzaba los límites de tasa de la API de Canvas y fallaba en silencio a mitad de camino; cada vez que un sistema dependiente necesitaba datos más recientes, la respuesta era "mañana por la mañana, quizá".
El equipo central de TI tenía tres problemas concretos. Primero, los docentes se quejaban de que los cambios en el libro de calificaciones tardaban hasta 24 horas en llegar a los paneles de analítica. Segundo, el proceso de sincronización consumía tanta cuota de la API que Canvas limitaba la tasa de otras integraciones. Tercero, el proceso no tenía forma de recuperarse — si fallaba a las 03:14, un ingeniero tenía que despertarse, averiguar dónde se había detenido y reejecutarlo manualmente. El equipo necesitaba una integración lo bastante en tiempo real para los docentes, lo bastante suave con Canvas para convivir con los límites de tasa compartidos y lo bastante observable para que un ingeniero de guardia pudiera confiar en ella.
Nuestra solución
Reemplazamos el proceso nocturno por una integración de Canvas LMS orientada a eventos en Python y FastAPI, construida sobre tres primitivas: un consumidor de Canvas Live Events, un cliente de la API de Canvas con control de concurrencia consciente de la cuota y un bus de eventos de cambio idempotente al que se suscriben los sistemas dependientes.
Los Canvas Live Events llegan a un receptor de webhooks de FastAPI, se verifican, se persisten en una tabla inbox y los procesa un worker de Celery que los distribuye a los manejadores dependientes correspondientes. Para el estado que Canvas no envía (contenido de cursos, datos detallados de matrícula, listas grandes), usamos una capa de sondeo inteligente que emplea GraphQL cuando es más barato y REST cuando no, a través de un único cliente de Canvas que respeta la cabecera `X-Rate-Limit-Remaining` y reduce el ritmo de forma dinámica antes de que Canvas se lo pida.
Los sistemas dependientes ya no hablan directamente con Canvas — se suscriben a nuestro bus interno de eventos de cambio, que es idempotente y está ordenado por entidad (por estudiante, por curso). Esa única decisión arquitectónica acabó con el problema de las llamadas duplicadas: más de 30 sistemas solían hacerle a Canvas la misma pregunta cada noche; ahora todos consumen un único evento normalizado. El volumen diario de llamadas a la API de Canvas se estabilizó en torno a 50K — muy dentro de la cuota de la universidad y con un margen predecible — mientras que la latencia de propagación de un cambio en el libro de calificaciones bajó de 24 horas a menos de 90 segundos.
- Consumidor de Canvas Live Events con verificación de carga firmada y persistencia en inbox
- Cliente de la API de Canvas consciente de la cuota, que respeta X-Rate-Limit-Remaining y usa concurrencia adaptativa
- Estrategia mixta REST + GraphQL — GraphQL donde reduce el volumen de solicitudes
- Ciclo de vida de tokens OAuth2 con rotación automática y reintento con renovación ante 401
- Bus de eventos de cambio idempotente y ordenado por entidad, al que se suscriben los sistemas dependientes
- Herramienta de reproducción integrada — reprocesa cualquier ventana de eventos de Canvas sin coordinación
- Optimización de paginación (cursores tipo marcador) que elimina los patrones N+1 de desplazamiento profundo
- Paneles de Datadog para latencia de extremo a extremo, margen de cuota de Canvas y retraso por sistema
- Alertas de PagerDuty sobre la tasa de consumo de cuota, la acumulación de eventos y el estado de los tokens OAuth