Le défi
Une université nord-américaine exploitait plus de 30 systèmes académiques et administratifs avec Canvas LMS via une synchronisation par lots nocturne devenue un traitement fragile de 6 heures. Chaque fois que Canvas ajoutait une fonctionnalité, le traitement plantait ; chaque fois que les inscriptions augmentaient, il atteignait les limites de débit de l’API Canvas et échouait silencieusement à mi-parcours ; chaque fois qu’un système en aval avait besoin de données plus fraîches, la réponse était « demain matin, peut-être ».
L’équipe informatique centrale avait trois problèmes concrets. Premièrement, les enseignants se plaignaient que les modifications du carnet de notes mettaient jusqu’à 24 heures à atteindre les tableaux de bord analytiques. Deuxièmement, le traitement consommait une telle part du quota d’API que d’autres intégrations étaient bridées par Canvas. Troisièmement, il n’existait aucun scénario de reprise — s’il échouait à 03 h 14, un ingénieur devait se réveiller, trouver où il s’était arrêté et le rejouer manuellement. L’équipe avait besoin d’une intégration assez temps réel pour les enseignants, assez douce envers Canvas pour respecter les limites de débit partagées, et assez observable pour qu’un ingénieur d’astreinte puisse lui faire confiance.
Notre solution
Nous avons remplacé le traitement nocturne par une intégration Canvas LMS événementielle en Python et FastAPI, bâtie autour de trois primitives : un consommateur de Canvas Live Events, un client d’API Canvas avec contrôle de concurrence conscient des quotas, et un bus d’événements de changement idempotent auquel les systèmes en aval s’abonnent.
Les Canvas Live Events arrivent dans un récepteur de webhooks FastAPI, sont vérifiés, persistés dans une table inbox puis traités par un worker Celery qui les distribue aux bons gestionnaires en aval. Pour l’état que Canvas ne pousse pas (contenu des cours, données d’inscription détaillées, grands effectifs), nous utilisons une couche d’interrogation intelligente qui emploie GraphQL lorsque c’est moins coûteux et REST sinon, via un client Canvas unique qui respecte l’en-tête `X-Rate-Limit-Remaining` et ralentit dynamiquement avant que Canvas ne l’y oblige.
Les systèmes en aval ne dialoguent plus directement avec Canvas — ils s’abonnent à notre bus interne d’événements de changement, idempotent et ordonné par entité (par étudiant, par cours). Ce seul choix d’architecture a supprimé le problème des appels en double : plus de 30 systèmes posaient chaque nuit la même question à Canvas ; ils consomment désormais tous un seul événement normalisé. Le volume quotidien d’appels à l’API Canvas s’est stabilisé autour de 50 000 — largement dans le quota de l’université avec une marge prévisible — tandis que la latence de propagation d’une modification du carnet de notes est passée de 24 heures à moins de 90 secondes.
- Consommateur de Canvas Live Events avec vérification de charge utile signée et persistance inbox
- Client d’API Canvas conscient des quotas, respectant X-Rate-Limit-Remaining et à concurrence adaptative
- Stratégie mixte REST + GraphQL — GraphQL là où il réduit le volume de requêtes
- Cycle de vie des jetons OAuth2 avec rotation automatique et repli par rafraîchissement en cas de 401
- Bus d’événements de changement idempotent et ordonné par entité auquel s’abonnent les systèmes en aval
- Outil de rejeu intégré — retraitement de toute fenêtre d’événements Canvas sans coordination
- Optimisation de la pagination (curseurs de type signet) éliminant les schémas N+1 à décalage profond
- Tableaux de bord Datadog pour la latence de bout en bout, la marge de quota Canvas et le retard par système
- Alertes PagerDuty sur le rythme de consommation du quota, l’arriéré d’événements et la santé des jetons OAuth