Lab: Solução de Problemas Lambda
Objetivo
Diagnosticar e corrigir funções Lambda com falhas utilizando uma abordagem metódica.
Exercício 1: Timeout
Código com Problema
# buggy_timeout.py
import time
import json
def lambda_handler(event, context):
"""
Esta função tem um problema de timeout
"""
print("Starting processing...")
# Simulação de processamento longo
data = event.get('data', [])
for item in data:
# Processamento muito lento
time.sleep(2) # 2 segundos por item!
print(f"Processed: {item}")
return {
'statusCode': 200,
'body': json.dumps({'processed': len(data)})
}
Configuração
- Timeout: 3 segundos
- Payload:
{"data": ["a", "b", "c", "d", "e"]}
Perguntas
- Por que essa função falha?
- Calcule o tempo total necessário
- Proponha 2 soluções diferentes
💡 Solução
1. Por quê:
- 5 itens × 2 segundos = 10 segundos no mínimo
- Timeout = 3 segundos
- A função é interrompida antes de terminar
2. Tempo necessário:
- Processamento: 5 × 2s = 10s
- Overhead Lambda: ~100ms
- Total: ~10,1 segundos
3. Soluções:
Solução A: Aumentar o timeout
aws lambda update-function-configuration \
--function-name buggy-timeout \
--timeout 30
Solução B: Otimizar o código
import json
from concurrent.futures import ThreadPoolExecutor
def process_item(item):
# Processamento (simulado com sleep reduzido)
return f"Processed: {item}"
def lambda_handler(event, context):
data = event.get('data', [])
# Processamento paralelo
with ThreadPoolExecutor(max_workers=5) as executor:
results = list(executor.map(process_item, data))
return {
'statusCode': 200,
'body': json.dumps({'results': results})
}
Exercício 2: Erro de Permissões
Código
# buggy_permissions.py
import json
import boto3
def lambda_handler(event, context):
"""
Esta função tenta ler do 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)})
}
Erro Recebido
{
"statusCode": 500,
"body": "{\"error\": \"An error occurred (AccessDenied) when calling the GetObject operation: Access Denied\"}"
}
Perguntas
- Qual é a causa do erro?
- Qual política IAM deve ser adicionada?
- Escreva o comando AWS CLI para corrigir
💡 Solução
1. Causa:
O role de execução do Lambda não possui permissão s3:GetObject
2. Política IAM a ser adicionada:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject"
],
"Resource": "arn:aws:s3:::my-bucket/*"
}
]
}
3. Comandos CLI:
# Criar a política
cat > s3-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["s3:GetObject"],
"Resource": "arn:aws:s3:::my-bucket/*"
}]
}
EOF
# Anexar ao role
aws iam put-role-policy \
--role-name lambda-role \
--policy-name S3ReadAccess \
--policy-document file://s3-policy.json
Exercício 3: Out of Memory
Código com Problema
# buggy_memory.py
import json
def lambda_handler(event, context):
"""
Esta função tem um problema de memória
"""
size = event.get('size', 1000000)
# Cria uma lista enorme em memória
data = []
for i in range(size):
data.append({
'id': i,
'name': f'Item {i}',
'description': 'A' * 1000 # 1KB por item
})
return {
'statusCode': 200,
'body': json.dumps({'count': len(data)})
}
Configuração
- Memória: 128 MB
- Payload:
{"size": 100000}(100K itens × ~1KB = ~100MB)
Perguntas
- Por que a função falha?
- Qual memória você recomenda?
- Como otimizar sem aumentar a memória?
💡 Solução
1. Por quê:
- 100.000 itens × ~1KB = ~100MB de dados
- Overhead Python/Lambda: ~30-50MB
- Total: ~130-150MB > 128MB configurado
2. Memória recomendada:
- Mínimo: 256 MB (com margem)
- Recomendado: 512 MB (para segurança)
aws lambda update-function-configuration \
--function-name buggy-memory \
--memory-size 512
3. Otimização sem mais memória:
import json
def lambda_handler(event, context):
size = event.get('size', 1000000)
# Processamento em batch com generator
def process_batch(start, end):
for i in range(start, end):
yield {'id': i, 'processed': True}
# Processar em 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)
# Processar o batch (não mantém tudo em memória)
for item in process_batch(batch_start, batch_end):
total_processed += 1
# Libera a memória entre os batches
return {
'statusCode': 200,
'body': json.dumps({'processed': total_processed})
}
Exercício 4: Handler Not Found
Configuração
- Arquivo:
my_function.py - Handler configurado:
lambda_function.handler
Erro
{
"errorMessage": "Unable to import module 'lambda_function': No module named 'lambda_function'",
"errorType": "Runtime.ImportModuleError"
}
Perguntas
- Qual é o erro de configuração?
- Qual valor de handler está correto?
- Liste 3 erros comuns de handler
💡 Solução
1. Erro:
O handler está configurado como lambda_function.handler mas o arquivo se chama my_function.py
2. Handler correto:
my_function.handler
Formato: <nome_arquivo_sem_extensão>.<nome_função>
3. Erros comuns:
- Nome de arquivo incorreto (
lambda_functionvsmy_function) - Nome de função incorreto (
handlervslambda_handler) - Arquivo em subdiretório (
src/handler.mainao invés dehandler.main) - Inclusão da extensão por engano (
handler.py.main) - Case incorreto (
Handlervshandler)
Exercício 5: Diagnóstico Completo
Cenário
Uma função Lambda retorna erros intermitentes. Veja as informações disponíveis:
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
Configuração:
- Timeout: 30 segundos
- Memória: 256 MB
- VPC: Sim (sub-rede privada)
Perguntas
Preencha a tabela de diagnóstico:
| Elemento | Observação | Problema Potencial | Ação |
|---|---|---|---|
| Duration | ? | ? | ? |
| Memory | ? | ? | ? |
| Init Duration | ? | ? | ? |
| Error message | ? | ? | ? |
| VPC | ? | ? | ? |
💡 Solução
| Elemento | Observação | Problema Potencial | Ação |
|---|---|---|---|
| Duration | ~30s (timeout) | Conexão DB lenta/bloqueada | Timeout DB + retry |
| Memory | 67-89 MB / 256 MB | OK, sem problema de memória | - |
| Init Duration | 1234ms | Cold start elevado | Otimizar ou Provisioned |
| Error message | Connection timed out | DB inacessível | Verificar SG, NAT |
| VPC | Sub-rede privada | Sem NAT para saída Internet? | Adicionar NAT Gateway |
Diagnóstico: A função está em uma VPC privada sem NAT Gateway. Ela não consegue acessar o banco de dados externo.
Soluções:
- Adicionar um NAT Gateway à sub-rede privada
- Ou colocar o banco de dados na mesma VPC
- Ou usar VPC Endpoints se o banco for um serviço AWS
- Adicionar timeouts de conexão no código
Exercício 6: Escrever Logs de Qualidade
Código Antes (logs ruins)
def lambda_handler(event, context):
print("Starting")
# ... código ...
print("Done")
return result
Exercício
Reescreva o código com logs de qualidade que facilitem o troubleshooting:
💡 Solução
import json
import logging
# Configuração do logger
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def lambda_handler(event, context):
# Log estruturado da 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:
# Sua lógica
user_id = event.get('user_id')
logger.info(f"Processing user: {user_id}")
result = process_user(user_id)
# Log de sucesso
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 erro detalhado
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)})}
Entregável
Documento a Produzir
- Soluções para os 6 exercícios
- Tabela de diagnóstico preenchida
- Código corrigido para cada bug
- Recomendações de boas práticas
Critérios de Avaliação
| Critério | Pontos |
|---|---|
| Diagnóstico correto | /25 |
| Soluções apropriadas | /25 |
| Código corrigido funcional | /25 |
| Qualidade dos logs | /15 |
| Documentação | /10 |
| Total | /100 |
Semana seguinte: MÓDULO 13 - Observabilidade