Files
Froidefond Julien 2d266f89f9 feat(perf): implement performance optimizations for session handling
- Introduced a new configuration file `config.yaml` for specifying project context and artifact rules.
- Added `.openspec.yaml` files for tracking changes related to performance improvements.
- Created design documents outlining the context, goals, decisions, and migration plans for optimizing session performance.
- Proposed changes include batching database queries, debouncing event refreshes, purging old events, and implementing loading states for better user experience.
- Added tasks and specifications to ensure proper implementation and validation of the new features.

These enhancements aim to improve the scalability and responsiveness of the application during collaborative sessions.
2026-03-10 08:06:47 +01:00

3.8 KiB

Context

Chaque route /api/*/subscribe crée un setInterval à 1s qui poll la DB pour les événements. Si 10 utilisateurs ont le même workshop ouvert, c'est 10 requêtes/seconde sur la même table. Le pattern weather utilise déjà une Map de subscribers in-process pour broadcaster les événements sans re-poll, mais ce pattern n'est pas généralisé. Les Server Actions appellent revalidatePath('/sessions') qui invalide tous les sous-segments, forçant Next.js à re-render des pages entières même pour une mutation mineure.

Goals / Non-Goals

Goals:

  • Réduire le nombre de requêtes DB de polling proportionnellement au nombre de clients connectés
  • Fournir un module de broadcast réutilisable pour tous les workshops
  • Réduire la surface d'invalidation du cache Next.js avec des tags granulaires
  • Limiter le volume de données chargées sur la page sessions avec pagination

Non-Goals:

  • Passer à WebSockets ou un serveur temps-réel externe (Redis, Pusher)
  • Modifier le modèle de données Prisma pour les événements
  • Implémenter du SSE multi-process / multi-instance (déploiement standalone single-process)

Decisions

1. Module broadcast.ts : Map<sessionId, Set>

Décision : Créer src/lib/broadcast.ts qui expose :

  • subscribe(sessionId, callback) → retourne unsubscribe()
  • broadcast(sessionId, event) → notifie tous les subscribers

Les routes SSE s'abonnent au lieu de poller. Les Server Actions appellent broadcast() après mutation.

Alternatives : EventEmitter Node.js → rejeté car moins typé ; BroadcastChannel → rejeté car limité à same-origin workers, pas adapté aux route handlers Next.js.

2. Polling de fallback maintenu mais mutualisé

Décision : Garder un seul polling par session active (le premier subscriber démarre l'interval, le dernier le stoppe). Le broadcast natif est prioritaire (appelé depuis Server Actions), le polling est le fallback pour les clients qui rejoignent en cours de route.

3. revalidateTag avec convention de nommage

Décision : Convention de tags :

  • session:<id> — pour une session spécifique
  • sessions-list:<userId> — pour la liste des sessions d'un user
  • workshop:<type> — pour tout le workshop

Chaque query Prisma dans les services est wrappée avec unstable_cache ou utilise cacheTag (Next.js 15+).

Alternatives : Garder revalidatePath mais avec des paths plus précis → moins efficace que les tags.

4. Pagination cursor-based sur sessions page

Décision : Pagination par cursor (basée sur createdAt DESC) plutôt qu'offset, pour la stabilité des listes en insertion fréquente. Taille de page initiale : 20 sessions par type de workshop. UI : bouton "Charger plus" (pas de pagination numérotée).

Alternatives : Virtual scroll → plus complexe, dépendance JS côté client ; offset pagination → instable si nouvelles sessions insérées entre deux pages.

Risks / Trade-offs

  • Broadcast in-process → ne fonctionne qu'en déploiement single-process. Acceptable pour le cas d'usage actuel (standalone Next.js). Documenter la limitation.
  • unstable_cache → API marquée unstable dans Next.js, peut changer. Mitigation : isoler dans les services, pas dans les composants.
  • Pagination → change l'UX de la page sessions (actuellement tout visible). Mitigation : conserver le total affiché et un indicateur "X sur Y".

Migration Plan

  1. Créer src/lib/broadcast.ts sans toucher aux routes existantes
  2. Migrer les routes SSE une par une (commencer par weather qui a déjà le pattern)
  3. Mettre à jour les Server Actions pour appeler broadcast() + revalidateTag()
  4. Ajouter cacheTag aux queries services
  5. Ajouter pagination sur sessions page en dernier (changement UI visible)

Rollback : chaque étape est indépendante — revert par feature.