- 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.
3.7 KiB
Context
L'application charge les collaborateurs de session via resolveCollaborator appelé en séquence dans une boucle (N+1). Le hook useLive déclenche router.refresh() sur chaque événement SSE reçu, sans groupement, causant des re-renders en cascade si plusieurs événements arrivent simultanément. La fonction cleanupOldEvents existe dans session-share-events.ts mais n'est jamais appelée, laissant les événements s'accumuler indéfiniment. L'absence de loading.tsx sur les routes principales empêche le streaming App Router de s'activer. Les modals (ShareModal) sont inclus dans le bundle initial alors qu'ils sont rarement utilisés.
Goals / Non-Goals
Goals:
- Éliminer le N+1 sur
resolveCollaboratoravec un fetch batché - Grouper les refreshes SSE consécutifs avec un debounce
- Purger les événements SSE au fil de l'eau (après chaque
createEvent) - Activer le streaming de navigation avec
loading.tsxsur les routes à chargement lent - Réduire le bundle JS initial en lazy-loadant les modals
Non-Goals:
- Refactorer l'architecture SSE (sujet Phase 2)
- Changer la stratégie de cache/revalidation (sujet Phase 2)
- Optimiser les requêtes Prisma profondes (sujet Phase 3)
- Modifier le comportement fonctionnel existant
Decisions
1. Batch resolveCollaborator par collect + single query
Décision : Dans session-queries.ts, collecter tous les userId des collaborateurs d'une liste de sessions, puis faire un seul prisma.user.findMany({ where: { id: { in: [...ids] } } }), et mapper les résultats en mémoire.
Alternatives : Garder le N+1 mais ajouter un cache mémoire par requête → rejeté car ne résout pas le problème structurellement.
2. Debounce via useRef + setTimeout natif
Décision : Dans useLive.ts, utiliser useRef pour stocker un timer et setTimeout / clearTimeout pour debounce à 300ms. Pas de dépendance externe.
Alternatives : Bibliothèque lodash.debounce → rejeté pour éviter une dépendance pour 5 lignes.
3. cleanupOldEvents inline dans createEvent
Décision : Appeler cleanupOldEvents à la fin de chaque createEvent (fire-and-forget, pas d'await bloquant). La purge garde les 50 derniers événements par session (seuil actuel).
Alternatives : Cron externe → trop complexe pour un quick win ; interval côté API SSE → couplage non souhaité.
4. loading.tsx avec skeleton minimaliste
Décision : Créer un loading.tsx par route principale (/sessions, /weather, /users) avec un skeleton générique (barres grises animées). Le composant est statique et ultra-léger.
5. next/dynamic avec ssr: false sur les modals
Décision : Wrapper ShareModal (et CollaborationToolbar si pertinent) avec next/dynamic({ ssr: false }). Le composant parent gère le loading state.
Risks / Trade-offs
- Debounce 300ms → légère latence perçue sur les mises à jour collaboratives. Mitigation : valeur configurable via constante.
- cleanupOldEvents fire-and-forget → si la purge échoue, les erreurs sont silencieuses. Mitigation : logger l'erreur sans bloquer.
- Batch resolveCollaborator → si la liste de sessions est très grande (>500), la requête
INpeut être lente. Mitigation : acceptable pour les volumes actuels ; paginer si nécessaire (Phase 3). - next/dynamic ssr: false → les modals ne sont pas rendus côté serveur. Acceptable car ils sont interactifs uniquement.
Migration Plan
Chaque optimisation est indépendante et déployable séparément. Pas de migration de données. Rollback : revert du commit concerné. L'ordre recommandé : (1) batch resolveCollaborator, (2) cleanupOldEvents, (3) debounce useLive, (4) loading.tsx, (5) next/dynamic.