Saltar al contenido principal

Lab: Solución de problemas Lambda

Objetivo​

Diagnosticar y corregir funciones Lambda defectuosas utilizando un enfoque metódico.

Ejercicio 1: Timeout​

Código problemático​

# buggy_timeout.py
import time
import json

def lambda_handler(event, context):
"""
Cette fonction a un problème de timeout
"""
print("Starting processing...")

# Simulation d'un traitement long
data = event.get('data', [])

for item in data:
# Traitement très lent
time.sleep(2) # 2 secondes par item !
print(f"Processed: {item}")

return {
'statusCode': 200,
'body': json.dumps({'processed': len(data)})
}

Configuración​

  • Timeout: 3 segundos
  • Payload: {"data": ["a", "b", "c", "d", "e"]}

Preguntas​

  1. ¿Por qué falla esta función?
  2. Calcule el tiempo total necesario
  3. Proponga 2 soluciones diferentes
💡 Solución

1. ¿Por qué?:

  • 5 ítems × 2 segundos = 10 segundos mínimo
  • Timeout = 3 segundos
  • La función se interrumpe antes de terminar

2. Tiempo necesario:

  • Procesamiento: 5 × 2s = 10s
  • Overhead Lambda: ~100ms
  • Total: ~10.1 segundos

3. Soluciones:

Solución A: Aumentar el timeout

aws lambda update-function-configuration \
--function-name buggy-timeout \
--timeout 30

Solución B: Optimizar el código

import json
from concurrent.futures import ThreadPoolExecutor

def process_item(item):
# Tratamiento (simulado con sleep reducido)
return f"Processed: {item}"

def lambda_handler(event, context):
data = event.get('data', [])

# Procesamiento en paralelo
with ThreadPoolExecutor(max_workers=5) as executor:
results = list(executor.map(process_item, data))

return {
'statusCode': 200,
'body': json.dumps({'results': results})
}

Ejercicio 2: Error de permisos​

Código​

# buggy_permissions.py
import json
import boto3

def lambda_handler(event, context):
"""
Cette fonction tente de lire depuis S3
"""
s3 = boto3.client('s3')

bucket = event['bucket']
key = event['key']

try:
response = s3.get_object(Bucket=bucket, Key=key)
content = response['Body'].read().decode('utf-8')

return {
'statusCode': 200,
'body': json.dumps({'content': content[:100]})
}
except Exception as e:
return {
'statusCode': 500,
'body': json.dumps({'error': str(e)})
}

Error recibido​

{
"statusCode": 500,
"body": "{\"error\": \"An error occurred (AccessDenied) when calling the GetObject operation: Access Denied\"}"
}

Preguntas​

  1. ¿Cuál es la causa del error?
  2. ¿Qué política IAM se debe agregar?
  3. Escriba el comando AWS CLI para corregir
💡 Solución

1. Causa: El rol de ejecución de Lambda no tiene el permiso s3:GetObject

2. Política IAM a agregar:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject"
],
"Resource": "arn:aws:s3:::my-bucket/*"
}
]
}

3. Comandos CLI:

# Crear la política
cat > s3-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["s3:GetObject"],
"Resource": "arn:aws:s3:::my-bucket/*"
}]
}
EOF

# Adjuntar al rol
aws iam put-role-policy \
--role-name lambda-role \
--policy-name S3ReadAccess \
--policy-document file://s3-policy.json

Ejercicio 3: Out of Memory​

Código problemático​

# buggy_memory.py
import json

def lambda_handler(event, context):
"""
Cette fonction a un problème de mémoire
"""
size = event.get('size', 1000000)

# Créer une énorme liste en mémoire
data = []
for i in range(size):
data.append({
'id': i,
'name': f'Item {i}',
'description': 'A' * 1000 # 1KB par item
})

return {
'statusCode': 200,
'body': json.dumps({'count': len(data)})
}

Configuración​

  • Memoria: 128 MB
  • Payload: {"size": 100000} (100K ítems × ~1KB = ~100MB)

Preguntas​

  1. ¿Por qué falla la función?
  2. ¿Qué memoria recomienda?
  3. ¿Cómo optimizar sin aumentar la memoria?
💡 Solución

1. ¿Por qué?:

  • 100,000 ítems × ~1KB = ~100MB de datos
  • Overhead Python/Lambda: ~30-50MB
  • Total: ~130-150MB > 128MB configurados

2. Memoria recomendada:

  • Mínimo: 256 MB (con margen)
  • Recomendado: 512 MB (por seguridad)
aws lambda update-function-configuration \
--function-name buggy-memory \
--memory-size 512

3. Optimización sin más memoria:

import json

def lambda_handler(event, context):
size = event.get('size', 1000000)

# Procesamiento por lotes con generador
def process_batch(start, end):
for i in range(start, end):
yield {'id': i, 'processed': True}

# Procesar en chunks de 10000
batch_size = 10000
total_processed = 0

for batch_start in range(0, size, batch_size):
batch_end = min(batch_start + batch_size, size)

# Procesar el lote (no mantiene todo en memoria)
for item in process_batch(batch_start, batch_end):
total_processed += 1

# Liberar memoria entre lotes

return {
'statusCode': 200,
'body': json.dumps({'processed': total_processed})
}

Ejercicio 4: Handler Not Found​

Configuración​

  • Archivo: my_function.py
  • Handler configurado: lambda_function.handler

Error​

{
"errorMessage": "Unable to import module 'lambda_function': No module named 'lambda_function'",
"errorType": "Runtime.ImportModuleError"
}

Preguntas​

  1. ¿Cuál es el error de configuración?
  2. ¿Cuál es el valor correcto del handler?
  3. Liste 3 errores comunes de handler
💡 Solución

1. Error: El handler está configurado como lambda_function.handler pero el archivo se llama my_function.py

2. Handler correcto:

my_function.handler

Formato: <nombre_archivo_sin_extensión>.<nombre_función>

3. Errores comunes:

  1. Nombre de archivo incorrecto (lambda_function vs my_function)
  2. Nombre de función incorrecto (handler vs lambda_handler)
  3. Archivo en un subdirectorio (src/handler.main en lugar de handler.main)
  4. Extensión incluida por error (handler.py.main)
  5. Mayúsculas/minúsculas incorrectas (Handler vs handler)

Ejercicio 5: Diagnóstico completo​

Escenario​

Una función Lambda retorna errores intermitentes. Aquí la información disponible:

Logs CloudWatch:

START RequestId: abc-123
[INFO] Connecting to database...
END RequestId: abc-123
REPORT RequestId: abc-123 Duration: 29987.45 ms Billed Duration: 30000 ms Memory Size: 256 MB Max Memory Used: 89 MB Init Duration: 1234.56 ms

START RequestId: def-456
[INFO] Connecting to database...
[ERROR] Connection timed out
END RequestId: def-456
REPORT RequestId: def-456 Duration: 30000.00 ms Billed Duration: 30000 ms Memory Size: 256 MB Max Memory Used: 67 MB

Configuración:

  • Timeout: 30 segundos
  • Memoria: 256 MB
  • VPC: Sí (subred privada)

Preguntas​

Complete la tabla de diagnóstico:

ElementoObservaciónProblema PotencialAcción
Duration???
Memory???
Init Duration???
Error message???
VPC???
💡 Solución
ElementoObservaciónProblema PotencialAcción
Duration~30s (timeout)Conexión a BD lenta/bloqueadaTimeout BD + retry
Memory67-89 MB / 256 MBOK, sin problema de memoria-
Init Duration1234msCold start elevadoOptimizar o Provisioned
Error messageConnection timed outBD inaccesibleVerificar SG, NAT
VPCSubred privada¿Sin NAT para salida a Internet?Agregar NAT Gateway

Diagnóstico: La función está en una VPC privada sin NAT Gateway. No puede acceder a la base de datos externa.

Soluciones:

  1. Agregar un NAT Gateway a la subred privada
  2. O poner la BD en la misma VPC
  3. O usar VPC Endpoints si la BD es un servicio AWS
  4. Agregar timeouts de conexión en el código

Ejercicio 6: Escribir logs de calidad​

Código antes (logs deficientes)​

def lambda_handler(event, context):
print("Starting")
# ... code ...
print("Done")
return result

Ejercicio​

Reescriba el código con logs de calidad que faciliten la solución de problemas:

💡 Solución
import json
import logging

# Configuración del logger
logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):
# Log estructurado de la entrada
logger.info(json.dumps({
'action': 'start',
'request_id': context.aws_request_id,
'event_keys': list(event.keys()),
'remaining_time_ms': context.get_remaining_time_in_millis()
}))

try:
# Su lógica
user_id = event.get('user_id')
logger.info(f"Processing user: {user_id}")

result = process_user(user_id)

# Log de éxito
logger.info(json.dumps({
'action': 'complete',
'request_id': context.aws_request_id,
'user_id': user_id,
'status': 'success'
}))

return {'statusCode': 200, 'body': json.dumps(result)}

except Exception as e:
# Log de error detallado
logger.error(json.dumps({
'action': 'error',
'request_id': context.aws_request_id,
'error_type': type(e).__name__,
'error_message': str(e),
'user_id': event.get('user_id')
}), exc_info=True)

return {'statusCode': 500, 'body': json.dumps({'error': str(e)})}

Entregable​

Documento a producir​

  1. Soluciones a los 6 ejercicios
  2. Tabla de diagnóstico completada
  3. Código corregido para cada bug
  4. Recomendaciones de buenas prácticas

Criterios de evaluación​

CriterioPuntos
Diagnóstico correcto/25
Soluciones apropiadas/25
Código corregido funcional/25
Calidad de los logs/15
Documentación/10
Total/100

Siguiente semana: MÓDULO 13 - Observabilidad