Vingt mille soumissions de quiz en moins d’un quart d’heure : c’est le pic observé sur une plateforme e-learning WordPress destinée à un réseau d’établissements scolaires, au moment précis où l’ensemble des élèves connectés à distance validaient leur quiz de fin de trimestre avant la fermeture du créneau horaire. Ce type de pic, prévisible dans son principe (tout le monde soumet en même temps, faute d’anticipation) mais redoutable dans son ampleur, imposait une architecture qui ne dépende pas d’un traitement synchrone de chaque soumission.
Architecture retenue
Navigateur élève
│ POST /soumettre-quiz (réponse immédiate : "reçu")
▼
Point d'entrée REST WordPress
│ Validation minimale (format, jeton de session)
│ Écriture brute en file (table dédiée, pas de calcul de note)
▼
Table file_soumissions_quiz (statut: en_attente)
│
▼
Worker Action Scheduler (traitement différé, par lots)
│ Calcul de la note, mise à jour du profil élève,
│ notification à l'enseignant si besoin
▼
Table resultats_quiz (statut: traite)
Le principe central de cette architecture : la requête HTTP de l’élève ne déclenche jamais le calcul complet du quiz. Elle se contente d’écrire la soumission brute dans une table dédiée, avec un statut « en attente », puis renvoie immédiatement une confirmation de réception à l’élève. Le calcul de la note, la mise à jour du profil et les éventuelles notifications aux enseignants sont traités ensuite, de façon asynchrone, par un ensemble de tâches Action Scheduler qui consomment cette file par lots.
Pourquoi un traitement synchrone aurait échoué
Avant cette refonte, chaque soumission de quiz déclenchait en direct le calcul de note (comparaison des réponses avec le corrigé, pondération selon le barème, mise à jour immédiate du tableau de bord enseignant). Sur un pic de 20 000 soumissions en quinze minutes, soit environ 22 requêtes par seconde en moyenne avec des pointes bien supérieures aux dernières minutes du créneau, ce traitement synchrone saturait rapidement le pool PHP-FPM, provoquant des délais d’attente puis des échecs de soumission pour les élèves connectés dans les toutes dernières minutes, précisément les plus stressés par l’approche de la fermeture du créneau.

Ce que change la file du point de vue de l’élève
Le point d’entrée REST, désormais réduit à une écriture minimale en base sans aucun calcul, répond en quelques dizaines de millisecondes, quel que soit le nombre de soumissions simultanées, tant que la table de file elle-même absorbe les écritures (ce qui reste largement dans les capacités de MySQL pour de simples INSERT sans jointure complexe). L’élève reçoit une confirmation immédiate de type « Votre quiz a bien été reçu, votre note sera disponible sous peu », ce qui élimine l’anxiété liée à une page qui semble bloquée en pleine soumission.
Le traitement en tâche de fond
Les tâches Action Scheduler consomment la file par lots de 200 soumissions, avec une planification toutes les deux minutes, ce qui permet d’absorber le pic sans jamais bloquer un unique worker sur un traitement trop long :
add_action( 'traiter_lot_soumissions_quiz', function () {
$lot = get_soumissions_en_attente( 200 );
foreach ( $lot as $soumission ) {
$note = calculer_note_quiz( $soumission );
enregistrer_resultat( $soumission->id, $note );
marquer_soumission_traitee( $soumission->id );
}
if ( has_more_soumissions_en_attente() ) {
as_schedule_single_action( time() + 30, 'traiter_lot_soumissions_quiz' );
}
});
Lors du pic mesuré, la totalité des 20 000 soumissions a été absorbée par le point d’entrée en moins de deux minutes, puis intégralement traitée par les workers en tâche de fond en un peu moins de vingt minutes, un délai jugé acceptable par les équipes pédagogiques puisque les notes n’ont pas vocation à s’afficher instantanément.
Ce qu’il a fallu surveiller
- La croissance de la table de file en cas de panne prolongée des workers, avec une alerte automatique si plus de 5 000 soumissions restent en attente au-delà de trente minutes.
- L’ordre de traitement, garanti par une colonne d’horodatage plutôt qu’un simple ordre d’insertion, pour éviter tout biais entre élèves ayant soumis à quelques secondes d’écart.
- L’idempotence du traitement, chaque soumission ne pouvant passer qu’une seule fois du statut « en attente » à « traité », même en cas de double déclenchement d’un lot.
Une file qui absorbe un pic prévisible vaut toujours mieux qu’un traitement synchrone qui promet une réponse immédiate mais s’effondre précisément quand tout le monde soumet en même temps.
En résumé
Face à un pic prévisible de 20 000 soumissions de quiz en fin de trimestre, la séparation entre un point d’entrée minimal (écriture en file, réponse immédiate) et un traitement asynchrone par lots via Action Scheduler a permis d’absorber la charge sans perte de soumission ni dégradation du temps de réponse perçu par les élèves. Le délai de vingt minutes pour traiter l’ensemble de la file s’est révélé largement acceptable, dès lors que la confirmation de réception, elle, reste instantanée.