NeahStable/PERFORMANCE_AND_PRODUCTION_ANALYSIS.md
2026-01-16 18:22:20 +01:00

18 KiB

Analyse Performance et Préparation Production - Neah

Date: $(date)
Auteur: Analyse Senior Developer
Version: 1.0


📋 Table des Matières

  1. Résumé Exécutif
  2. Analyse des Performances
  3. Problèmes de Configuration Production
  4. Sécurité
  5. Recommandations Prioritaires
  6. Checklist de Mise en Production

🎯 Résumé Exécutif

État Actuel

  • Architecture solide: Next.js 16, Prisma, Redis, PostgreSQL
  • Bonnes pratiques: Logging structuré, gestion d'erreurs, cache Redis
  • ⚠️ Problèmes critiques: Configuration DB pool, timeouts HTTP, logs console
  • ⚠️ Production readiness: 70% - Nécessite corrections avant déploiement

Priorités

  1. CRITIQUE - Configuration pool de connexions Prisma
  2. CRITIQUE - Correction Dockerfile (migrate dev → deploy)
  3. HAUTE - Ajout de timeouts sur toutes les requêtes HTTP
  4. HAUTE - Remplacement console.log par logger structuré
  5. MOYENNE - Optimisation images Next.js
  6. MOYENNE - Rate limiting et circuit breakers

🔍 Analyse des Performances

1. Base de Données (PostgreSQL)

Problèmes Identifiés

1.1 Pool de Connexions Non Configuré

// lib/prisma.ts - ACTUEL
export const prisma = new PrismaClient({
  datasources: { db: { url: env.DATABASE_URL } },
  log: process.env.NODE_ENV === 'production' ? [] : ['query'],
})

Problème:

  • Pas de limite de connexions
  • Risque d'épuisement du pool sous charge
  • Pas de configuration de timeout
  • Pas de monitoring des connexions

Impact: 🔴 CRITIQUE - Peut causer des timeouts et crashes en production

Solution Recommandée:

// lib/prisma.ts - RECOMMANDÉ
export const prisma = new PrismaClient({
  datasources: {
    db: {
      url: env.DATABASE_URL
    }
  },
  log: process.env.NODE_ENV === 'production' ? [] : ['error', 'warn'],
})

// Configuration du pool via DATABASE_URL
// DATABASE_URL=postgresql://user:pass@host:5432/db?connection_limit=10&pool_timeout=20

Configuration DATABASE_URL recommandée:

postgresql://user:pass@host:5432/db?connection_limit=10&pool_timeout=20&connect_timeout=10

1.2 Requêtes N+1 Potentielles

Fichiers à vérifier:

  • app/api/missions/route.ts - Include multiples relations
  • app/api/missions/all/route.ts - Include missionUsers avec user
  • app/api/calendars/route.ts - Vérifier les requêtes imbriquées

Recommandation: Utiliser select au lieu de include quand possible, ou utiliser Prisma.raw pour des requêtes optimisées.


2. Requêtes HTTP Externes

Problèmes Identifiés

2.1 Timeouts Manquants

Fichiers affectés:

  • lib/services/n8n-service.ts - Pas de timeout sur fetch
  • app/api/missions/[missionId]/generate-plan/route.ts - Pas de timeout
  • app/api/users/[userId]/route.ts - Pas de timeout sur Leantime API
  • app/api/rocket-chat/messages/route.ts - Timeouts partiels
  • app/api/leantime/tasks/route.ts - Pas de timeout

Exemple problématique:

// ❌ MAUVAIS - app/api/missions/[missionId]/generate-plan/route.ts
const response = await fetch(webhookUrl, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey },
  body: JSON.stringify(webhookData),
});
// Pas de timeout - peut bloquer indéfiniment

Solution Recommandée:

// ✅ BON
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 30000); // 30s timeout

try {
  const response = await fetch(webhookUrl, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey },
    body: JSON.stringify(webhookData),
    signal: controller.signal,
  });
  clearTimeout(timeoutId);
  // ... handle response
} catch (error) {
  clearTimeout(timeoutId);
  if (error.name === 'AbortError') {
    throw new Error('Request timeout after 30s');
  }
  throw error;
}

2.2 Pas de Circuit Breaker

Problème: Si un service externe (N8N, Leantime, RocketChat) est down, toutes les requêtes échouent sans retry intelligent.

Recommandation: Implémenter un circuit breaker pattern:

// lib/utils/circuit-breaker.ts
class CircuitBreaker {
  private failures = 0;
  private lastFailureTime = 0;
  private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED';
  
  async execute<T>(fn: () => Promise<T>): Promise<T> {
    if (this.state === 'OPEN') {
      if (Date.now() - this.lastFailureTime > 60000) {
        this.state = 'HALF_OPEN';
      } else {
        throw new Error('Circuit breaker is OPEN');
      }
    }
    
    try {
      const result = await fn();
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }
  
  private onSuccess() {
    this.failures = 0;
    this.state = 'CLOSED';
  }
  
  private onFailure() {
    this.failures++;
    this.lastFailureTime = Date.now();
    if (this.failures >= 5) {
      this.state = 'OPEN';
    }
  }
}

3. Cache et Optimisations

Points Positifs

  • Redis utilisé pour cache emails, notifications, messages
  • Request deduplication implémentée (lib/utils/request-deduplication.ts)
  • Cache avec TTL approprié

⚠️ Améliorations Possibles

3.1 Cache Database Queries

// Exemple: app/api/missions/route.ts
// Ajouter cache pour les requêtes fréquentes
const cacheKey = `missions:${userId}:${limit}:${offset}`;
const cached = await redis.get(cacheKey);
if (cached) return JSON.parse(cached);

const missions = await prisma.mission.findMany(...);
await redis.setex(cacheKey, 60, JSON.stringify(missions)); // 60s TTL

3.2 Optimisation Images Next.js

Problème actuel:

// next.config.mjs
images: {
  unoptimized: true, // ❌ Désactive l'optimisation
}

Solution:

images: {
  unoptimized: false, // ✅ Active l'optimisation
  formats: ['image/avif', 'image/webp'],
  deviceSizes: [640, 750, 828, 1080, 1200],
  imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
}

4. WebSocket et Connexions Longues

⚠️ Problèmes Identifiés

4.1 RocketChat Call Listener

  • lib/services/rocketchat-call-listener.ts
  • Pas de rate limiting sur reconnexions
  • Pas de backoff exponentiel optimal
  • Logs console.log excessifs (35 occurrences)

Recommandations:

// Améliorer le backoff
private reconnectDelay = 3000;
private maxReconnectAttempts = 10;

// ✅ MEILLEUR
private getReconnectDelay(): number {
  return Math.min(3000 * Math.pow(2, this.reconnectAttempts), 30000);
}

4.2 IMAP Connection Pool

  • lib/services/email-service.ts
  • Pool bien géré mais peut être optimisé
  • Timeout de 30 minutes peut être réduit

5. Logging et Monitoring

Problèmes Critiques

5.1 Console.log en Production

Statistiques:

  • 110 occurrences de console.log/error/warn/debug dans lib/services/
  • 35 dans rocketchat-call-listener.ts
  • 19 dans refresh-manager.ts
  • 17 dans prefetch-service.ts

Impact:

  • Performance dégradée (console.log est synchrone)
  • Logs non structurés
  • Pas de centralisation

Solution:

// Remplacer tous les console.log par logger
// ❌ AVANT
console.log('[ROCKETCHAT] Message received', data);

// ✅ APRÈS
logger.debug('[ROCKETCHAT] Message received', { data });

Action: Créer un script de migration:

# scripts/replace-console-logs.sh
find lib/services -name "*.ts" -exec sed -i '' 's/console\.log/logger.debug/g' {} \;
find lib/services -name "*.ts" -exec sed -i '' 's/console\.error/logger.error/g' {} \;
find lib/services -name "*.ts" -exec sed -i '' 's/console\.warn/logger.warn/g' {} \;

5.2 Pas de Métriques de Performance

Recommandation: Ajouter des métriques:

// lib/utils/metrics.ts
export function trackApiCall(endpoint: string, duration: number) {
  // Envoyer à un service de métriques (DataDog, New Relic, etc.)
  logger.info('API_CALL', { endpoint, duration });
}

🏗️ Problèmes de Configuration Production

1. Dockerfile

Problème Critique

Fichier: Dockerfile

# ❌ MAUVAIS - Ligne 18
RUN npx prisma migrate dev --name init

Problème:

  • migrate dev crée une nouvelle migration même si la DB est à jour
  • Ne doit JAMAIS être utilisé en production
  • Peut causer des conflits de migrations

Solution:

# ✅ BON
# Ne pas faire de migrations dans le Dockerfile
# Les migrations doivent être appliquées séparément avant le déploiement
# RUN npx prisma migrate deploy  # Seulement si nécessaire, et seulement en production

Recommandation: Utiliser Dockerfile.prod qui est mieux configuré, mais vérifier qu'il n'utilise pas migrate dev.

2. Next.js Configuration

⚠️ Problèmes

Fichier: next.config.mjs

// ❌ PROBLÉMATIQUE
eslint: {
  ignoreDuringBuilds: true, // Cache les erreurs ESLint
},
typescript: {
  ignoreBuildErrors: true, // Cache les erreurs TypeScript
},
images: {
  unoptimized: true, // Désactive l'optimisation d'images
},

Recommandation:

// ✅ MEILLEUR
eslint: {
  ignoreDuringBuilds: false, // Ou au moins en staging
},
typescript: {
  ignoreBuildErrors: false, // Corriger les erreurs TypeScript
},
images: {
  unoptimized: false, // Activer l'optimisation
  formats: ['image/avif', 'image/webp'],
},

3. Variables d'Environnement

⚠️ Vérifications Nécessaires

Variables critiques à vérifier:

  • DATABASE_URL - Doit inclure les paramètres de pool
  • REDIS_URL - Doit être configuré avec timeout
  • NEXTAUTH_SECRET - Doit être unique et fort
  • Tous les tokens API (N8N, Leantime, RocketChat)

Recommandation: Créer un script de validation:

// scripts/validate-env.ts
const requiredVars = [
  'DATABASE_URL',
  'NEXTAUTH_SECRET',
  'NEXTAUTH_URL',
  // ...
];

for (const varName of requiredVars) {
  if (!process.env[varName]) {
    throw new Error(`Missing required environment variable: ${varName}`);
  }
}

🔒 Sécurité

1. Secrets dans les Logs

⚠️ Risque Modéré

Problème: Vérifier que les mots de passe/tokens ne sont pas loggés.

Fichiers à vérifier:

  • lib/services/email-service.ts - Credentials IMAP
  • app/api/courrier/test-connection/route.ts - Password dans logs

Solution actuelle (bonne):

// ✅ BON - app/api/courrier/test-connection/route.ts
console.log('Testing connection with:', {
  ...body,
  password: body.password ? '***' : undefined // Masqué
});

2. Rate Limiting

Manquant

Problème: Pas de rate limiting sur les API routes.

Recommandation: Implémenter avec @upstash/ratelimit ou middleware Next.js:

// middleware.ts
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';

const ratelimit = new Ratelimit({
  redis: Redis.fromEnv(),
  limiter: Ratelimit.slidingWindow(10, '10 s'),
});

export async function middleware(request: NextRequest) {
  const ip = request.ip ?? '127.0.0.1';
  const { success } = await ratelimit.limit(ip);
  
  if (!success) {
    return new Response('Rate limit exceeded', { status: 429 });
  }
}

3. CORS et Headers Sécurité

Bon

Fichier: next.config.mjs

  • Content-Security-Policy configuré
  • Headers de sécurité à ajouter si nécessaire

🎯 Recommandations Prioritaires

Priorité 1 - CRITIQUE (Avant Production)

  1. Configurer le pool de connexions Prisma

    • Modifier DATABASE_URL avec connection_limit=10&pool_timeout=20
    • Tester sous charge
  2. Corriger le Dockerfile

    • Retirer migrate dev
    • Utiliser migrate deploy uniquement si nécessaire
  3. Ajouter des timeouts sur toutes les requêtes HTTP

    • Créer un utilitaire fetchWithTimeout
    • Appliquer à tous les fetch() externes
  4. Remplacer console.log par logger

    • Script de migration automatique
    • Vérifier tous les fichiers

Priorité 2 - HAUTE (Semaine 1)

  1. Implémenter Circuit Breaker

    • Pour N8N, Leantime, RocketChat
    • Retry avec backoff exponentiel
  2. Activer l'optimisation d'images Next.js

    • Modifier next.config.mjs
    • Tester les performances
  3. Ajouter Rate Limiting

    • Sur les API routes critiques
    • Monitoring des limites

Priorité 3 - MOYENNE (Semaine 2-3)

  1. Cache des requêtes DB fréquentes

    • Missions, Calendars, Users
    • TTL approprié (60-300s)
  2. Monitoring et Métriques

    • Intégrer Sentry ou équivalent
    • Métriques de performance (APM)
  3. Optimiser les requêtes N+1

    • Audit des include Prisma
    • Utiliser select quand possible

Checklist de Mise en Production

Pré-Déploiement

Configuration

  • Configurer DATABASE_URL avec pool settings
  • Vérifier toutes les variables d'environnement
  • Créer script de validation des env vars
  • Configurer les secrets dans Vercel (pas en code)

Code

  • Corriger le Dockerfile (retirer migrate dev)
  • Ajouter timeouts sur toutes les requêtes HTTP
  • Remplacer tous les console.log par logger
  • Activer l'optimisation d'images Next.js
  • Corriger les erreurs TypeScript/ESLint (ou documenter pourquoi ignorées)

Base de Données

  • Appliquer toutes les migrations Prisma
  • Créer des index sur les colonnes fréquemment queryées
  • Configurer les sauvegardes automatiques
  • Tester la restauration depuis backup

Sécurité

  • Implémenter rate limiting
  • Vérifier qu'aucun secret n'est loggé
  • Configurer CORS correctement
  • Activer HTTPS uniquement

Monitoring

  • Configurer Sentry ou équivalent
  • Créer endpoint /api/health
  • Configurer les alertes Vercel
  • Documenter les procédures d'alerte

Déploiement

Staging

  • Déployer en staging d'abord
  • Tester toutes les fonctionnalités critiques
  • Vérifier les performances sous charge
  • Tester les scénarios d'erreur

Production

  • Appliquer les migrations Prisma
  • Déployer sur Vercel
  • Vérifier les health checks
  • Monitorer les logs les premières heures
  • Tester les fonctionnalités critiques

Post-Déploiement

Monitoring

  • Surveiller les métriques de performance
  • Vérifier les logs d'erreurs
  • Monitorer l'utilisation DB/Redis
  • Vérifier les alertes fonctionnent

Optimisation Continue

  • Analyser les requêtes DB lentes
  • Optimiser les endpoints les plus utilisés
  • Ajuster les TTL de cache selon l'usage
  • Réviser les timeouts selon les métriques

📊 Métriques de Succès

Performance

  • Temps de réponse API: < 200ms (p95)
  • Temps de chargement page: < 2s (First Contentful Paint)
  • Taux d'erreur: < 0.1%
  • Uptime: > 99.9%

Base de Données

  • Connexions actives: < 80% du pool max
  • Requêtes lentes: < 1% des requêtes > 1s
  • Taux de cache hit: > 70% pour les requêtes fréquentes

Infrastructure

  • CPU utilisation: < 70% en moyenne
  • Mémoire: < 80% en moyenne
  • Disque: < 80% utilisé

🔧 Scripts Utiles

Validation Environnement

# scripts/validate-production.sh
#!/bin/bash
set -e

echo "Validating production environment..."

# Vérifier les variables d'environnement
node scripts/validate-env.ts

# Vérifier les migrations
npx prisma migrate status

# Vérifier la connexion DB
npx prisma db execute --stdin <<< "SELECT 1"

# Vérifier Redis
redis-cli -h $REDIS_HOST -p $REDIS_PORT ping

echo "✅ All checks passed"

Migration Console.log

# scripts/migrate-console-logs.sh
#!/bin/bash

find lib/services app/api -name "*.ts" -type f | while read file; do
  sed -i '' 's/console\.log(/logger.debug(/g' "$file"
  sed -i '' 's/console\.error(/logger.error(/g' "$file"
  sed -i '' 's/console\.warn(/logger.warn(/g' "$file"
  sed -i '' 's/console\.debug(/logger.debug(/g' "$file"
done

echo "✅ Console logs migrated to logger"

Health Check

// app/api/health/route.ts
import { NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
import { getRedisClient } from '@/lib/redis';

export async function GET() {
  const health = {
    status: 'ok',
    timestamp: new Date().toISOString(),
    checks: {
      database: 'unknown',
      redis: 'unknown',
    },
  };

  // Check DB
  try {
    await prisma.$queryRaw`SELECT 1`;
    health.checks.database = 'ok';
  } catch (error) {
    health.checks.database = 'error';
    health.status = 'degraded';
  }

  // Check Redis
  try {
    const redis = getRedisClient();
    await redis.ping();
    health.checks.redis = 'ok';
  } catch (error) {
    health.checks.redis = 'error';
    health.status = 'degraded';
  }

  return NextResponse.json(health, {
    status: health.status === 'ok' ? 200 : 503,
  });
}

📚 Ressources


📝 Notes Finales

Cette analyse identifie les problèmes critiques à résoudre avant la mise en production. Les priorités 1 doivent être traitées immédiatement, les priorités 2 dans la semaine, et les priorités 3 peuvent être planifiées après le déploiement initial.

Recommandation: Créer des tickets pour chaque point de la checklist et les suivre jusqu'à résolution complète.


Dernière mise à jour: $(date)