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.
This commit is contained in:
65
openspec/changes/perf-realtime-scale/design.md
Normal file
65
openspec/changes/perf-realtime-scale/design.md
Normal file
@@ -0,0 +1,65 @@
|
||||
## 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<subscriber>>
|
||||
|
||||
**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.
|
||||
Reference in New Issue
Block a user