aws.crafter.run

Frontera

Throttling y rate limiting

También llamado Rate limiting

Rechazar en la puerta el tráfico que no cabe, para que el rechazo sea barato y ocurra donde tú lo decides en lugar de caro y donde lo decida el primer servicio que se rompa.

IntermedioVerificado el
Ver .md

El problema

Los límites por defecto de tu arquitectura no están alineados entre sí, y el más generoso de todos es justo el primero: el que da a internet.

El problema es que están en unidades distintas y por eso nadie los compara. API Gateway habla de peticiones por segundo, Lambda de ejecuciones concurrentes y DynamoDB de unidades de escritura. Puestos en la misma unidad, la forma es incómoda:

API Gateway10 000 RPScuenta y regiónLambda5 000 RPS1 000 concurrentes ÷ 200 msDynamoDB1 000 RPSuna sola partición↑ lo que de verdad cabe
La puerta es más ancha que la habitación. Los límites por defecto no están alineados entre sí, y el más generoso es el primero — el que da a internet. Sin un throttle propio, API Gateway acepta encantado diez veces el tráfico que tu partición puede escribir, y tú pagas las diez veces.

La conversión que falta es la ley de Little, que en serverless se usa a diario:

concurrencia ≈ peticiones por segundo × duración media en segundos

Con 1 000 concurrentes y 200 ms de duración, tu Lambda sostiene 5 000 peticiones por segundo. Y una partición de DynamoDB escribe 1 000 por segundo, y ese número no se amplía con un ticket.

Así que la puerta admite diez veces lo que cabe en la habitación. Y sin un throttle propio, lo que pasa cuando llega el pico no es un rechazo limpio, sino esto:

Y hay un agravante que sorprende a todo el mundo la primera vez: los 10 000 RPS son de la cuenta y de la región, sumando todas tus APIs y todas tus etapas. Un test de carga contra staging puede tirar producción si comparten cuenta.

La solución

Poner tu propio límite delante, y ponerlo donde el rechazo es barato. API Gateway lo implementa con el algoritmo del cubo de fichas, que tiene dos parámetros — y la mitad de la gente configura solo uno.

rate · se reponen 100 fichas por segundoburst · caben 200 fichases el tamaño del cubo, no una toleranciapeticionescada una coge una fichahay ficha → pasacubo vacío →429 Too Many Requests
Son dos parámetros y casi todo el mundo configura uno. El rate es el ritmo sostenido; el burst es cuántas peticiones pueden llegar en el mismo instante. Con rate 100 y burst 200, un cliente puede meterte 200 de golpe y luego 100 por segundo — y quien tiene que aguantar esas 200 es lo que haya detrás.

La distinción no es académica. Con rate = 100 y burst = 200, un cliente que llevaba un rato callado puede meterte 200 peticiones simultáneas y luego seguir a 100 por segundo. Si dimensionaste lo de detrás para 100, ese pico inicial va a doler. El burst es un préstamo, y hay que poder devolverlo.

Dónde se pone el límite

API Gateway aplica cuatro tipos de ajuste, y los aplica en este orden:

Orden Ajuste Alcance
1 Por cliente o por método en un usage plan por API key
2 Por método en una etapa esa etapa
3 Por cuenta y región todas tus APIs
4 Límites de AWS por región todos los clientes de AWS

Cada nivel está acotado por el de arriba: los límites por cliente no pueden superar los de la cuenta, y los de etapa y método no pueden superar los de AWS. El que manda en la práctica es siempre el más restrictivo que se toque primero.

Cuándo usarlo

En toda API pública, siempre. No hay una versión razonable de “mi API no necesita límite”: el valor por defecto es el de la cuenta, y ese valor no lo elegiste tú.

Los casos concretos donde el patrón es la respuesta:

Y donde no es la respuesta:

Para autenticar. Una API key de API Gateway identifica a un cliente para contarle las peticiones. No es una credencial: viaja en una cabecera, no caduca sola y no autoriza nada. Si la usas como autenticación, tienes una API abierta con un contador.

Para limitar por IP o por usuario. API Gateway agrupa por API key, por método y por etapa — no por quien llama. El rate limiting por dirección IP es de AWS WAF, con una rate-based rule.

Cuando el trabajo puede esperar. Si la petición se puede encolar, tirarla es la peor opción disponible. El throttle es para lo que hay que contestar ahora; para lo demás está la cola.

Cómo implementarlo

Primero el número, después la consola

El error de método es empezar por el ajuste. El límite correcto se calcula desde atrás: se busca el recurso más estrecho del camino y se sube hasta la puerta.

Un ejemplo completo, que es lo que realmente hay que hacer con lápiz y papel:

  1. El cuello. La tabla escribe en una sola partición: 1 000 escrituras/s.
  2. Margen. Reserva un 30% para picos y reintentos: 700/s de objetivo.
  3. Escrituras por petición. Si cada llamada escribe 2 elementos, la puerta admite 350 peticiones/s.
  4. El rate. rate = 350.
  5. El burst. Lo que tu cuello aguanta concurrentemente, no el doble por inercia. Si no tienes un motivo para más, burst = rate es un punto de partida honesto.
  6. Comprobación con la ley de Little. 350 peticiones/s × 200 ms = 70 de concurrencia. Ése es el número que va en la reservedConcurrency de la función, con su margen.

Fíjate en que el número final —350— no se parece en nada a los 10 000 del valor por defecto. Ésa es la distancia que estabas dejando abierta.

Los tres ajustes de un usage plan

Ajuste Qué controla Unidad
Rate ritmo sostenido peticiones por segundo
Burst capacidad del cubo peticiones simultáneas
Quota volumen total peticiones por día, semana o mes

quota es de otra naturaleza: no protege de un pico, protege de un abuso sostenido y sirve para vender planes. Un cliente puede estar dentro de su rate y agotar su quota del mes en una tarde.

Limitar por IP: AWS WAF

Cuando lo que necesitas es “no más de N peticiones desde la misma IP”, la herramienta es una rate-based rule de AWS WAF:

Con una advertencia que AWS escribe explícitamente: aplica el límite cerca del valor que configuras, pero no garantiza una coincidencia exacta. Es una protección, no un contador.

El código

Throttle de etapa y de método con la CLI. Es el ajuste que falta en el 90% de las APIs desplegadas:

aws apigateway update-stage \
  --rest-api-id abc123 \
  --stage-name prod \
  --patch-operations \
    op=replace,path=/*/*/throttling/rateLimit,value=350 \
    op=replace,path=/*/*/throttling/burstLimit,value=350

El /*/*/ significa todos los recursos, todos los métodos. Para un método concreto se sustituye por la ruta y el verbo — y ahí es donde se protege el endpoint caro sin castigar a los demás:

op=replace,path=/~1pedidos/POST/throttling/rateLimit,value=50

La ~1 es un / escapado: ~1pedidos es el recurso /pedidos. Es la parte de esta API que más tiempo hace perder.

Un usage plan con sus tres ajustes:

aws apigateway create-usage-plan \
  --name "plan-basico" \
  --throttle rateLimit=100,burstLimit=100 \
  --quota limit=100000,period=MONTH \
  --api-stages apiId=abc123,stage=prod

Y el cliente. Un 429 que nadie interpreta no reduce el tráfico. Lo mínimo exigible al consumidor es distinguirlo de un error de verdad:

resp = requests.post(url, json=carga)

if resp.status_code == 429:
    # No es un fallo: es la puerta diciendo "ahora no".
    # Reintentar sin backoff convierte el límite en un bucle cerrado.
    esperar_con_backoff_y_jitter(intento)
elif resp.status_code >= 500:
    ...

Te va a morder

Configurar el rate y olvidar el burst. Es el error número uno. Pones rate = 100, duermes tranquilo, y un cliente te mete el burst entero de golpe. Si no sabes qué burst quieres, ponlo igual al rate y súbelo cuando tengas un motivo.

Creer que el límite es por API. Los 10 000 RPS son por cuenta y región. Comparten ese número producción, staging, la API interna y la que montaste para una demo. Es un argumento fuerte para separar entornos en cuentas distintas.

Contar con que tu región va a 10 000. En catorce regiones el valor por defecto es 2 500, con burst 1 250.

Pedir un burst mayor. El burst lo fija AWS a partir del RPS de tu cuenta; no es un parámetro que se solicite por separado. Si necesitas más ráfaga, lo que se pide es más RPS.

Poner el throttle y no mirarlo. Un límite bien puesto rechaza tráfico legítimo de vez en cuando, y eso hay que verlo. Sin una alarma sobre 4XXError y sobre el conteo de peticiones rechazadas, la diferencia entre “el límite está funcionando” y “llevo tres semanas rechazando al 20% de mis clientes” es invisible.

Usar API keys como autenticación. Identifican, no autentican. La autorización va en un autorizador —JWT, Lambda authorizer— y la API key solo dice a qué usage plan perteneces.

Olvidar que el 429 tiene que enseñar algo. Devuelve Retry-After si puedes, y documenta los límites. Un cliente que no sabe cuál es su límite no puede respetarlo, y acabará descubriéndolo a base de reintentar.

Coste

La aritmética de este patrón es la más favorable de todo el catálogo, porque lo que se ahorra es todo lo que había detrás:

Dónde se rechaza Qué has pagado ya
En la puerta (429) la petición de API Gateway
En Lambda (concurrencia agotada) + la invocación y su duración
En DynamoDB (throttling) + la escritura fallida y los reintentos del SDK
En el cliente (500 tras el timeout) + la latencia, y el reintento del cliente

Cada fila añade coste y empeora la señal: el mismo exceso de tráfico se presenta como un 429 claro en la primera y como una avería tuya en la última.

Y hay un ahorro que no está en la factura de AWS: el throttle convierte un incidente en una decisión. Sin él, cuando llega el pico, lo que se rompe lo decide el azar. Con él, sabes exactamente qué se va a rechazar y a partir de qué número.

Fuentes

El algoritmo del cubo de fichas, el significado de rate como ritmo de reposición y de burst como capacidad del cubo, el orden en que se aplican los cuatro tipos de ajuste, que los límites por cliente no pueden superar los de la cuenta, que el límite de cuenta se examina sobre todas las APIs de la cuenta y la región, la respuesta 429 Too Many Requests y la advertencia de que throttles y quotas se aplican en la medida de lo posible y deben entenderse como objetivos y no como techos garantizados: Throttle requests to your REST APIs — Amazon API Gateway. Los 10 000 RPS por cuenta y región, los 2 500 de catorce regiones, el burst de 5 000 y 1 250 y que lo determina AWS a partir del RPS: Amazon API Gateway quotas and important notes. El límite de 1 000 ejecuciones concurrentes por región: Lambda quotas. Las 1 000 unidades de escritura por segundo de una partición: Amazon DynamoDB service quotas. La ventana de evaluación de 60, 120, 300 o 600 segundos con 300 por defecto, el límite mínimo de 10 peticiones y la advertencia de que WAF aplica el límite cerca del valor configurado sin garantizar una coincidencia exacta: Rate-based rule high-level settings in AWS WAF.

Patrones relacionados

Un enlace sin la relación nombrada es un "ver también". Aquí cada uno dice qué relación tiene y por qué.