mirror of
https://github.com/MaksTinyWorkshop/_Assistant_Lead_Tech
synced 2026-06-28 01:53:40 +02:00
ef24d85d57
Triage des 27 propositions du buffer de capitalisation (skill capitalisation-triage), avec vérification des doublons contre la base. Intégré dans knowledge/ (23 entrées): - backend: redis (compensation incrBy non-atomique), nestjs (injection cassée sous tsx watch; guard write mode dégradé), async (test rollback pipeline multi-fichiers), contracts (idempotence POST), auth (disclosure comptes soft-deleted), prisma (index partial soft-delete), llm-providers (nouveau: OAuth vs API key, prompt caching). - frontend: tests (garde-fous parking Later), navigation (fichiers non-route sous src/app Expo Router), general (type client vs payload backend), state (fallback catch-all mapping DB→UI). - workflow: story-tracking (statut BMAD vs narratif obsolète). - product: general (nouveau: doc feature store sans UI). - infra: NOUVEAU DOMAINE (traefik, tailscale, docker, docker-networking, reverse-proxy-paths, sidecar tailscale) + 00_INDEX.md. Autres: - 90_debug_et_postmortem.md: post-mortem réseau Docker partagé hors compose. - Rejeté 3 doublons (types enum contracts, getter PrismaService, $transaction). - Buffer 95_a_capitaliser.md purgé et restauré à son état initial. - _projects.conf: MAJ statuts epics + ajout app-rl799.
144 lines
5.1 KiB
Markdown
144 lines
5.1 KiB
Markdown
# Backend — Risques & vigilance : Redis
|
|
|
|
> Extrait de la base de connaissance Lead_tech. Voir `knowledge/backend/risques/README.md` pour l'index complet.
|
|
|
|
---
|
|
|
|
<a id="risque-redis-thrash-connexion"></a>
|
|
## 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
|
|
|
|
---
|
|
|
|
<a id="risque-entitlements-ttl-sla"></a>
|
|
## 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
|
|
|
|
---
|
|
|
|
<a id="risque-compteurs-inmemory"></a>
|
|
## 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
|
|
|
|
---
|
|
|
|
<a id="risque-ttl-redis-heure-locale"></a>
|
|
## 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
|
|
|
|
---
|
|
|
|
<a id="risque-compensation-incrby-non-atomique"></a>
|
|
## 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
|