# Backend — Risques & vigilance : Redis > Extrait de la base de connaissance Lead_tech. Voir `knowledge/backend/risques/README.md` pour l'index complet. --- ## Redis — thrash de connexion sous charge ### Risques - Connexions concurrentes multiples si `connect()` est appelé "à la demande" sans lock - Spam logs + saturation connexions quand Redis est down ou lent ### Symptômes - N appels simultanés → N tentatives de connexion en parallèle - Logs "Redis connection failed" en rafale au démarrage ou lors d'un restart Redis ### Bonnes pratiques / mitigations ```typescript // Pattern single-flight + cooldown + fallback DB best-effort if (!this.connectPromise) { this.connectPromise = this.client.connect().finally(() => { this.connectPromise = null; }); } await this.connectPromise; // Si échec → nextConnectRetryAtMs = now + 1000 → return false → fallback DB ``` - Contexte technique : Redis / NestJS — 09-03-2026 --- ## Entitlements — TTL cache supérieur au SLA de propagation ### Risques - TTL cache > SLA propagation → un webhook raté viole mécaniquement le SLA (accès stale plus long que garanti) - Utilisateur avec accès périmé ou sans accès dû, pendant toute la durée du TTL résiduel ### Symptômes - Accès premium encore actif après annulation (ou inversement) - NFR "propagation ≤ 60s" non respecté en cas de webhook manqué ### Bonnes pratiques / mitigations - TTL cache ≤ SLA cible (ex : NFR "≤ 60s" → TTL = 60s max) - Toujours coupler TTL + invalidation explicite via webhook (les deux, pas l'un ou l'autre) - Contexte technique : Redis / entitlements / NestJS — 09-03-2026 --- ## Compteurs in-memory ≠ métriques persistées ### Risques - Compteurs in-memory remis à zéro au restart (perte de données) - Non agrégables sur plusieurs instances (données partielles par pod) ### Symptômes - Métriques qui "repartent de 0" à chaque déploiement - Dashboards incorrects en environnement multi-instance ### Bonnes pratiques / mitigations - V1 low-cost : `Redis INCRBY` best-effort par `eventType` → persisté et agrégé multi-instances - Évolutif vers Prometheus/OTel sans changer l'interface (abstraction dès le départ) - Contexte technique : Redis / NestJS — 09-03-2026 --- ## TTL Redis quota calculé en heure locale (dérive jusqu'à ±12h) ### Risques - Le reset du quota journalier dérive selon le timezone du serveur, pouvant aller jusqu'à ±12h d'écart par rapport à minuit UTC ### Symptômes - Quota qui se remet à zéro à des heures inattendues selon l'environnement de déploiement - Comportement différent en dev local (TZ machine) et en prod (TZ container) ### Bonnes pratiques / mitigations ```typescript // ✅ CORRECT — UTC midnight garanti const midnight = new Date( Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1), ); const ttlMs = midnight.getTime() - now.getTime(); // ❌ RISQUÉ — heure locale du serveur const endOfDay = new Date(); endOfDay.setHours(23, 59, 59, 999); // dérive selon TZ serveur ``` - Règle : tout `expireAt` ou `TTL` de quota journalier doit utiliser `Date.UTC()` — vérifier systématiquement en review - Contexte technique : Redis / NestJS — app-alexandrie 20-03-2026 --- ## Compensation `incrBy(-1)` non-atomique après dépassement de quota ### Risques - Pattern courant de quota Redis : `INCR` → vérifier `> limit` → si oui, décrémenter (`incrBy(-1)`) et lever 429. La compensation n'est PAS atomique avec le check. - Si Redis devient indisponible (flap) entre l'incrément et la compensation, l'appel de décrément échoue silencieusement et retourne `null`. Le compteur reste incrémenté → quota fantôme jusqu'à l'expiration de la clé. ### Symptômes - Utilisateur bloqué par le quota alors qu'il n'a pas atteint la limite réelle - Aucune erreur visible : la valeur de retour `null` du décrément n'est pas vérifiée - Comportement intermittent corrélé aux instabilités Redis ### Bonnes pratiques / mitigations ```typescript // ❌ Compensation non vérifiée — échec silencieux possible await redis.incr(key); if (current > limit) { await redis.incrBy(key, -1); // retourne null si Redis flap → compensation perdue throw quotaExceeded(); } // ✅ Vérifier le retour de la compensation et loguer const compensated = await redis.incrBy(key, -1); if (compensated === null) { logger.error({ type: 'quota', event: 'compensation_failed', key }); } ``` - **Règle** : toujours vérifier le retour du décrément de compensation et loguer explicitement si `null`. Documenter ce choix dans les Dev Notes de la story (comportement intentionnel vs bug). - **Solution robuste** : encapsuler incrément + compensation conditionnelle dans le même pipeline `MULTI/EXEC` ou un script Lua (atomicité garantie), au prix d'une complexité plus élevée. - Contexte technique : Redis / NestJS — app-alexandrie 01-04-2026