Modelo mental
La pregunta con la que casi todo el mundo llega a Lambda es “¿cuántas peticiones por segundo aguanta?”, y esa pregunta no tiene respuesta, porque Lambda no escala por peticiones por segundo. Escala por concurrencia: cuántas invocaciones hay en vuelo simultáneamente.
La conversión es una multiplicación y conviene tenerla en los dedos:
concurrencia ≈ peticiones por segundo × duración media en segundos
100 peticiones por segundo que tardan 200 ms son 20 de concurrencia. Las mismas 100 peticiones, si la duración se va a 2 segundos, son 200. Tu función se ha vuelto diez veces más cara de escalar sin que el tráfico se moviera. Por eso una dependencia lenta no solo hace tu API lenta: te acerca al techo de la cuenta.
Y ese techo —1 000 concurrentes por defecto— es de la región, no de la función. Todas tus funciones comparten el mismo bote. Una que se atasca llamando a un servicio caído puede acumular cientos de invocaciones esperando y dejar sin cupo al resto de tu sistema, que estaba perfectamente sano. Es el modo de fallo más desagradable de Lambda porque cruza fronteras entre equipos.
La defensa es la concurrencia reservada: apartar un trozo del bote para una función. Tiene doble efecto y los dos son útiles: le garantiza ese cupo y, a la vez, le impide pasar de ahí.
El entorno de ejecución sobrevive
Entre invocaciones, tu proceso sigue vivo. Lo que está fuera del handler se ejecuta una vez por entorno, no una vez por invocación, y de ahí salen dos cosas:
- El cold start, que es el tiempo de arrancar un entorno nuevo. Solo lo pagan las invocaciones que no encuentran uno libre.
- La optimización más rentable que existe en Lambda: crear los clientes del SDK, abrir las conexiones y cargar la configuración fuera del handler. Se hace una vez y lo aprovechan las cientos de invocaciones siguientes.
/tmp también persiste entre invocaciones del mismo entorno. Es útil como caché
y es una trampa si supones que está vacío.
Cómo te factura
Dos conceptos que se suman:
| Concepto | Precio aproximado |
|---|---|
| Invocaciones | ~0,20 USD / millón |
| Duración | por GB-segundo, según memoria configurada |
| Concurrencia aprovisionada | se paga esté ociosa o no |
Lo interesante es que subir la memoria puede salir gratis. Como AWS asigna CPU en proporción y la duración se factura en GB-segundo, si doblas la memoria y la función tarda la mitad, el coste de duración es idéntico — y tu latencia se ha reducido a la mitad. Por debajo de 1 769 MB tienes menos de una vCPU, así que cualquier función que haga trabajo de CPU de verdad y esté en 256 MB casi seguro está pagando más por ir despacio.
La conclusión práctica: medir con distintas memorias no es una micro- optimización, es de las primeras cosas que hacer. La intuición de “menos memoria, más barato” es falsa en cuanto la función hace algo más que esperar.
La concurrencia aprovisionada es el otro lado: elimina el cold start pero se factura por tenerla reservada, haya tráfico o no. Es un coste fijo dentro de un servicio que vendes como coste variable.
Cuándo NO usarlo
- El trabajo pasa de 15 minutos. El timeout es un muro que no se levanta. Para procesos largos: Step Functions troceando, Fargate o Batch.
- Carga alta, constante y predecible. El modelo de Lambda paga un sobreprecio por invocación a cambio de escalar a cero. Si nunca escalas a cero y el tráfico es plano, un contenedor siempre encendido sale más barato.
- El p99 no tolera cold starts y no quieres pagar concurrencia aprovisionada.
- Necesitas conexiones persistentes de larga duración, GPU, o más de 10 240 MB de memoria.
- Dependes de estado en memoria entre invocaciones. El entorno se reutiliza, pero no está garantizado: cachear ahí está bien, depender de ello no.
Errores comunes
Crear el cliente del SDK dentro del handler. Se paga la construcción, el TLS y la resolución de credenciales en cada invocación en vez de una vez por entorno. Es el error de rendimiento más común y el más fácil de arreglar: subir dos líneas de sitio.
Dar por hecho los 1 000 de concurrencia en una cuenta nueva. AWS lo dice explícitamente: las cuentas nuevas arrancan con cuotas reducidas y las sube automáticamente según el uso. Tu prueba de carga en la cuenta de sandbox no mide lo que crees.
No poner concurrencia reservada en las funciones peligrosas. La que consume una cola con un backlog enorme, o la que llama a un tercero lento, son las candidatas a comerse el bote entero.
Confundir los dos límites de payload. Síncrono son 6 MB; asíncrono, 1 MB. El mismo evento que funciona detrás de API Gateway puede ser rechazado invocando la función de forma asíncrona.
Chocar con los 250 MB descomprimidos. El paquete comprimido son 50 MB, pero el límite que se alcanza de verdad es el de 250 MB descomprimido incluyendo las capas. Un par de librerías de ciencia de datos y ya estás fuera.
Meter la configuración en variables de entorno. El total son 4 KB sumando todas. Es menos de lo que parece en cuanto hay un par de ARN largos y una clave pública.
Desplegar cien funciones a la vez. El resto del plano de control va a 15 peticiones por segundo en total entre todas las APIs, y no se amplía. Un despliegue masivo se estrangula ahí, y el error que ves no dice nada de eso.
Fuentes
Todas las cuotas —concurrencia, timeout, memoria, payloads síncrono y
asíncrono, paquetes de despliegue, /tmp, variables de entorno, capas, ancho de
banda y límites del plano de control—, la relación entre memoria y vCPU y el
aviso sobre cuotas reducidas en cuentas nuevas:
Lambda quotas.
Los importes proceden de la página de precios de AWS, que se renderiza
dinámicamente y no pudo citarse literalmente: trátalos como orden de magnitud.