
Cómo construir un sistema serverless de alerta y triage ante emergencias en AWS con costo $0 en la Capa Gratuita
Aprende a diseñar una arquitectura orientada a eventos en AWS para ingesta masiva ante catástrofes con integración directa entre API Gateway y SQS sin cómputo intermedio. Implementa deduplicación geoespacial determinística por Geohash, manejo de fallos parciales con ReportBatchItemFailures en Lambda y retención automática con DynamoDB TTL a costo cero.
Cómo construir un sistema serverless de alerta y triage ante emergencias en AWS con costo $0 en la Capa Gratuita
Por David — Proyecto para el AWS Community Content Creators Bootcamp LATAM 2026 (
#contentcreatorslatam)El escenario de producción: cuando las redes fallan y el tráfico explota
Imagina este escenario crítico de la vida real: una tormenta repentina o un fenómeno meteorológico extremo inunda un sector urbano densamente poblado en cuestión de minutos. En un lapso de apenas 180 segundos, más de 15.000 ciudadanos abren simultáneamente su navegador web, aplicación comunitaria o dispositivo móvil para enviar llamados de auxilio: "El agua superó un metro y medio en la intersección de San Martín y Rivadavia; hay familias atrapadas".
Si tu backend está construido sobre la arquitectura síncrona convencional —un servidor web con Express o FastAPI conectado directamente a una base de datos relacional (como PostgreSQL en RDS) o una función Lambda que procesa y escribe cada reporte en tiempo real— tu infraestructura colapsará en segundos.
Ocurre el temido fenómeno del Thundering Herd Problem:
- La concurrencia de cómputo en AWS Lambda agota rápidamente la cuota regional de ejecuciones simultáneas.
- La base de datos agota su pool de conexiones disponibles (
max_connections) bajo la avalancha de escrituras simultáneas. - Los usuarios reciben errores masivos de
504 Gateway Timeouty502 Bad Gateway, provocando que reintenten frenéticamente y multipliquen por tres la carga del sistema.
En aplicaciones de comercio electrónico o redes sociales, un error 500 significa una venta perdida o una mala reseña. En sistemas comunitarios de respuesta ante emergencias y catástrofes civiles, la pérdida silenciosa de un mensaje no es una anomalía estadística en un dashboard: es una vida humana en peligro inminente.
Qué vas a construir y qué vas a aprender
En este tutorial exhaustivo de arquitectura de producción vamos a desplegar ResQ-Cloud, una solución Serverless orientada a eventos (Event-Driven Architecture) especialmente diseñada para ingerir avalanchas masivas de tráfico sin degradación y manteniendo un costo operativo de $0,00 USD en reposo dentro de la Capa Gratuita (AWS Free Tier).
Al finalizar este artículo habrás dominado los siguientes conceptos fundamentales de arquitectura en la nube:
- Ingesta directa desacoplada sin cómputo intermediario: Cómo configurar Amazon API Gateway (HTTP API) para depositar mensajes directamente en Amazon SQS sin pagar invocaciones ni sufrir latencias de arranque en frío (cold starts) de funciones Lambda innecesarias.
- Buffer de resiliencia y amortiguación de carga: Cómo utilizar Amazon SQS como shock absorber para retener mensajes hasta por 4 días ante cortes de energía o caídas de conectividad.
- Manejo de fallos parciales en lotes (
ReportBatchItemFailures): Cómo procesar eventos en lotes (batching) en AWS Lambda sin reprocesar mensajes sanos cuando un único registro malformado (poison pill) falla. - Deduplicación geoespacial determinística por Geohash: Cómo convertir coordenadas de latitud y longitud en celdas de precisión 6 (~1.2 km²) para agrupar reportes idénticos y evitar saturar a los rescatistas con alertas duplicadas.
- Persistencia NoSQL y ciclo de vida con FinOps: Cómo estructurar Amazon DynamoDB con Single-Table Design y habilitar Time-To-Live (TTL) para que los registros históricos expiren automáticamente a costo cero.
- Despacho prioritario con Amazon SNS: Cómo filtrar eventos en tiempo real y emitir alertas inmediatas hacia Defensa Civil y servicios de emergencia en menos de dos segundos.
Arquitectura de la Solución
A continuación se presenta el diagrama completo de la arquitectura implementada, diseñado siguiendo los estándares oficiales de AWS Architecture Center:
Flujo de Datos Paso a Paso:
- Ingesta Ciudadana (PWA / Sensores IoT): El usuario o sensor envía un payload JSON mediante una petición HTTP POST segura hacia
/reports. - Amazon API Gateway (HTTP API): Endpoint de bajísima latencia configurado con integración de servicio nativa hacia Amazon SQS. Deposita el mensaje en el buffer sin ejecutar código intermedio y responde inmediatamente con código HTTP 200/202 al cliente en menos de 35 milisegundos.
- Amazon SQS (Buffer Principal): La cola
resq-reports-queueamortigua el pico de tráfico. Si el backend procesador se detiene temporalmente, los mensajes permanecen seguros. - Amazon SQS (Dead-Letter Queue - DLQ): Si un mensaje falla tras 3 intentos sucesivos de procesamiento (
maxReceiveCount = 3), se redirige aresq-reports-dlqpara aislar cargas dañinas sin bloquear el lote. - AWS Lambda (Report Processor): Se activa mediante un Event Source Mapping configurado en lotes de 10 elementos y ventana de acumulación de 5 segundos. Decodifica las coordenadas, calcula el Geohash y procesa cada registro de forma aislada.
- Amazon DynamoDB: Almacena el reporte estructurado con clave de partición zonal (
GEO#<hash>) y marca de tiempo con TTL de 7 días para depuración automática sin consumo de capacidad de escritura (WCU). - Amazon SNS (Topic de Alertas): Si el reporte presenta severidad crítica (nivel de agua superior a 1.5 metros o personas atrapadas), se despacha una notificación prioritaria a los centros de comando y brigadas de rescate.
Paso 1: Ingesta Directa sin Cómputo (API Gateway HTTP API a SQS)
Uno de los antipatrones más frecuentes en arquitecturas Serverless novatas es colocar una función Lambda entre API Gateway y SQS únicamente para ejecutar un comando
sqs.sendMessage().Esta práctica introduce tres desventajas severas:
- Sobrecosto financiero: Estás pagando una invocación de Lambda por cada reporte entrante, duplicando el consumo del Free Tier.
- Latencia innecesaria y Cold Starts: Las peticiones sufren retrasos de inicialización del entorno de ejecución de Lambda (200ms a 1000ms en arranques en frío).
- Riesgo de agotamiento de concurrencia: Una ráfaga súbita consume el límite regional de concurrencia de Lambda (1.000 ejecuciones concurrentes por defecto), bloqueando otros servicios críticos de tu cuenta de AWS.
API Gateway cuenta con integraciones de servicio directas (Service Integrations) capaces de interactuar nativamente con SQS sin necesidad de cómputo intermediario.
Definición de Infraestructura como Código en Terraform:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# Rol IAM para que API Gateway deposite mensajes en SQS
resource "aws_iam_role" "apigw_sqs_role" {
name = "resq-apigw-sqs-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = { Service = "apigateway.amazonaws.com" }
}]
})
}
# Política con principio de mínimo privilegio
resource "aws_iam_policy" "apigw_sqs_policy" {
name = "resq-apigw-sqs-policy"
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = "sqs:SendMessage"
Resource = aws_sqs_queue.reports_queue.arn
}]
})
}
resource "aws_iam_role_policy_attachment" "apigw_sqs_attach" {
role = aws_iam_role.apigw_sqs_role.name
policy_arn = aws_iam_policy.apigw_sqs_policy.arn
}
# API Gateway HTTP API
resource "aws_apigatewayv2_api" "resq_api" {
name = "resq-cloud-http-api"
protocol_type = "HTTP"
}
# Integración directa HTTP API -> SQS SendMessage
resource "aws_apigatewayv2_integration" "sqs_integration" {
api_id = aws_apigatewayv2_api.resq_api.id
integration_type = "AWS_PROXY"
integration_subtype = "SQS-SendMessage"
credentials_arn = aws_iam_role.apigw_sqs_role.arn
payload_format_version = "1.0"
request_parameters = {
QueueUrl = aws_sqs_queue.reports_queue.url
MessageBody = "$request.body"
}
}
# Ruta POST /reports
resource "aws_apigatewayv2_route" "reports_route" {
api_id = aws_apigatewayv2_api.resq_api.id
route_key = "POST /reports"
target = "integrations/${aws_apigatewayv2_integration.sqs_integration.id}"
}
# Despliegue automático de stage
resource "aws_apigatewayv2_stage" "default_stage" {
api_id = aws_apigatewayv2_api.resq_api.id
name = "$default"
auto_deploy = true
}Paso 2: Procesador Lambda con Manejo Parcial de Lotes (ReportBatchItemFailures)
Cuando configuramos Amazon SQS como disparador (Event Source Mapping) de AWS Lambda con procesamiento por lotes (
batch_size = 10), el comportamiento estándar ante errores no controlados es catastrófico: si 9 mensajes son válidos pero el décimo contiene un JSON malformado y la función arroja una excepción, AWS asume que falló el lote completo.Como consecuencia:
- Los 9 mensajes sanos vuelven a ingresar a la cola.
- Se reintentan en el siguiente ciclo, duplicando operaciones en DynamoDB.
- Se consumen invocaciones adicionales de cómputo innecesariamente.
- El ciclo continúa hasta agotar el
maxReceiveCount, enviando injustamente los 9 reportes válidos a la Dead-Letter Queue.
Para solucionar este desafío de arquitectura, habilitamos la propiedad
ReportBatchItemFailures en la configuración del Event Source Mapping de Lambda y retornamos un array con los identificadores exactos de los registros que fallaron (batchItemFailures). De este modo, SQS elimina del buffer los mensajes exitosos y únicamente reintenta el mensaje defectuoso.Implementación del Handler en TypeScript (Node.js 20 LTS):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import { SQSBatchResponse, SQSEvent } from "aws-lambda";
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, PutCommand } from "@aws-sdk/lib-dynamodb";
import ngeohash from "ngeohash";
const ddb = DynamoDBDocumentClient.from(new DynamoDBClient({}));
const TABLE_NAME = process.env.TABLE_NAME || "resq-cloud-reports";
export const handler = async (event: SQSEvent): Promise<SQSBatchResponse> => {
const batchItemFailures: { itemIdentifier: string }[] = [];
for (const record of event.Records) {
try {
const payload = JSON.parse(record.body);
const { latitude, longitude, severity, description, contact } = payload;
// Validación estricta de coordenadas geográficas
if (typeof latitude !== "number" || typeof longitude !== "number") {
throw new Error("Coordenadas geográficas inválidas o ausentes");
}
// Geohash de precisión 6 (~1.2 km x 0.6 km) para deduplicación zonal
const zoneHash = ngeohash.encode(latitude, longitude, 6);
const timestamp = Math.floor(Date.now() / 1000);
const ttl = timestamp + (7 * 24 * 60 * 60); // Expira en 7 días para optimización FinOps
// Escritura idempotente en DynamoDB
await ddb.send(
new PutCommand({
TableName: TABLE_NAME,
Item: {
PK: `GEO#${zoneHash}`,
SK: `TIME#${timestamp}#${record.messageId}`,
latitude,
longitude,
severity: severity || "MEDIUM",
description: description || "Sin descripción provista",
contact: contact || null,
ttl,
},
})
);
} catch (err) {
console.error(`Error procesando mensaje individual ${record.messageId}:`, err);
// Retornamos únicamente el identificador del mensaje defectuoso
batchItemFailures.push({ itemIdentifier: record.messageId });
}
}
return { batchItemFailures };
};Configuración del Event Source Mapping en Terraform:
1
2
3
4
5
6
7
resource "aws_lambda_event_source_mapping" "sqs_processor" {
event_source_arn = aws_sqs_queue.reports_queue.arn
function_name = aws_lambda_function.report_processor.arn
batch_size = 10
maximum_batching_window_in_seconds = 5
function_response_types = ["ReportBatchItemFailures"]
}Paso 3: Deduplicación Geoespacial y Modelado en DynamoDB
Durante un desastre, docenas de personas reportan el mismo incidente desde cuadras contiguas. Si procesáramos cada alerta de forma aislada, saturaríamos a las cuadrillas de rescate con llamados redundantes para la misma esquina.
¿Por qué Geohash?
El algoritmo Geohash es una función hash espacial jerárquica que subdivide la superficie terrestre en una cuadrícula rectangular codificada en Base32:
- Precisión 1: ~5.000 km x 5.000 km (continente).
- Precisión 5: ~4.9 km x 4.9 km (distrito urbano).
- Precisión 6: ~1.2 km x 0.6 km (vecindario / radio de cobertura de una unidad de bomberos).
- Precisión 8: ~38 metros x 19 metros (esquina o edificio específico).
Al transformar
(latitude: -34.6037, longitude: -58.3816) en el hash 69y7pu, obtenemos una clave de partición determinística (PK: GEO#69y7pu). Cuando los equipos de rescate consultan el estado de una zona, ejecutan una sola operación Query en DynamoDB sobre la partición zonal, obteniendo todos los reportes consolidados en orden cronológico inverso (SK: TIME#<timestamp>).FinOps: Depuración Automática con Time-To-Live (TTL)
En sistemas de emergencia, la información operativa pierde relevancia crítica tras 72 horas. Sin embargo, en bases de datos tradicionales, las tablas continúan creciendo indefinidamente, obligando a pagar almacenamiento mensual y capacidad de lectura/escritura para ejecutar scripts de borrado masivo (
DELETE WHERE).Habilitando DynamoDB TTL en el atributo
ttl, AWS se encarga de eliminar de forma automática y asíncrona todos los elementos vencidos en segundo plano sin costo de capacidad de escritura (WCU) y sin impacto en el rendimiento de producción.Análisis de los 5 Pilares de AWS Well-Architected Framework
Para validar que ResQ-Cloud cumple con los estándares de excelencia técnica promovidos por los AWS Heroes en el Bootcamp, evaluamos la arquitectura frente a los pilares fundamentales del Well-Architected Framework:
1. Excelencia Operativa (Operational Excellence)
- Infraestructura como Código (IaC): Toda la infraestructura está declarada mediante módulos reproducibles en Terraform v1.8+, eliminando derivas de configuración manual en consola.
- Trazabilidad: AWS Lambda emite métricas nativas a Amazon CloudWatch, registrando latencia de ejecución, errores de deserialización y volumen de mensajes en cola.
2. Seguridad (Security)
- Principio de Mínimo Privilegio: API Gateway solo cuenta con permisos estrictos de
sqs:SendMessagesobre el ARN específico de la cola principal. No se otorgan permisos comodín (*). - Cifrado en Reposo y en Tránsito: API Gateway expone endpoints exclusivamente bajo HTTPS (TLS 1.2+). Amazon SQS y DynamoDB tienen cifrado en reposo habilitado por defecto mediante claves administradas por AWS (SSE-KMS).
3. Fiabilidad (Reliability)
- Desacoplamiento Estricto: Si la función Lambda experimenta un fallo temporal o se satura la concurrencia, SQS almacena los mensajes hasta por 4 días sin pérdida de información.
- Aislamiento de Cargas Venenosas: La cola DLQ evita que mensajes corruptos generen bucles infinitos de reintento.
4. Eficiencia del Rendimiento (Performance Efficiency)
- Latencia de Ingesta: La integración directa de API Gateway a SQS registra latencias p99 inferiores a 40 milisegundos.
- Agrupamiento en Lotes: El batching de 10 elementos por invocación reduce en un 90% el número total de llamadas a Lambda y DynamoDB.
5. Optimización de Costos (Cost Optimization)
- Costo Cero en Reposo: No existen servidores aprovisionados, balanceadores de carga ALB ni instancias EC2 en ejecución.
- Free Tier Perpetuo y de 12 Meses: Con 1.000.000 de llamadas mensuales en API Gateway, 1.000.000 de solicitudes en SQS, 1.000.000 de invocaciones en Lambda y 25 GB en DynamoDB, la factura mensual para un municipio mediano es exactamente $0,00 USD.
Criterio Técnico y Trade-Offs: ¿Cuándo NO usar esta solución?
Un verdadero arquitecto de soluciones no se enamora ciegamente de una tecnología; comprende con claridad sus limitaciones y cuándo debe elegir un enfoque alternativo.
- Cuándo NO usar SQS Standard: SQS Standard ofrece rendimiento casi ilimitado a costa de no garantizar orden temporal estricto (best-effort ordering) y permitir duplicaciones ocasionales (at-least-once delivery). Si tu aplicación requiere orden FIFO al milisegundo absoluto, debes utilizar SQS FIFO o migrar hacia Amazon Kinesis Data Streams.
- Cuándo NO usar flujo asíncrono puro: Si el cliente móvil necesita saber en la misma llamada HTTP si su reporte fue consolidado con otro en tiempo real, un diseño asíncrono con SQS no es adecuado. En ese caso se requiere una arquitectura basada en API Gateway WebSocket API o AWS AppSync (GraphQL Subscriptions) con comunicación bidireccional.
Pruebas de Funcionamiento y Verificación
Una vez desplegada la infraestructura mediante
terraform apply, podemos validar la ingesta con una llamada cURL simulando un reporte de emergencia real:1
2
3
4
5
6
7
8
9
curl -X POST https://<tu-api-id>.execute-api.us-east-1.amazonaws.com/reports \
-H "Content-Type: application/json" \
-d '{
"latitude": -34.6037,
"longitude": -58.3816,
"severity": "CRITICAL",
"description": "Nivel de agua 1.4m en San Martín y Rivadavia",
"contact": "+5491112345678"
}'Respuesta HTTP esperada (en menos de 35ms):
1
2
3
4
{
"status": "queued",
"messageId": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d"
}Al inspeccionar la tabla en DynamoDB y los logs en CloudWatch Insights, confirmamos que el Geohash fue calculado correctamente (
GEO#69y7pu), el TTL quedó programado para dentro de 7 días y el reporte fue procesado sin colapsar la infraestructura.Conclusión
Diseñar para el mundo real exige anticiparse a la catástrofe. ResQ-Cloud demuestra que combinando los servicios fundamentales de AWS (API Gateway, SQS, Lambda y DynamoDB) con sólidos principios de arquitectura es posible construir plataformas de rescate y auxilio civil de altísimo impacto social, invulnerables a picos de tráfico y con un presupuesto operativo que cabe íntegramente en la Capa Gratuita.
El código fuente completo con las plantillas de Terraform y el handler TypeScript está disponible en el repositorio de GitHub:
👉 https://github.com/DavidBritto/resq-cloud
👉 https://github.com/DavidBritto/resq-cloud
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article