Qué pasa
Cuando un nodo desaparece de golpe, los pods de platform-api que vivían en él siguen en el EndpointSlice hasta que el control-plane los declara perdidos. Durante esa ventana, el usage hook de LiteLLM balancea hacia una réplica inalcanzable, se come el timeout y falla abierto: la petición se sirve sin comprobar el cap.
El fix de 2026-08-07 (project_nan_usage_gate_fail_open_fix) cubre el caso ordenado: preStop.sleep de 5s, maxUnavailable: 0, y CHECK_RETRIES=1. Eso resuelve los rollouts, donde el pod avisa antes de morir. No cubre la pérdida abrupta de un nodo, donde no hay preStop que valga.
Y el reintento no lo salva: CHECK_RETRIES=1 reintenta una vez, pero si el balanceo le devuelve el mismo endpoint muerto, el reintento se pierde igual.
Evidencia (2026-08-19)
19 eventos de nan_usage_gate_fail_open_total, todos con cause="platform_api_unreachable", en dos ventanas y ninguna otra:
| Momento (CEST) |
Eventos |
Qué pasaba |
| 19:13 |
3 |
reinicio de nan-eu001, que alojaba una réplica |
| 20:38-20:39 |
16 |
nan-eu007 aislado por firewall, alojaba otra réplica |
En ambos casos los eventos cesaron en cuanto el nodo volvió. nan_usage_gate_check_retry_total se quedó en 5 repartidos, muy por debajo de los 19 fail-open, lo que confirma que el reintento no está absorbiendo estos casos: si estuviera funcionando veríamos los dos contadores subir juntos.
Propuesta
Que el reintento excluya el endpoint que acaba de fallar en vez de rebalancear a ciegas. Opciones, de menos a más invasiva:
- Resolver los endpoints y reintentar contra otro explícitamente, en vez de volver a pegarle al Service y confiar en el balanceo.
- Subir
CHECK_RETRIES a 2 con backoff corto. Más probable acertar un endpoint vivo, pero sigue siendo probabilístico y sube el peor caso de latencia.
- Bajar el timeout de conexión (distinto del de lectura) para que un endpoint muerto se descarte rápido y quede tiempo para el reintento dentro del presupuesto actual.
La (1) es la correcta; la (3) es la más barata y ya ayudaría.
Sea cual sea, el test que lo demuestra es el mismo: matar un nodo con una réplica de platform-api y comprobar que fail_open_total no se mueve. Hoy sí se mueve.
Contexto
- El fail-open es deliberado y correcto: preferimos servir sin capar a cortar el servicio. El problema no es la política, es que la ventana es más ancha de lo necesario.
- Afecta solo a modelos capados de pago por token (
glm5.2, deepseek-v4-flash, mimo-v2.5), o sea gasto sin comprobar.
- La alerta
nan-usage-hook-fail-open-trickle usa increase(...[6h]) > 15, así que un pico puntual la deja disparada 6 horas. Eso es correcto por diseño, pero conviene saberlo al triarla: mira primero cuándo ocurrieron los eventos, no solo el total.
Qué pasa
Cuando un nodo desaparece de golpe, los pods de
platform-apique vivían en él siguen en el EndpointSlice hasta que el control-plane los declara perdidos. Durante esa ventana, el usage hook de LiteLLM balancea hacia una réplica inalcanzable, se come el timeout y falla abierto: la petición se sirve sin comprobar el cap.El fix de 2026-08-07 (
project_nan_usage_gate_fail_open_fix) cubre el caso ordenado:preStop.sleepde 5s,maxUnavailable: 0, yCHECK_RETRIES=1. Eso resuelve los rollouts, donde el pod avisa antes de morir. No cubre la pérdida abrupta de un nodo, donde no hay preStop que valga.Y el reintento no lo salva:
CHECK_RETRIES=1reintenta una vez, pero si el balanceo le devuelve el mismo endpoint muerto, el reintento se pierde igual.Evidencia (2026-08-19)
19 eventos de
nan_usage_gate_fail_open_total, todos concause="platform_api_unreachable", en dos ventanas y ninguna otra:En ambos casos los eventos cesaron en cuanto el nodo volvió.
nan_usage_gate_check_retry_totalse quedó en 5 repartidos, muy por debajo de los 19 fail-open, lo que confirma que el reintento no está absorbiendo estos casos: si estuviera funcionando veríamos los dos contadores subir juntos.Propuesta
Que el reintento excluya el endpoint que acaba de fallar en vez de rebalancear a ciegas. Opciones, de menos a más invasiva:
CHECK_RETRIESa 2 con backoff corto. Más probable acertar un endpoint vivo, pero sigue siendo probabilístico y sube el peor caso de latencia.La (1) es la correcta; la (3) es la más barata y ya ayudaría.
Sea cual sea, el test que lo demuestra es el mismo: matar un nodo con una réplica de platform-api y comprobar que
fail_open_totalno se mueve. Hoy sí se mueve.Contexto
glm5.2,deepseek-v4-flash,mimo-v2.5), o sea gasto sin comprobar.nan-usage-hook-fail-open-trickleusaincrease(...[6h]) > 15, así que un pico puntual la deja disparada 6 horas. Eso es correcto por diseño, pero conviene saberlo al triarla: mira primero cuándo ocurrieron los eventos, no solo el total.