El reto
Una plataforma de analítica ingería eventos de producto de una base de clientes en crecimiento en una única tabla "raw_events". El esquema original era correcto pero lento: cada evento llegaba a una tabla con registro y siete índices, el WAL era el cuello de botella y el rendimiento de ingesta se había estancado en unos 8,000 eventos por segundo. Las IOPS de RDS eran la partida de costo dominante, y una expansión prevista de clientes de 3 veces iba a romper el pipeline antes de firmar el siguiente contrato.
El equipo había leído los consejos habituales — "use una cola", "fragmente la tabla", "pase a una TSDB dedicada" — pero todas esas respuestas implicaban una migración de varios trimestres. La verdadera pregunta era: ¿puede PostgreSQL seguir el ritmo si dejamos de pelear con él? La dificultad era un requisito estricto: todo lo que llegara a las tablas de informes auditados debía ser duradero, consistente e indexado. No podíamos sacrificar la integridad en el lado de analítica para salvar el lado de ingesta.
Nuestra solución
Dividimos el pipeline en dos etapas claramente separadas con dos contratos de durabilidad distintos.
Etapa 1 (ingesta): una tabla de staging UNLOGGED que recibe el torrente de datos. Las tablas UNLOGGED de PostgreSQL omiten las escrituras WAL en las inserciones, lo cual es justo la compensación adecuada para datos de staging de corta vida — obtenemos de 5 a 10 veces más rendimiento de inserción bruta a cambio de perder las filas en staging si el servidor se cae. Combinado con COPY por lotes (no INSERT) desde un servicio de ingesta en Python y una regla deliberada de "sin índices en la tabla de staging", la ingesta bruta pasó de 8K a más de 80K eventos/seg en la misma instancia RDS.
Etapa 2 (duradera): un worker de Celery vacía la tabla de staging en microlotes hacia la tabla real `events`, con registro completo e índices, dentro de una única transacción con upserts idempotentes. La ruta de informes auditados solo lee de la tabla duradera. Si el servidor se cae durante la ingesta, perdemos como máximo unos segundos de eventos en tránsito de la tabla de staging — y los productores originales reintentan, de modo que la tabla duradera converge de nuevo a lo correcto.
Lo complementamos con una capa operativa pequeña pero cuidadosa: un panel de Grafana para la tasa de llenado de la etapa 1, una alerta crítica si el drenador se retrasa, rotación de particiones en la tabla duradera y un runbook escrito para los tres modos de fallo que importan.
- Tabla de staging UNLOGGED sin índices — diseñada específicamente para el rendimiento de inserción bruta
- Ingesta COPY por lotes desde un servicio Python/FastAPI, no INSERT fila por fila
- Drenador Celery que mueve microlotes a la tabla de eventos duradera y totalmente indexada
- Upserts idempotentes (ON CONFLICT DO NOTHING) para que los reintentos de los productores sean seguros
- Particionado mensual en la tabla duradera para acotar el trabajo de vacuum e índices
- Paneles y alertas de latencia de extremo a extremo en Grafana / Datadog
- Runbook escrito que cubre la conmutación por error de RDS, la caída del drenador y la contrapresión de los productores
- Aprobación de auditoría del perfil de durabilidad antes del lanzamiento — sin sorpresas