aws.crafter.run

Escala

Concurrencia reservada como aislamiento

También llamado Reserved concurrency

Repartir el bote de concurrencia de la cuenta para que ninguna función pueda quedarse con todo, y de paso poner un tope a las que hablan con dependencias que no escalan como Lambda.

IntermedioVerificado el

El problema

Un lunes por la mañana alguien lanza una importación masiva. Su función empieza a escalar —para eso está Lambda— y en pocos minutos hay novecientas invocaciones en vuelo.

Y entonces tu checkout empieza a devolver errores.

No has desplegado nada. El checkout no ha cambiado. Lo que ha pasado es que la concurrencia de Lambda es un bote de la cuenta y la región, no de la función: mil ejecuciones simultáneas por defecto, repartidas por orden de llegada entre todo lo que tengas desplegado.

Sin reservarEl importador se desboca y el checkout se queda sin cupoimportador · 900Con reservaCada crítica tiene su suelo; la desbocada, su techocheckout · 200importador · 300sin reservar · 5001 0001 000Regla dura: no se puede reservar más que el cupo sin reservar menos 100Esas 100 unidades quedan siempre para las funciones que no reservan nada
La concurrencia reservada es a la vez suelo y techo. Garantiza ese cupo a la función y le impide pasar de ahí. Lo que reserva deja de estar disponible para las demás aunque no lo use.

Es el modo de fallo más desagradable de Lambda porque cruza fronteras entre equipos. Una función de un proyecto que nadie mira puede tirar la API de producción de otro, y el equipo del checkout va a pasar la mañana buscando el problema en su código, donde no está.

Y hay una segunda cara del mismo problema. Lambda escala más rápido que casi cualquier cosa a la que llame: mil entornos cada diez segundos, por función. Si detrás hay una base de datos relacional con cien conexiones, o el API de un tercero con su propio límite, Lambda no está absorbiendo el pico: se lo está pasando entero al eslabón que no aguanta.

La solución

Repartir el bote explícitamente. La concurrencia reservada aparta un trozo para una función concreta, y hace dos cosas a la vez que conviene entender bien porque suelen contarse solo la mitad:

Reserved concurrency establece a la vez el máximo y el mínimo de instancias concurrentes asignadas a tu función. Cuando una función tiene concurrencia reservada, ninguna otra función puede usar esa concurrencia.

Es decir: es suelo y techo en la misma configuración.

Casi siempre quieres las dos cosas, pero por motivos distintos: reservas para proteger lo crítico, y limitas para proteger lo que está detrás.

Además no cuesta nada: a diferencia de la concurrencia aprovisionada, configurar concurrencia reservada no añade ningún cargo.

Cuándo usarlo

Cuándo NO usarlo

Cómo implementarlo

  1. Mide con ConcurrentExecutions por función, durante un periodo que incluya un pico real.
  2. Estima con la fórmula: concurrencia = peticiones por segundo × duración media en segundos.
  3. Reserva para lo crítico un valor por encima del pico observado.
  4. Limita lo peligroso al ritmo que aguante su dependencia, no al que aguante Lambda.
  5. Deja holgura sin reservar para todo lo demás — y recuerda que el sistema te obliga a dejar al menos 100.
  6. Vigila Throttles. Un tope sin alarma es un cuello de botella invisible.

El código

# Reservar 200 para el checkout: es su suelo y su techo a la vez.
aws lambda put-function-concurrency \
  --function-name checkout --reserved-concurrent-executions 200

# Apagar una funcion en seco, sin borrarla ni tocar sus disparadores.
aws lambda put-function-concurrency \
  --function-name importador --reserved-concurrent-executions 0

# Quitar el limite y devolver la funcion al bote comun.
aws lambda delete-function-concurrency --function-name importador
import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as cw from 'aws-cdk-lib/aws-cloudwatch';

// Critica: se reserva por encima del pico medido, para garantizar cupo.
const checkout = new lambda.Function(this, 'Checkout', {
  runtime: lambda.Runtime.NODEJS_22_X,
  handler: 'index.handler',
  code: lambda.Code.fromAsset('dist/checkout'),
  reservedConcurrentExecutions: 200,
});

// Peligrosa: se limita al ritmo que aguanta la base de datos de detras,
// no al que aguanta Lambda.
const importador = new lambda.Function(this, 'Importador', {
  runtime: lambda.Runtime.NODEJS_22_X,
  handler: 'index.handler',
  code: lambda.Code.fromAsset('dist/importador'),
  reservedConcurrentExecutions: 30,
});

// Un tope sin alarma es un cuello de botella invisible: lo que antes era
// un error ruidoso pasa a ser una cola que crece en silencio.
new cw.Alarm(this, 'AlarmaThrottles', {
  metric: importador.metricThrottles({ period: cdk.Duration.minutes(5) }),
  threshold: 1,
  evaluationPeriods: 2,
  comparisonOperator: cw.ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
  treatMissingData: cw.TreatMissingData.NOT_BREACHING,
});

Te va a morder

Coste

Configurar concurrencia reservada no cuesta nada. Es una de las pocas palancas de AWS que mejora la fiabilidad sin aparecer en la factura.

Lo que sí mueve dinero es su efecto indirecto, y en las dos direcciones:

Efecto Consecuencia en la factura
Techo en una función desbocada menos invocaciones — ahorra
Menos presión sobre la base de datos menos throttling y menos reintentos — ahorra
Backlog acumulado en colas más sondeos y más tiempo de retención — suma un poco
Cupo reservado y sin usar ningún cargo, pero capacidad desperdiciada

Ese último punto es el coste real y no se ve: reservar 300 para una función que usa 40 no te cobra nada, pero deja 260 unidades inutilizables para el resto de la cuenta. Es un coste de oportunidad, y solo aparece el día que otra función empieza a recibir Throttles sin motivo aparente.

Fuentes

Semántica de suelo y techo, ausencia de cargo, regla de reservar como mucho el cupo sin reservar menos 100, comportamiento del cupo reservado y sin usar, apagado con reservada a 0, límites propios de Amazon MQ y fórmula de estimación de concurrencia: Configuring reserved concurrency for a function. Límite por defecto de 1 000 ejecuciones concurrentes, velocidad de escalado y cuotas reducidas en cuentas nuevas: Lambda quotas. Retención por defecto y máxima de una cola SQS: Amazon SQS message quotas.

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é.