Saltar al contenido
# 🏥 /health Endpoint Enhancement **Issue:** [P3.2](#) **Prioridad:** Media **Estimación:** 6-8 horas --- ## 🤔 ¿Qué hago? ¿Cómo lo hago? ¿Y para qué lo hago? El endpoint `/health` es minimal — solo `{"status": "ok"}`. En producción, necesitamos visibilidad de cada dependencia, SLOs y capacidad para diagnosticar degradación. **What:** Expandir `/health` con 4 secciones estructuradas (services, SLOs, capacity, metadata). **How:** 1. Probar latencia de cada dependencia (Qdrant, Anthropic, embeddings) 2. Calcular SLOs en tiempo real (availability, latency p99, cache hit rate) 3. Reportar utilización de recursos (memory, cache entries, knowledge entries) **Why:** Kubernetes readinessProbe/livenessProbe necesita detail; alerting basado en `/health` sin detail es ciego. --- ## 📊 Response Schema ```json { "status": "healthy", "timestamp": "2026-09-09T12:34:56.789Z", "version": "1.2.0", "uptime_seconds": 86400, "services": { "qdrant_cache": { "status": "UP", "latency_ms": 45, "last_check": "2026-09-09T12:34:50Z", "reachable": true }, "qdrant_knowledge": { "status": "UP", "latency_ms": 52, "last_check": "2026-09-09T12:34:50Z", "reachable": true }, "anthropic": { "status": "UP", "latency_ms": 250, "last_check": "2026-09-09T12:34:55Z", "reachable": true, "rate_limit_remaining": 4950 }, "embeddings": { "status": "UP", "model": "nomic-embed-text-v1.5", "inference_latency_ms": 120 } }, "slos": { "availability_pct": 99.82, "availability_status": "PASS", "latency_p99_ms": 1850, "latency_status": "PASS", "cache_hit_rate_pct": 72.3, "cache_hit_status": "PASS" }, "capacity": { "memory_mb": 234, "memory_limit_mb": 512, "memory_utilization_pct": 45.7, "cache_entries": 1200, "knowledge_entries": 450, "log_buffer_size": 2340 }, "checks_performed": { "qdrant_connectivity": "PASS", "anthropic_auth": "PASS", "embeddings_model_loaded": "PASS", "disk_space": "PASS", "certificate_expiry": "PASS" } } ``` --- ## 🟢 Status Codes | Status Code | Significado | Caso | | --- | --- | --- | | **200** | Healthy | Todas las dependencias UP, SLOs pass | | **200 + warn** | Degraded pero operable | 1 dependencia lenta, SLO amarillo | | **503** | Unhealthy | Dependencia DOWN, SLO rojo | --- ## 📝 Latencia de Health Check - `GET /health` debe responder en <100ms (sin esperar a Qdrant) - Latencies de dependencias calculadas en background (async cache) - Fallback: si check reciente existe (<5s), usarlo; sino, skip ese service **Implementación:** ```python @app.get("/health") async def health() -> dict: """Health check rápido con datos cacheados.""" cached = await get_health_cache() # <1ms (redis/memory) # Información que siempre tenemos (no requiere I/O) return { "status": cached.get("overall_status", "healthy"), "services": cached.get("services", {}), "slos": cached.get("slos", {}), "capacity": get_current_capacity(), # instant (memory.usage()) } ``` **Background task (cada 5 segundos):** ```python async def update_health_cache(): """Actualizar cache de health en background.""" while True: services = {} services["qdrant_cache"] = await check_qdrant_cache() # async services["qdrant_knowledge"] = await check_qdrant_knowledge() services["anthropic"] = await check_anthropic() cache_health = { "services": services, "slos": calculate_slos(), "timestamp": datetime.utcnow().isoformat(), } await set_health_cache(cache_health) await asyncio.sleep(5) ``` --- ## 🧪 Test Cases | Caso | Escenario | Expected | Status | | --- | --- | --- | --- | | TC1 | All services UP | status=healthy, 200 OK | ✅ | | TC2 | Qdrant DOWN | status=degraded, 503 | ✅ | | TC3 | Anthropic rate limited | rate_limit_remaining=0, warn | ✅ | | TC4 | Response <100ms | latency <100ms | ✅ | | TC5 | Memory at 90% | capacity.utilization_pct=90 | ✅ | | TC6 | SLO threshold violated | slos.status=FAIL | ✅ | | TC7 | No requests since startup | availability_pct=100 | ✅ | | TC8 | Schema validation | JSON schema pass | ✅ | | TC9 | Certificate expiry check | checks_performed.certificate_expiry | ✅ | | TC10 | Cache entries accurate | matches actual_cache.size | ✅ | | TC11 | Concurrent health requests | All get cache, no contention | ✅ | | TC12 | Health after dependency recovery | Status goes UP | ✅ | --- ## 🔗 Kubernetes Integration ```yaml # deployment.yaml livenessProbe: httpGet: path: /health port: 8000 initialDelaySeconds: 30 periodSeconds: 10 failureThreshold: 3 timeoutSeconds: 5 readinessProbe: httpGet: path: /health port: 8000 initialDelaySeconds: 10 periodSeconds: 5 failureThreshold: 2 timeoutSeconds: 3 # Ready si status code 200 y status != "DOWN" ``` --- ## 📊 Monitoreo en Prometheus ```promql # Latency de Qdrant probe_latency_ms{service="qdrant_cache"} # Tasa de aciertos de caché slo_cache_hit_rate_pct # Disponibilidad observada slo_availability_pct # Utilización de memoria health_memory_utilization_pct ``` --- ## ✅ Criterios de Aceptación - ✅ `/health` expone 4 secciones (services, SLOs, capacity, checks) - ✅ Responde en <100ms siempre - ✅ Detección automática de degradación - ✅ Latencies reales para cada dependencia - ✅ Tests: 12/12 pass - ✅ Schema JSON validable --- ## 📝 Próximos Pasos 1. Crear rama: `GH-XXX-health-enhancement` 2. Implementar background cache updater 3. Expandir `/health` endpoint 4. Escribir 12 tests 5. Integrar con Kubernetes probes 6. PR → merge 7. Deploy y validar latencias en staging