Módulo 3: Serverless & Lambda for AI
4. Cold Starts en AI
Descripción
En esta cápsula vas a confrontar el problema más discutido de Lambda para AI: los cold starts. No con opiniones — con números reales. Vas a entender qué es un cold start, medir su impacto con diferentes dependencias (openai vs langchain vs numpy+pandas), y aprender estrategias concretas de mitigación. Al terminar, podrás calcular si el cold start de tu función es aceptable para tu caso de uso y qué hacer si no lo es.
Contexto: Las cápsulas anteriores te enseñaron a escribir una función Lambda para AI (02) y a empaquetarla como zip o container (03). En ambos casos mencionamos cold starts como trade-off. Aquí profundizas: ¿cuánto duran realmente? ¿Por qué son peores para AI? ¿Cómo los mitigas sin gastar una fortuna? Este es un problema de ingeniería, no una limitación abstracta.
Qué Es un Cold Start
El concepto
Un cold start ocurre cuando Lambda necesita crear un nuevo container para ejecutar tu función. Esto pasa cuando:
- Primera invocación después de deployar
- No hay containers disponibles (todos están ocupados con otras invocaciones)
- Container expirado (Lambda recicla containers inactivos después de ~5-15 minutos)
- Scaling up (tráfico aumenta y Lambda necesita más containers)
Cold start (container nuevo):
┌─────────────────────────────────────────────────┐
│ 1. Provisionar container │ ~200-500ms
│ 2. Descargar código/imagen │ ~100ms-3s
│ 3. Inicializar Python runtime │ ~100-200ms
│ 4. Ejecutar código fuera del handler (imports) │ ~200ms-10s
│ 5. Ejecutar handler │ Variable
└─────────────────────────────────────────────────┘
Warm start (container existente):
┌─────────────────────────────────────────────────┐
│ 5. Ejecutar handler │ Variable
└─────────────────────────────────────────────────┘
La diferencia clave: en un warm start, los pasos 1-4 se saltan. El container ya existe, el runtime está cargado, tus imports están en memoria. Solo se ejecuta el handler.
Por qué importa más para AI
Para una función web típica (retorna HTML, procesa un form), un cold start de 500ms es imperceptible. Para una función AI, el cold start se suma al tiempo de inferencia:
Función web típica:
├── Cold start: 500ms
├── Handler: 50ms
└── Total: 550ms (usuario espera ~0.5s)
Función AI (openai SDK):
├── Cold start: 1.5s
├── Handler: 3s (llamada a GPT-4o-mini)
└── Total: 4.5s (usuario espera ~4.5s)
Función AI (langchain + deps):
├── Cold start: 8s
├── Handler: 3s (llamada a GPT-4o-mini)
└── Total: 11s (usuario piensa que la app se rompió)
El handler tarda lo mismo en ambos casos (la llamada al LLM es la misma). La diferencia es que con dependencias pesadas, el cold start duplica o triplica el tiempo total.
Medición Real de Cold Starts
Metodología
Para medir cold starts de forma fiable, necesitas:
- Forzar cold start (actualizar la función entre invocaciones)
- Medir tiempo total desde que Lambda recibe el evento
- Separar init duration de handler duration
- Repetir 10+ veces para promediar
Lambda reporta el Init Duration en los logs de CloudWatch — ese es tu cold start medido por AWS.
Herramienta de medición
# measure_cold_start.py
# Script para medir cold starts de forma consistente
import json
import time
import subprocess
import statistics
def force_cold_start(function_name):
"""Fuerza un cold start actualizando una env var."""
subprocess.run([
"aws", "lambda", "update-function-configuration",
"--function-name", function_name,
"--environment", json.dumps({
"Variables": {
"OPENAI_API_KEY": "sk-test",
"COLD_START_MARKER": str(time.time()),
}
}),
], capture_output=True)
time.sleep(5)
def invoke_and_measure(function_name):
"""Invoca Lambda y retorna duración total."""
payload = json.dumps({"body": json.dumps({"prompt": "Say hello"})})
start = time.time()
result = subprocess.run([
"aws", "lambda", "invoke",
"--function-name", function_name,
"--payload", payload,
"--log-type", "Tail",
"output.json",
], capture_output=True, text=True)
total = time.time() - start
return total
def run_benchmark(function_name, iterations=10):
"""Ejecuta benchmark de cold starts."""
cold_starts = []
warm_starts = []
for i in range(iterations):
# Cold start
force_cold_start(function_name)
cold = invoke_and_measure(function_name)
cold_starts.append(cold)
# Warm start (invocación inmediata, mismo container)
warm = invoke_and_measure(function_name)
warm_starts.append(warm)
print(f" Iteration {i+1}: cold={cold:.2f}s, warm={warm:.2f}s")
print(f"\nCold starts: avg={statistics.mean(cold_starts):.2f}s, "
f"p50={statistics.median(cold_starts):.2f}s, "
f"max={max(cold_starts):.2f}s")
print(f"Warm starts: avg={statistics.mean(warm_starts):.2f}s, "
f"p50={statistics.median(warm_starts):.2f}s, "
f"max={max(warm_starts):.2f}s")
print(f"Cold start overhead: {statistics.mean(cold_starts) - statistics.mean(warm_starts):.2f}s")
Resultados por tipo de dependencia
Estos son resultados representativos para funciones Lambda con 512MB de memory en us-east-1:
┌────────────────────────────────────────────────────────────────┐
│ Dependencia │ Zip Cold │ Container Cold │ Warm │
├────────────────────────┼──────────┼────────────────┼──────────┤
│ Sin dependencias │ 0.3s │ 0.8s │ <0.1s │
│ openai SDK (~5MB) │ 0.8s │ 1.5s │ <0.1s │
│ openai + httpx (~8MB) │ 1.0s │ 1.8s │ <0.1s │
│ anthropic SDK (~8MB) │ 1.0s │ 1.7s │ <0.1s │
│ langchain (~80MB) │ 3.5s │ 5.0s │ <0.1s │
│ langchain + chromadb │ 5.0s │ 7.0s │ <0.1s │
│ numpy + pandas (~120MB)│ 4.0s │ 6.5s │ <0.1s │
│ numpy + sklearn │ 5.5s │ 8.0s │ <0.1s │
│ torch CPU (~800MB) │ N/A │ 15-25s │ <0.1s │
└────────────────────────┴──────────┴────────────────┴──────────┘
Memory: 512MB | Runtime: Python 3.11 | Region: us-east-1
N/A = no cabe en zip (excede 250MB limit)
Lo que revelan los datos
-
Warm starts son siempre <100ms. El cold start es un evento puntual; una vez warm, Lambda responde rápido.
-
El salto de openai a langchain es 4x. No es lineal con el tamaño — depende de cuántos imports y cuánta inicialización hay.
-
Container agrega 1-2s sobre zip. El overhead es el pull de imagen desde ECR.
-
512MB de memory. Con más memory, Lambda asigna más CPU proporcional, y los cold starts bajan. Con 1024MB, los números se reducen ~30%.
-
torch es prohibitivo. 15-25s de cold start hace Lambda inviable para modelos locales. Usa SageMaker o ECS.
Anatomía del Cold Start
Dónde se gasta el tiempo
# handler.py con logging de tiempos
import time
_init_start = time.time()
import json # ~1ms
import os # ~1ms
_after_stdlib = time.time()
from openai import OpenAI # ~300ms (imports httpx, pydantic, etc.)
_after_openai = time.time()
# Si usas langchain, agrega ~2-5s aquí
# from langchain_openai import ChatOpenAI
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY", ""))
_after_client = time.time()
_init_total = _after_client - _init_start
print(f"INIT: stdlib={(_after_stdlib-_init_start)*1000:.0f}ms, "
f"openai={(_after_openai-_after_stdlib)*1000:.0f}ms, "
f"client={(_after_client-_after_openai)*1000:.0f}ms, "
f"total={_init_total*1000:.0f}ms")
def handler(event, context):
handler_start = time.time()
body = json.loads(event.get("body", "{}"))
prompt = body.get("prompt", "test")
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
max_tokens=100,
)
handler_time = time.time() - handler_start
return {
"statusCode": 200,
"body": json.dumps({
"answer": response.choices[0].message.content,
"init_ms": int(_init_total * 1000),
"handler_ms": int(handler_time * 1000),
}),
}
Output típico en CloudWatch:
INIT: stdlib=2ms, openai=312ms, client=45ms, total=359ms
Init Duration: 847ms (AWS overhead + tu init)
Duration: 2456ms (handler execution)
Billed Duration: 3303ms (init + handler)
El costo oculto: imports transitivos
from openai import OpenAI no solo importa openai. Importa httpx, anyio, pydantic, certifi, y más. Cada import transitivo suma al cold start:
openai imports:
├── httpx (~150ms)
│ ├── anyio
│ ├── httpcore
│ └── certifi
├── pydantic (~100ms)
│ └── pydantic-core
└── typing_extensions (~5ms)
Total: ~300ms
langchain imports:
├── langchain_core (~500ms)
│ ├── pydantic
│ ├── jsonpatch
│ └── tenacity
├── langchain (~1500ms)
│ ├── requests
│ ├── aiohttp
│ ├── SQLAlchemy
│ └── numpy (si instalado)
├── langchain_openai (~200ms)
│ └── openai
└── dataclasses-json (~100ms)
Total: ~2500ms
Estrategias de Mitigación
Estrategia 1: Optimización de paquete
La primera línea de defensa: incluye solo lo que necesitas.
# Problema: pip install langchain trae TODO
pip install langchain
# Instala: langchain, langchain-core, langchain-text-splitters,
# SQLAlchemy, aiohttp, requests, numpy, pyyaml, ...
# Solución: instala solo los componentes que usas
pip install langchain-core langchain-openai
# Instala: langchain-core, langchain-openai, openai, pydantic
# ~70% menos dependencias
# requirements.txt — ANTES (todo langchain)
langchain>=0.2.0
# requirements.txt — DESPUÉS (solo lo necesario)
langchain-core>=0.2.0
langchain-openai>=0.1.0
Impacto medido:
langchain completo: Cold start ~3.5s (zip) / ~5.0s (container)
langchain-core + openai: Cold start ~1.5s (zip) / ~2.5s (container)
Reducción: ~55%
Estrategia 2: Lazy imports
Importa módulos pesados solo cuando los necesitas, no al inicio:
# ❌ Import al inicio — penaliza TODOS los cold starts
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
def handler(event, context):
...
# ✅ Lazy import — solo penaliza la primera invocación que lo use
_chain = None
def _get_chain():
global _chain
if _chain is None:
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_messages([
("system", "Be concise."),
("user", "{input}"),
])
llm = ChatOpenAI(model="gpt-4o-mini")
_chain = prompt | llm
return _chain
def handler(event, context):
body = json.loads(event.get("body", "{}"))
if body.get("use_chain"):
chain = _get_chain()
result = chain.invoke({"input": body["prompt"]})
answer = result.content
else:
# Path rápido sin langchain
response = client.chat.completions.create(...)
answer = response.choices[0].message.content
...
Ventaja: si la mayoría de invocaciones usan el path sin LangChain, el cold start no incluye esos imports.
Estrategia 3: Más memory = más CPU = cold start más rápido
Lambda asigna CPU proporcional a la memory configurada. Más memory → más CPU → imports más rápidos:
Cold start de langchain por nivel de memory:
├── 128MB: ~6.0s (CPU mínima)
├── 256MB: ~4.5s
├── 512MB: ~3.5s
├── 1024MB: ~2.5s (sweet spot para AI)
├── 1769MB: ~2.0s (1 vCPU completo)
├── 3008MB: ~1.5s
└── 10240MB: ~1.2s (disminishing returns)
El sweet spot para funciones AI es 1024-1769MB: suficiente CPU para imports rápidos sin pagar por memory que no usas en runtime.
# Cambiar memory
aws lambda update-function-configuration \
--function-name ai-endpoint \
--memory-size 1024
# Lambda Power Tuning — herramienta para encontrar el óptimo
# https://github.com/alexcasalboni/aws-lambda-power-tuning
Estrategia 4: Provisioned Concurrency
Mantiene N containers siempre warm. Zero cold starts para esos N containers.
# Configurar 5 containers siempre warm
aws lambda put-provisioned-concurrency-config \
--function-name ai-endpoint \
--qualifier prod \
--provisioned-concurrent-executions 5
Sin provisioned concurrency:
├── Invocaciones 1-N: Cold start (~3s cada una)
├── Invocaciones siguientes: Warm (~0.1s)
└── Después de inactividad: Cold start de nuevo
Con provisioned concurrency (5):
├── Invocaciones 1-5 simultáneas: Warm (~0.1s) ← siempre
├── Invocación 6+: Cold start (overflow)
└── Coste: pagas por los 5 containers 24/7
Coste de provisioned concurrency:
Provisioned: $0.0000041667/GB-second (siempre corriendo)
On-demand: $0.0000166667/GB-second (solo cuando se ejecuta)
5 containers × 512MB × 24h × 30 días:
5 × 0.5GB × 86400s × 30 × $0.0000041667 = ~$27/mes
Eso son $27/mes para eliminar cold starts de las primeras 5 invocaciones concurrentes.
¿Vale la pena? Depende de tu caso:
| Escenario | ¿Provisioned? | Razón |
|---|---|---|
| Chatbot con usuarios activos | ✅ Sí | UX importa, cold start visible |
| API con SLA de latencia | ✅ Sí | SLA no tolera 3-5s extra |
| Batch processing nocturno | ❌ No | Sin usuario esperando |
| MVP con <100 invocaciones/día | ❌ No | Coste no justificado |
| API con tráfico constante | ✅ Sí | Containers se mantienen warm naturalmente |
Estrategia 5: Keep-warm (ping periódico)
Un CloudWatch Event que invoca tu Lambda cada 5 minutos para mantener containers warm:
# En SAM template
Resources:
AIEndpoint:
Type: AWS::Serverless::Function
Properties:
Handler: handler.handler
# ... config ...
Events:
KeepWarm:
Type: Schedule
Properties:
Schedule: rate(5 minutes)
Input: '{"source": "keep-warm"}'
def handler(event, context):
# Detecta invocación keep-warm y retorna rápido
if event.get("source") == "keep-warm":
return {"statusCode": 200, "body": "warm"}
# Lógica normal
...
Limitaciones:
- Solo mantiene warm 1 container. Si tienes invocaciones concurrentes, las adicionales siguen teniendo cold start.
- Lambda puede reciclar el container incluso con pings (no es garantía).
- Coste: las invocaciones keep-warm se cobran (pero son muy baratas porque duran <100ms).
Keep-warm: 12 invocaciones/hora × 24h × 30 días = 8640 invocaciones
× 100ms × 512MB = ~$0.05/mes
vs Provisioned concurrency: ~$27/mes para 5 containers
Keep-warm es ~500x más barato pero solo mantiene 1 container.
Comparación de Estrategias de Mitigación
| Estrategia | Reducción de cold start | Coste adicional | Complejidad | Cuándo |
|---|---|---|---|---|
| Optimizar paquete | 30-60% | $0 | Baja | Siempre (primera acción) |
| Lazy imports | 20-40% (condicional) | $0 | Baja | Cuando tienes paths opcionales |
| Más memory | 20-50% | Varía | Baja | Cuando el cold start es CPU-bound |
| Provisioned concurrency | 100% (N containers) | $5-50+/mes | Media | Producción con SLA |
| Keep-warm | ~100% (1 container) | ~$0.05/mes | Baja | MVP, tráfico bajo |
Combinación recomendada para AI
Nivel 1 (gratis, siempre):
├── Optimizar dependencias (solo lo que usas)
├── Lazy imports para paths opcionales
└── Memory ≥ 512MB para funciones AI
Nivel 2 (bajo tráfico, ~$0.05/mes):
└── Keep-warm cada 5 minutos
Nivel 3 (producción, $5-50/mes):
└── Provisioned concurrency (N según tráfico concurrente)
Cold Starts en Contexto: ¿Es Realmente un Problema?
Cuándo el cold start NO importa
- Procesamiento batch: Nadie espera. Un cold start de 10s en un job de 2 minutos es irrelevante.
- Webhooks y event processing: El evento se procesa en background. 3s extra no afectan.
- Tráfico constante: Si tu Lambda recibe invocaciones cada pocos segundos, los containers se mantienen warm naturalmente. Cold starts son raros.
- Async processing: El usuario recibe "202 Accepted" inmediato. El cold start no afecta su experiencia.
Cuándo el cold start SÍ importa
- Chatbots y APIs síncronas: El usuario ve un spinner durante cold start + inferencia. 8s total se siente lento.
- Picos de tráfico: Cuando el tráfico sube súbitamente, Lambda necesita crear muchos containers nuevos → muchos cold starts simultáneos.
- SLAs de latencia: Si prometiste p99 < 5s, un cold start de 3s + handler de 3s = 6s > SLA.
Framework de decisión
¿Tu usuario espera la respuesta en tiempo real?
├── NO → Cold starts no importan. No inviertas en mitigación.
└── SÍ → ¿Cuánto dura tu cold start?
├── <2s → Aceptable para la mayoría de UX. Optimiza paquete.
├── 2-5s → Depende del UX. Keep-warm puede ser suficiente.
└── >5s → Necesitas provisioned concurrency o reconsiderar Lambda.
¿El coste de provisioned lo justifica?
├── SÍ → Provisioned concurrency
└── NO → Considera ECS/Fargate (servidor siempre-on)
Troubleshooting
Problema 1: "Cold start de 10s+ con openai SDK solamente"
# Verifica la memory configurada
aws lambda get-function-configuration \
--function-name ai-endpoint \
--query 'MemorySize'
# Si es 128MB, ese es el problema — no hay suficiente CPU
# Sube a 512MB mínimo para funciones AI
aws lambda update-function-configuration \
--function-name ai-endpoint \
--memory-size 512
Problema 2: "Cold starts son inconsistentes (a veces 2s, a veces 8s)"
# Lambda puede provisionar tu container en diferentes tipos de hardware.
# La varianza es normal. Mide con 10+ invocaciones y usa el p50/p95.
# Verifica en CloudWatch Logs
aws logs filter-log-events \
--log-group-name /aws/lambda/ai-endpoint \
--filter-pattern "Init Duration"
# También verifica que no estás hitting un limit de concurrencia
aws lambda get-function \
--function-name ai-endpoint \
--query 'Concurrency'
Problema 3: "Provisioned concurrency está activa pero aún veo cold starts"
# Verifica el número de provisioned vs invocaciones concurrentes
aws lambda get-provisioned-concurrency-config \
--function-name ai-endpoint \
--qualifier prod
# Si tienes 5 provisioned pero 8 invocaciones simultáneas,
# 3 tendrán cold start. Aumenta provisioned o implementa rate limiting.
Problema 4: "Keep-warm funciona pero al mediodía hay cold starts"
Lambda puede crear nuevos containers cuando el tráfico aumenta. Keep-warm solo mantiene 1 container. Si al mediodía tienes 5 invocaciones concurrentes, 4 tendrán cold start.
09:00 - 1 invocación → warm (keep-warm container)
09:01 - 1 invocación → warm
12:00 - 5 simultáneas → 1 warm + 4 cold starts
12:01 - 3 simultáneas → 3 warm (containers del pico anterior)
Solución: provisioned concurrency ajustada al pico esperado, o acepta los cold starts en picos.
Ejercicios Prácticos
Ejercicio 1: Mide tu cold start
Crea una función Lambda con el SDK de openai (zip deployment). Invócala 5 veces forzando cold start y 5 veces en warm. Documenta los resultados en una tabla.
Ver solución
# Crear función (si no existe)
# Usa el build.sh de la cápsula 03
# Script de medición simplificado
for i in $(seq 1 5); do
echo "--- Cold start $i ---"
# Forzar cold start cambiando env var
aws lambda update-function-configuration \
--function-name ai-endpoint-zip \
--environment "Variables={OPENAI_API_KEY=$OPENAI_API_KEY,MARKER=$i}" \
--query 'LastUpdateStatus' --output text
sleep 8
# Invocar y capturar Init Duration
aws lambda invoke \
--function-name ai-endpoint-zip \
--payload '{"body": "{\"prompt\": \"Say hi\"}"}' \
--log-type Tail \
--query 'LogResult' \
--output text out.json | base64 -d | grep -E "Init Duration|Duration|Billed"
echo "--- Warm start $i ---"
# Invocar inmediatamente (warm)
aws lambda invoke \
--function-name ai-endpoint-zip \
--payload '{"body": "{\"prompt\": \"Say hi\"}"}' \
--log-type Tail \
--query 'LogResult' \
--output text out.json | base64 -d | grep -E "Duration|Billed"
done
Resultado esperado (ejemplo):
| # | Cold Start (Init) | Cold Total | Warm Total |
|---|-------------------|------------|------------|
| 1 | 823ms | 3.2s | 2.4s |
| 2 | 791ms | 3.1s | 2.3s |
| 3 | 856ms | 3.3s | 2.5s |
| 4 | 812ms | 3.2s | 2.4s |
| 5 | 834ms | 3.1s | 2.3s |
| **Avg** | **823ms** | **3.18s** | **2.38s** |
Cold start overhead: 3.18 - 2.38 = 0.80s
Ejercicio 2: Optimiza las dependencias
Toma una función que usa langchain completo y reemplázalo por solo los componentes que necesitas (langchain-core, langchain-openai). Mide el cold start antes y después.
Ver solución
# ANTES: requirements.txt
echo "langchain>=0.2.0
langchain-openai>=0.1.0" > requirements-before.txt
pip install -r requirements-before.txt -t before-package/ \
--platform manylinux2014_x86_64 \
--only-binary=:all: \
--python-version 3.11
du -sh before-package/
# ~85MB
# DESPUÉS: requirements.txt optimizado
echo "langchain-core>=0.2.0
langchain-openai>=0.1.0" > requirements-after.txt
pip install -r requirements-after.txt -t after-package/ \
--platform manylinux2014_x86_64 \
--only-binary=:all: \
--python-version 3.11
du -sh after-package/
# ~35MB
echo "Reducción: $(echo "scale=0; (85-35)*100/85" | bc)%"
# ~59% reducción en tamaño
# El handler se mantiene igual si solo usas ChatOpenAI y ChatPromptTemplate
# Esos están en langchain-core y langchain-openai
# Despliega ambas versiones y mide cold starts
# Antes: Init ~2.5-3.5s
# Después: Init ~1.0-1.5s
Resultado documentado:
| Versión | Tamaño deps | Cold start (avg) | Warm start (avg) |
|---------|-------------|------------------|------------------|
| langchain completo | 85MB | 3.5s | 2.4s |
| langchain-core+openai | 35MB | 1.5s | 2.4s |
| **Mejora** | **59%** | **57%** | **0%** |
El warm start no cambia — la mejora es 100% en cold start.
Ejercicio 3: Implementa keep-warm
Configura un CloudWatch Event que invoque tu Lambda cada 5 minutos. Modifica el handler para detectar invocaciones keep-warm y retornar inmediatamente.
Ver solución
# handler.py con soporte keep-warm
import json
import os
import time
from openai import OpenAI
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
def handler(event, context):
# Detectar invocación keep-warm y retornar rápido
if event.get("source") == "keep-warm":
return {
"statusCode": 200,
"body": json.dumps({
"status": "warm",
"remaining_ms": context.get_remaining_time_in_millis(),
}),
}
# Lógica normal
try:
body = json.loads(event.get("body", "{}"))
except json.JSONDecodeError:
return _response(400, {"error": "Invalid JSON"})
prompt = body.get("prompt", "").strip()
if not prompt:
return _response(400, {"error": "prompt required"})
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
max_tokens=500,
)
return _response(200, {
"answer": response.choices[0].message.content,
"tokens": response.usage.total_tokens,
})
def _response(status, body):
return {
"statusCode": status,
"headers": {"Content-Type": "application/json", "Access-Control-Allow-Origin": "*"},
"body": json.dumps(body),
}
# template.yaml con keep-warm event
Resources:
AIEndpoint:
Type: AWS::Serverless::Function
Properties:
Handler: handler.handler
Runtime: python3.11
CodeUri: lambda-function/
Timeout: 60
MemorySize: 512
Environment:
Variables:
OPENAI_API_KEY: !Ref OpenAIKey
Events:
AskAPI:
Type: Api
Properties:
Path: /ask
Method: post
KeepWarm:
Type: Schedule
Properties:
Schedule: rate(5 minutes)
Input: '{"source": "keep-warm"}'
Description: "Mantiene Lambda warm para evitar cold starts"
Parameters:
OpenAIKey:
Type: String
NoEcho: true
sam build && sam deploy --guided
# Verificar que keep-warm funciona
# En CloudWatch Logs, cada 5 minutos debes ver:
# {"status": "warm", "remaining_ms": 59800}
Ejercicio 4: Calcula el coste de mitigación
Para tu función AI (512MB, cold start de 3s), calcula y compara el coste mensual de tres escenarios: sin mitigación, con keep-warm, y con provisioned concurrency (3 containers). Asume 5000 invocaciones/día reales.
Ver solución
## Datos base
- Memory: 512MB (0.5GB)
- Invocaciones reales: 5000/día = 150,000/mes
- Duración promedio handler: 3s
- Cold start: 3s adicionales
- Tráfico: ~70% entre 9am-6pm (9 horas)
- Pico concurrencia: ~5 simultáneas
## Escenario 1: Sin mitigación
- 150,000 invocaciones × 3s × 0.5GB = 225,000 GB-seconds
- ~5% cold starts (estimado con tráfico regular): 7,500 cold starts
- Cold start extra: 7,500 × 3s × 0.5GB = 11,250 GB-seconds
- Total: 236,250 GB-seconds
- Coste compute: 236,250 × $0.0000166667 = ~$3.94
- Coste invocaciones: 150,000 × $0.20/1M = ~$0.03
- **Total: ~$3.97/mes**
## Escenario 2: Keep-warm
- Invocaciones reales: igual ($3.97)
- Keep-warm: 12/hora × 24h × 30 días = 8,640 invocaciones
- Keep-warm cost: 8,640 × 0.1s × 0.5GB × $0.0000166667 = ~$0.007
- Cold starts reducidos: ~1% (solo en picos de concurrencia)
- **Total: ~$3.98/mes** (prácticamente igual)
## Escenario 3: Provisioned Concurrency (3)
- On-demand compute (same): ~$3.97
- Provisioned: 3 × 0.5GB × 86,400s × 30 × $0.0000041667 = ~$16.20
- Cold starts: 0 para las primeras 3 concurrentes
- **Total: ~$20.17/mes**
## Resumen
| Estrategia | Coste/mes | Cold starts | UX |
|-----------|----------|-------------|------|
| Sin mitigación | $3.97 | ~5% invocaciones | 3s extra ocasional |
| Keep-warm | $3.98 | ~1% (solo picos) | Buena para tráfico bajo |
| Provisioned (3) | $20.17 | ~0% (hasta 3 conc.) | Consistente |
Conclusión: Keep-warm es almost-free y elimina la mayoría de cold starts.
Provisioned vale la pena si el SLA de latencia justifica $16 extra/mes.
Resumen
- Cold starts ocurren cuando Lambda crea un nuevo container. Para funciones AI, pueden durar 1-15s dependiendo de las dependencias.
- El impacto principal es en UX: un cold start de 5s + handler de 3s = 8s que el usuario percibe como lento.
- Optimizar dependencias es la primera acción (gratis, -30-60% en cold start). No instales
langchaincuando solo necesitaslangchain-core. - Más memory = más CPU = cold starts más rápidos. El sweet spot para AI es 512MB-1024MB.
- Keep-warm (~$0.05/mes) mantiene 1 container warm. Suficiente para tráfico bajo.
- Provisioned concurrency ($5-50+/mes) elimina cold starts para N containers. Necesario para producción con SLAs.
- Warm starts son siempre <100ms. Cold starts son un problema puntual, no constante.
- No todo necesita mitigación. Procesamiento batch y async no se ven afectados por cold starts.
Recursos Adicionales
- Lambda Cold Starts — Official Docs — Cómo Lambda gestiona execution environments
- Provisioned Concurrency — Documentación oficial de provisioned concurrency
- Lambda Power Tuning — Herramienta para encontrar el memory/cost óptimo
- Understanding Lambda Cold Starts — Lumigo — Análisis detallado de cold starts con datos reales
- Lambda SnapStart — Optimización de cold starts (Java, pero el concepto aplica)
- Serverless Cold Start Comparison — Benchmark de cold starts por runtime y memory
- AWS Lambda Pricing Calculator — Para calcular costes de provisioned concurrency
- Reducing Lambda Cold Starts — AWS Blog — Best practices oficiales