optimise MariaDB : deux index confirmés par EXPLAIN, N+1 supprimé
Jeu de données de charge (500 classes, 20 000 élèves, 30 000 sessions de lab) généré localement contre une MariaDB jetable, jamais en production, via la nouvelle commande charge:generer (refuse de tourner si APP_ENV=production). EXPLAIN confirme deux manques réels : lab_sessions.etat (30 056 lignes parcourues sans index, lu chaque minute par l'expiration des sessions et le self-healing) et classes.archivee_a (lu chaque jour par la purge RGPD). devoirs.echeance n'avait besoin de rien : l'index existant sur classe_id suffisait déjà, EXPLAIN l'a confirmé plutôt que d'ajouter un index inutile. ResultatsController faisait une requête de corrections et une d'observations par classe du prof (N+1) : remplacé par une requête de chaque pour toutes les classes, regroupées en mémoire. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
ca544ac620
commit
e35e0e3c0b
5 changed files with 305 additions and 6 deletions
|
|
@ -4,6 +4,8 @@ namespace App\Http\Controllers;
|
|||
|
||||
use App\Models\Classe;
|
||||
use App\Models\Correction;
|
||||
use App\Models\Observation;
|
||||
use App\Models\Seance;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\View\View;
|
||||
|
||||
|
|
@ -11,6 +13,10 @@ use Illuminate\View\View;
|
|||
* « Résultats » (séparation vitrine/application, 2026-10-13) : vue
|
||||
* d'ensemble, par classe, des corrections et des observations. Le détail
|
||||
* séance par séance reste dans le cockpit de chaque classe.
|
||||
*
|
||||
* Tout en 4 requêtes au total, quel que soit le nombre de classes du prof
|
||||
* (bloc B, optimisation MariaDB, 2026-10-14) : une requête par classe pour
|
||||
* les corrections et une pour les observations étaient un N+1 classique.
|
||||
*/
|
||||
class ResultatsController extends Controller
|
||||
{
|
||||
|
|
@ -18,12 +24,24 @@ class ResultatsController extends Controller
|
|||
{
|
||||
$prof = Auth::guard('web')->user();
|
||||
$classes = $prof->classes()->whereNull('archivee_a')->orderBy('nom')->get();
|
||||
$classeIds = $classes->pluck('id');
|
||||
|
||||
$resultats = $classes->map(function (Classe $classe) {
|
||||
$corrections = Correction::whereHas('labSession', fn ($q) => $q->whereIn('seance_id', $classe->seances()->select('id')))
|
||||
->latest()
|
||||
->limit(50)
|
||||
->get();
|
||||
$classeIdParSeanceId = Seance::whereIn('classe_id', $classeIds)->pluck('classe_id', 'id');
|
||||
|
||||
$correctionsParClasse = Correction::whereHas('labSession', fn ($q) => $q->whereIn('seance_id', $classeIdParSeanceId->keys()))
|
||||
->with('labSession:id,seance_id')
|
||||
->latest()
|
||||
->get()
|
||||
->groupBy(fn (Correction $c): int => $classeIdParSeanceId[$c->labSession->seance_id] ?? 0);
|
||||
|
||||
$observationsParClasse = Observation::whereIn('classe_id', $classeIds)
|
||||
->with('eleve')
|
||||
->latest()
|
||||
->get()
|
||||
->groupBy('classe_id');
|
||||
|
||||
$resultats = $classes->map(function (Classe $classe) use ($correctionsParClasse, $observationsParClasse) {
|
||||
$corrections = ($correctionsParClasse->get($classe->id) ?? collect())->take(50);
|
||||
|
||||
return [
|
||||
'classe' => $classe,
|
||||
|
|
@ -31,7 +49,7 @@ class ResultatsController extends Controller
|
|||
'reussiteMoyenne' => $corrections->isNotEmpty()
|
||||
? (int) round($corrections->avg(fn ($c) => $c->note_max > 0 ? $c->note / $c->note_max * 100 : 0))
|
||||
: null,
|
||||
'observations' => $classe->observations()->with('eleve')->limit(5)->get(),
|
||||
'observations' => ($observationsParClasse->get($classe->id) ?? collect())->take(5),
|
||||
];
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue