Saltar a contenido

ADR 009: Redis y requerimientos no funcionales

Estado

Aceptado — 2026-06 · Diseño de ingeniería (sin implementación aún)

Relacionado con ADR 001, ADR 007 y ADR 008 (idempotencia en POST).

Contexto

El marco técnico adopta prácticas NFR desde el desarrollo: rate limiting configurable por tenant, caché, health checks profundos e idempotencia en escrituras. Los umbrales numéricos (latencia, concurrencia, cuotas) se calibran en producción.

Hoy:

  • Redis 7 está en docker-compose.yml y CORTEX_REDIS_URL en settings.
  • RateLimitMiddleware es un stub que deja pasar todas las peticiones.
  • /ready devuelve {"status": "ready"} sin comprobar dependencias.
  • No hay cliente Redis en código Python ni patrón de idempotencia.

Decisión

Rate limiting

  • Implementar en RateLimitMiddleware con Redis: INCR cortex:rl:{tenant}:{window} + TTL 60s.
  • Límite por defecto: CORTEX_RATE_LIMIT_PER_MINUTE (120); override por tenant en fase 2 (config en PG o Redis hash).
  • Respuesta 429 con cuerpo ApiError (RATE_LIMIT_EXCEEDED) y header Retry-After.
  • Excluir: /health, /ready, /api/docs, /api/openapi.json.

Caché (fase 2 — documentar, no bloquear fase 1)

  • GET idempotentes de lectura: manifests UI, listados estáticos de recursos.
  • Clave: cortex:cache:{tenant}:{path_hash}; TTL corto (30–120s).
  • Invalidación en writes del mismo recurso.

Health checks

Ruta Comportamiento
GET /api/v1/health Liviano; proceso vivo
GET /api/v1/ready Comprueba Redis PING + PG SELECT 1 en tenant default

Respuesta /ready si falla dependencia: 503 con detalle por servicio.

Idempotencia

  • Header Idempotency-Key en POST /api/v1/booking/bookings (y escrituras críticas futuras).
  • Redis: cortex:idempotency:{tenant}:{key} → respuesta serializada, TTL 24h.
  • Replay: misma respuesta HTTP sin re-ejecutar lógica.

Leyenda: Rate limit y health checks con Redis y PG (módulo Dependiente). Estado: Diseño (stub actual).

flowchart LR
  API[FastAPI] --> RL[RateLimitMiddleware]
  RL --> Redis[(Redis 7)]
  API --> Ready["/ready"]
  Ready --> Redis
  Ready --> PG[(PostgreSQL)]

Leyenda: Middleware y /ready dependen de Redis y PostgreSQL. Estado: Diseño.

sequenceDiagram
  participant C as Cliente
  participant API as FastAPI
  participant Redis as Redis
  participant DB as PostgreSQL

  C->>API: POST /api/v1/booking/bookings Idempotency-Key
  API->>Redis: GET idempotency key
  alt clave existe
    Redis-->>API: respuesta cacheada
    API-->>C: 200 replay
  else clave nueva
    API->>DB: transacción crear
    API->>Redis: SET idempotency TTL 24h
    API-->>C: 201 created
  end

Leyenda: Idempotencia en POST booking vía Redis. Estado: Diseño.

Escalabilidad y stateless

  • API sin sesión server-side; estado en PG y Redis.
  • Réplicas horizontales detrás de Coolify/nginx sin sticky sessions.
  • Pool de conexiones async SQLAlchemy por proceso (ADR 007).

Fuera de alcance documentado

  • Umbrales numéricos de latencia/concurrencia (producción).
  • Reintentos idempotentes en cliente (patrón recomendado en OpenAPI, no código framework).
  • Rollback de despliegue (procedimiento ops Coolify).

Variables de entorno

Variable Default Rol
CORTEX_REDIS_URL redis://localhost:6379/0 Rate limit, idempotency, caché
CORTEX_RATE_LIMIT_PER_MINUTE 120 Cuota por tenant por minuto

Consecuencias

  • Positivas: NFRs medibles, alineado con correo técnico, base para calibrar en producción.
  • Negativas: Redis pasa a dependencia dura para rate limit; /ready más lento pero útil para orquestadores.

Referencias

  • framework/cortex_framework/tenant/rate_limit.py — stub actual
  • framework/cortex_framework/api/routes.py/health, /ready
  • ADR 008Idempotency-Key