Parte de Limitador de Tasa Distribuido
Fallback local fail-open cuando Redis es inalcanzable
Rehíce el limitador para que una caída de Redis degrade a una ventana acotada por instancia en lugar de fallar cada solicitud, manteniendo viva la ingesta ante la pérdida de una dependencia.
La ruta de Redis era correcta para decidir cuotas, pero no para fallas de dependencia. Allow devolvía el error crudo de pipe.Exec al llamador, así que una caída corta de Redis hacía fallar cada handler protegido aunque el llamador hubiera preferido una tasa local acotada. Eso metía la disponibilidad de Redis dentro del contrato de vida de la ruta, demasiado estricto para una puerta de ingreso cuyo primer trabajo era rechazar carga antes de que el servicio agotara capacidad.
La nueva ruta mantiene separadas las decisiones de sobre-límite y las fallas de transporte. Si Redis responde, el sorted set compartido sigue decidiendo la solicitud. Si Redis no está alcanzable, el limitador usa una ventana deslizante en memoria con la misma clave de identidad y tamaño limit / expectedReplicas. La ventana local no se guarda como modo; cada llamada intenta Redis primero y cae a local solo para esa llamada. La recuperación queda atada al siguiente ida y vuelta exitoso con Redis, no a un temporizador.
func Allow(ctx context.Context, rdb *redis.Client, key string, limit int, window time.Duration) (bool, error) {
ok, err := allowRedis(ctx, rdb, key, limit, window)
if err == nil {
return ok, nil
}
// Falla de transporte: degradar, no fallar la solicitud de plano.
metrics.RedisFallback.Inc()
log.Warn("rate limiter: redis inalcanzable, usando ventana local", "key", key, "err", err)
return localWindow.Allow(key, limit/expectedReplicas, window), nil
}
El costo es que una caída de Redis puede admitir hasta una ventana local por proceso, así que el techo agregado puede superar el límite global configurado hasta que Redis vuelva. Esa desviación queda acotada por la cantidad de réplicas y por la misma duración de ventana que usa el limitador compartido. Los operadores reciben un contador separado, redis_fallback_total, que deja visible la degradación sin convertirla en errores de solicitud.