Skip to content

ADR-0016: Capa de Resiliencia de Despliegue en Capa Gratuita

  • Estado: Accepted
  • Fecha: 2026-05-27 (registrado retroactivamente; la capa de resiliencia se publicó en una versión anterior)
  • Responsables de la decisión: Waldemar Szemat

La demo corre en Google Cloud Run como una única instancia scale-to-zero (ADR-0007); la misma imagen también corre en el objetivo secundario Hugging Face Spaces CPU Basic. La URL pública de la demo se comparte abiertamente. Una breve ráfaga de visitantes curiosos (una publicación que es recogida, una mención en una conferencia) puede disparar el tráfico más rápido de lo que el agente puede responder.

Sin protección, la instancia encola turnos hasta que o bien el bucle de eventos se queda atrás y los tiempos de espera caen en cascada, o bien el proxy de borde de la plataforma devuelve 502 Bad Gateway. Ambos resultados convierten un momento público en una demo rota.

¿Cómo protegemos a la instancia de las ráfagas de solicitudes concurrentes manteniendo el costo operativo de cerca de $0/mes a escala de demo y la simplicidad de una única instancia, sin introducir Redis?

  • Presupuesto operativo de cerca de $0/mes a escala de demo (ADR-0007): sin Redis, sin servicio de limitación de tasa administrado.
  • Simplicidad de una única instancia: el diseño de despliegue es una instancia (--max-instances 1); compartir el estado de limitación de tasa entre instancias no es una preocupación actual.
  • Semántica de reintento transparente: el cliente (la aplicación) debe saber cuándo reintentar. Retry-After es la cabecera de carga estructural.
  • La demo sobrevive a ráfagas de entrada idéntica: cuando diez espectadores hacen clic en el mismo escenario de ejemplo, la instancia debería computar la respuesta una sola vez.
  • Opción A: Sin protección. La instancia maneja cada solicitud hasta que se cae.
  • Opción B: Limitador de tasa de ventana deslizante respaldado por Redis + caché de respuestas.
  • Opción C: Limitador de tasa de ventana deslizante en proceso + caché de respuestas TTL en proceso, ambos basados en una firma de solicitud normalizada. Memoria acotada.
  • Opción D: Usar un límite de tasa a nivel de CDN (Cloudflare frente a la plataforma de hosting).

Opción elegida: Opción C — una capa de resiliencia en proceso con un limitador de tasa de ventana deslizante y una caché de respuestas TTL, ambos acotados y residentes en memoria. La razón individual de mayor carga estructural es el presupuesto de cerca de $0/mes: cualquier dependencia externa para la limitación de tasa viola la postura de despliegue. El diseño en proceso también coincide con la verdad de una única instancia: no hay una segunda instancia con la cual coordinarse.

Comportamiento:

  • El limitador de tasa se basa en la IP del cliente (consciente del proxy, de modo que la cabecera X-Forwarded-For se respeta detrás del proxy inverso de la plataforma). Una ventana deslizante con un conteo acotado de solicitudes por ventana rechaza las ráfagas con HTTP 429 y una pista Retry-After.
  • La caché de respuestas se basa en una tupla normalizada de (texto, configuración regional, modelo). Un TTL corto mantiene la memoria acotada. Las entradas idénticas dentro de la ventana del TTL devuelven la respuesta en caché, evitando un gasto redundante de LLM.
  • La caché NUNCA se usa para un turno pausado (HITL): un borrador pausado no es una respuesta final y no debe servirse a una sesión diferente como si lo fuera.
  • El limitador de tasa y la caché están implementados en un módulo de resiliencia dedicado.
  • Las pruebas unitarias cubren: la decisión de límite de tasa bajo ráfaga, un acierto de caché ante entrada idéntica, un fallo de caché ante una configuración regional o un modelo diferente, sin caché en los turnos pausados, y la propagación de Retry-After.
  • El limitador se aplica antes del trabajo del agente, y la caché se aplica alrededor de la invocación del agente.
  • La demo sobrevive a las ráfagas de tráfico sin infraestructura externa.
  • El tráfico de entrada idéntica (el patrón de “todos hicieron clic en el mismo ejemplo”) cuesta cero gasto de LLM en las repeticiones dentro del TTL de la caché.
  • La semántica de HTTP 429 + Retry-After es transparente y amable con el cliente.
  • El presupuesto de cerca de $0/mes a escala de demo se preserva.
  • La protección es por instancia. Un despliegue con escalado horizontal (múltiples instancias detrás de un balanceador de carga) necesitaría externalizar el limitador y la caché a Redis. El diseño es honesto sobre esta limitación, registrada como una brecha de preparación para producción en el informe de rendimiento.
  • El reinicio de la instancia pierde el estado de la caché. El tráfico de arranque en frío paga el costo completo de LLM hasta que la caché se calienta.
  • El limitador basado en IP es grueso: un NAT corporativo presenta a toda la organización como una única IP. El conteo de ventana acotada debe ajustarse para no penalizar el tráfico legítimo con NAT; el ajuste actual es intencionalmente indulgente.
  • La decisión de límite de tasa agrega una verificación en proceso por solicitud. El costo de latencia es de menos de un milisegundo.
  • La caché agrega estado en memoria. Un TTL acotado mantiene la huella de memoria manejable dentro de la memoria de la instancia.
  • Buena, porque cero infraestructura.
  • Mala, porque la instancia se cae bajo tráfico de ráfaga.
  • Mala, porque no hay semántica de 429 + Retry-After: el cliente ve un 502 del proxy de la plataforma.

Opción B: Limitador + caché respaldados por Redis

Section titled “Opción B: Limitador + caché respaldados por Redis”
  • Buena, porque sobrevive al escalado horizontal multi-instancia.
  • Mala, porque agrega una dependencia externa (instancia de Redis, gestión de secretos, salto de red).
  • Mala, porque entra en conflicto con el presupuesto de cerca de $0/mes.
  • Mala, porque es sobreingeniería para la realidad de una única instancia.

Opción C (elegida): Limitador + caché en proceso

Section titled “Opción C (elegida): Limitador + caché en proceso”
  • Buena, porque no hay dependencia externa.
  • Buena, porque coincide con la verdad de una única instancia.
  • Buena, porque es explícita, comprobable y autocontenida.
  • Mala, porque no escala más allá de una instancia (reconocido).
  • Buena, porque delega la protección al borde de Cloudflare.
  • Mala, porque no cubre la necesidad de la caché de respuestas.
  • Mala, porque en el objetivo secundario gratuito (Hugging Face Spaces) el tráfico se enruta directamente, sin control de DNS / dominio para poner Cloudflare al frente.