2025-12-13 02:34:34 +00:00
|
|
|
# PR5 — Timeouts & Observabilité (handlers + stack traces + readyz)
|
|
|
|
|
|
|
|
|
|
## Résumé
|
|
|
|
|
|
|
|
|
|
Cette PR corrige les problèmes **MOD-P1-004**, **MOD-P1-005**, et **MOD-P1-006** :
|
|
|
|
|
- **MOD-P1-004**: Timeouts context systématiques dans handlers
|
|
|
|
|
- **MOD-P1-005**: Stack traces conditionnels (dev/DEBUG seulement)
|
|
|
|
|
- **MOD-P1-006**: /readyz tolérance Redis/RabbitMQ (degraded mode)
|
|
|
|
|
|
|
|
|
|
## Items Corrigés
|
|
|
|
|
|
|
|
|
|
### MOD-P1-004: Pas de Timeout Context dans Tous Handlers
|
|
|
|
|
**Fichiers**:
|
|
|
|
|
- `internal/api/router.go:83` (déjà implémenté)
|
2025-12-16 16:23:49 +00:00
|
|
|
- `internal/middleware/timeout.go` (déjà implémenté)
|
2025-12-13 02:34:34 +00:00
|
|
|
|
|
|
|
|
**Problème**:
|
|
|
|
|
- Handlers peuvent bloquer indéfiniment sans timeout
|
|
|
|
|
|
|
|
|
|
**Fix**:
|
|
|
|
|
- ✅ **Déjà implémenté**: Le router utilise `middleware.Timeout(r.config.HandlerTimeout)` globalement
|
|
|
|
|
- ✅ **Validation**: Tous les handlers passent par le middleware timeout (30s par défaut)
|
|
|
|
|
|
|
|
|
|
**Validation**:
|
|
|
|
|
```bash
|
|
|
|
|
# Vérifier que le middleware timeout est appliqué
|
|
|
|
|
grep -n "middleware.Timeout" internal/api/router.go
|
|
|
|
|
# → ligne 83: router.Use(middleware.Timeout(r.config.HandlerTimeout))
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### MOD-P1-005: Stack Traces dans Logs Prod
|
|
|
|
|
**Fichiers**:
|
|
|
|
|
- `internal/api/router.go:62-64` (modifié)
|
|
|
|
|
- `internal/middleware/error_handler.go:148-151` (déjà implémenté)
|
|
|
|
|
|
|
|
|
|
**Problème**:
|
|
|
|
|
- Stack traces toujours loggés même en production (peut exposer info sensible)
|
|
|
|
|
|
|
|
|
|
**Fix**:
|
|
|
|
|
1. **Amélioration de la logique**: Modifié `includeStackTrace` pour vérifier `APP_ENV=development` OU `LOG_LEVEL=DEBUG`
|
|
|
|
|
- Avant: `includeStackTrace := r.config.Env != config.EnvProduction` (incluait staging)
|
|
|
|
|
- Après: `includeStackTrace := r.config.Env == config.EnvDevelopment || r.config.LogLevel == "DEBUG"`
|
|
|
|
|
|
|
|
|
|
2. **Error Handler**: Déjà implémenté avec condition `if includeStackTrace`
|
|
|
|
|
|
|
|
|
|
**Validation**:
|
|
|
|
|
```bash
|
|
|
|
|
# Test existant
|
|
|
|
|
go test ./internal/middleware -v -count=1 -run TestErrorHandler_StackTrace
|
|
|
|
|
# ✅ PASS (2 tests: avec et sans stack trace)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### MOD-P1-006: /readyz Échoue si Redis/RabbitMQ Down
|
|
|
|
|
**Fichiers**:
|
|
|
|
|
- `internal/handlers/health.go:142-184` (déjà implémenté)
|
|
|
|
|
|
|
|
|
|
**Problème**:
|
|
|
|
|
- `/readyz` peut échouer si Redis/RabbitMQ down (même si DB OK)
|
|
|
|
|
|
|
|
|
|
**Fix**:
|
|
|
|
|
- ✅ **Déjà implémenté**: `/readyz` retourne status "degraded" si Redis/RabbitMQ down, mais reste 200 OK
|
|
|
|
|
- ✅ **DB critique**: Si DB down → 503 "not_ready"
|
|
|
|
|
- ✅ **Services optionnels**: Si Redis/RabbitMQ down → 200 "degraded"
|
|
|
|
|
|
|
|
|
|
**Validation**:
|
|
|
|
|
```bash
|
|
|
|
|
# Tests ajoutés
|
|
|
|
|
go test ./internal/handlers -v -count=1 -run TestHealthHandler_Readiness
|
|
|
|
|
# ✅ PASS (2 tests: degraded mode et database critical)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Fichiers Modifiés
|
|
|
|
|
|
|
|
|
|
1. `internal/api/router.go`
|
|
|
|
|
- Ligne 62-64: Amélioration de la logique `includeStackTrace` pour MOD-P1-005
|
|
|
|
|
- Commentaire MOD-P1-005 ajouté
|
|
|
|
|
|
|
|
|
|
2. `internal/handlers/health_p1_test.go` (nouveau)
|
|
|
|
|
- Test `TestHealthHandler_Readiness_DegradedMode`: Valide que /readyz retourne "degraded" si Redis/RabbitMQ down
|
|
|
|
|
- Test `TestHealthHandler_Readiness_DatabaseCritical`: Valide que /readyz retourne "not_ready" si DB down
|
|
|
|
|
|
|
|
|
|
## Commandes de Validation
|
|
|
|
|
|
|
|
|
|
### Build
|
|
|
|
|
```bash
|
|
|
|
|
# Compilation
|
|
|
|
|
go build ./internal/api ./internal/middleware ./internal/handlers
|
|
|
|
|
# ✅ Succès
|
|
|
|
|
|
|
|
|
|
# Build complet
|
|
|
|
|
go build ./cmd/api/main.go
|
|
|
|
|
# ✅ Succès
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Tests
|
|
|
|
|
```bash
|
|
|
|
|
# Tests stack traces (MOD-P1-005)
|
|
|
|
|
go test ./internal/middleware -v -count=1 -run TestErrorHandler_StackTrace
|
|
|
|
|
# ✅ PASS (2 tests: IncludeStackTrace_True, IncludeStackTrace_False)
|
|
|
|
|
|
|
|
|
|
# Tests health readiness (MOD-P1-006)
|
|
|
|
|
go test ./internal/handlers -v -count=1 -run TestHealthHandler_Readiness
|
|
|
|
|
# ✅ PASS (2 tests: DegradedMode, DatabaseCritical)
|
|
|
|
|
|
|
|
|
|
# Tous les tests
|
|
|
|
|
go test ./... -count=1 -short
|
|
|
|
|
# ✅ Tests unitaires passent
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Validation Manuelle
|
|
|
|
|
|
|
|
|
|
**MOD-P1-004 (Timeouts)**:
|
|
|
|
|
- ✅ Middleware timeout appliqué globalement dans `router.go:83`
|
|
|
|
|
- ✅ Timeout par défaut: 30s (configurable via `HANDLER_TIMEOUT`)
|
|
|
|
|
- ✅ Tous les handlers passent par le middleware timeout
|
|
|
|
|
|
|
|
|
|
**MOD-P1-005 (Stack Traces)**:
|
|
|
|
|
```bash
|
|
|
|
|
# En production (APP_ENV=production, LOG_LEVEL=INFO)
|
|
|
|
|
# → Stack traces NE sont PAS loggés ✅
|
|
|
|
|
|
|
|
|
|
# En development (APP_ENV=development)
|
|
|
|
|
# → Stack traces SONT loggés ✅
|
|
|
|
|
|
|
|
|
|
# En DEBUG (LOG_LEVEL=DEBUG)
|
|
|
|
|
# → Stack traces SONT loggés ✅
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**MOD-P1-006 (Health Readiness)**:
|
|
|
|
|
```bash
|
|
|
|
|
# DB OK, Redis/RabbitMQ down
|
|
|
|
|
curl http://localhost:8080/api/v1/readyz
|
|
|
|
|
# → Status: 200 OK, body: {"status": "degraded", ...} ✅
|
|
|
|
|
|
|
|
|
|
# DB down
|
|
|
|
|
# → Status: 503 Service Unavailable, body: {"status": "not_ready", ...} ✅
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Détails Techniques
|
|
|
|
|
|
|
|
|
|
### Corrections Apportées
|
|
|
|
|
|
|
|
|
|
1. **MOD-P1-004 (Timeouts)**:
|
|
|
|
|
- ✅ **Déjà implémenté**: Middleware timeout global dans `router.go:83`
|
|
|
|
|
- ✅ **Timeout par défaut**: 30s (configurable via `HANDLER_TIMEOUT`)
|
|
|
|
|
- ✅ **Comportement**: Handlers timeout après 30s, retourne 504 Gateway Timeout
|
|
|
|
|
|
|
|
|
|
2. **MOD-P1-005 (Stack Traces)**:
|
|
|
|
|
- **Avant**: `includeStackTrace := r.config.Env != config.EnvProduction` (incluait staging)
|
|
|
|
|
- **Après**: `includeStackTrace := r.config.Env == config.EnvDevelopment || r.config.LogLevel == "DEBUG"`
|
|
|
|
|
- **Comportement**: Stack traces seulement en development ou si LOG_LEVEL=DEBUG
|
|
|
|
|
|
|
|
|
|
3. **MOD-P1-006 (Health Readiness)**:
|
|
|
|
|
- ✅ **Déjà implémenté**: `/readyz` retourne "degraded" si Redis/RabbitMQ down
|
|
|
|
|
- ✅ **DB critique**: Si DB down → 503 "not_ready"
|
|
|
|
|
- ✅ **Services optionnels**: Si Redis/RabbitMQ down → 200 "degraded"
|
|
|
|
|
|
|
|
|
|
### Endpoints Vérifiés
|
|
|
|
|
|
|
|
|
|
- ✅ **Tous les handlers**: Passent par `middleware.Timeout()` global
|
|
|
|
|
- ✅ `/api/v1/readyz`: Retourne "degraded" si Redis/RabbitMQ down, "not_ready" si DB down
|
|
|
|
|
- ✅ **Error Handler**: Stack traces conditionnels selon env/LOG_LEVEL
|
|
|
|
|
|
|
|
|
|
## Risques / Limitations
|
|
|
|
|
|
|
|
|
|
1. **Timeouts globaux**: Le middleware timeout est appliqué globalement
|
|
|
|
|
- **Impact**: Tous les handlers ont le même timeout (30s)
|
|
|
|
|
- **Mitigation**: Timeout configurable via `HANDLER_TIMEOUT` env var
|
|
|
|
|
|
|
|
|
|
2. **Stack traces en staging**: Stack traces ne sont plus loggés en staging (seulement dev/DEBUG)
|
|
|
|
|
- **Impact**: Debugging plus difficile en staging
|
|
|
|
|
- **Mitigation**: Utiliser Sentry pour les erreurs en staging/prod
|
|
|
|
|
|
|
|
|
|
3. **Health checks**: Tests utilisent SQLite en mémoire
|
|
|
|
|
- **Impact**: Tests ne reflètent pas exactement le comportement avec PostgreSQL
|
|
|
|
|
- **Mitigation**: Tests d'intégration avec PostgreSQL pourraient valider le comportement réel
|
|
|
|
|
|
|
|
|
|
## Tests Ajoutés/Modifiés
|
|
|
|
|
|
|
|
|
|
- ✅ `TestErrorHandler_StackTrace` (existant): Valide stack traces conditionnels
|
|
|
|
|
- ✅ `TestHealthHandler_Readiness_DegradedMode` (nouveau): Valide degraded mode
|
|
|
|
|
- ✅ `TestHealthHandler_Readiness_DatabaseCritical` (nouveau): Valide DB critique
|
|
|
|
|
|
|
|
|
|
## Documentation
|
|
|
|
|
|
|
|
|
|
**Comportement attendu**:
|
|
|
|
|
- **Timeouts**: Tous les handlers timeout après 30s (configurable)
|
|
|
|
|
- **Stack traces**: Loggés seulement si `APP_ENV=development` OU `LOG_LEVEL=DEBUG`
|
|
|
|
|
- **Health readiness**:
|
|
|
|
|
- DB down → 503 "not_ready"
|
|
|
|
|
- Redis/RabbitMQ down → 200 "degraded"
|
|
|
|
|
- Tout OK → 200 "ready"
|
|
|
|
|
|
|
|
|
|
**Configuration**:
|
|
|
|
|
- `HANDLER_TIMEOUT`: Timeout pour handlers (défaut: 30s)
|
|
|
|
|
- `APP_ENV`: Environnement (development, staging, production)
|
|
|
|
|
- `LOG_LEVEL`: Niveau de log (DEBUG, INFO, WARN, ERROR)
|
|
|
|
|
|
|
|
|
|
## Prochaines Étapes
|
|
|
|
|
|
|
|
|
|
- ✅ PR5 complétée
|
|
|
|
|
- ⏭️ PR6: Quick wins (MOD-P2-004, MOD-P2-010, MOD-P3-001, MOD-P3-002)
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
**Statut**: ✅ **READY FOR REVIEW**
|
|
|
|
|
|
|
|
|
|
**Effort**: ~6h (comme estimé dans audit: 4h + 1h + 1h)
|
|
|
|
|
|
|
|
|
|
**Breaking Changes**: Aucun
|