Volver al Blog
Databases & Backend19 de noviembre de 202511 min de lectura

Escalando Postgres: cuándo Citus, TimescaleDB y Supavisor son la respuesta

DR

Doug Ribeiro

11 min de lectura

El post de la escalera de adopción metió las escrituras multi-región, los 100M+ vectores y los millones de eventos por segundo en el Nivel 4: no lo intentes. Ese techo sigue siendo correcto para una sola instancia de Postgres.

Lo que quiero tratar aquí es el nivel intermedio que la mayoría de los argumentos 'just use Postgres' saltan: el espacio entre 'Postgres vanilla aguanta' y 'toca cambiar a un sistema distribuido'. Ahí hay mucho más terreno del que parece, y el consejo por defecto ('gradúate a una herramienta especializada') suele ser prematuro. A veces la respuesta correcta es graduarse dentro del propio ecosistema Postgres, con extensiones y sidecars que resuelven un dolor concreto sin renunciar a todo lo que Postgres hace bien: SQL, ACID, el tooling, el mercado de talento, una historia de backups madura.

Cuatro ejes de escalado cubren casi todo ese terreno intermedio. Te cuento a qué recurriría en cada uno, y aproximadamente cuándo.

Escalado de conexiones: cuando PgBouncer no da más

PgBouncer es requisito de Nivel 2 en la escalera. Para la mayoría de las cargas, es toda la respuesta. El problema es que PgBouncer mismo es single-threaded y está limitado por CPU. Una vez que te acercas a las ~10 000 conexiones de cliente y el CPU de PgBouncer se mantiene sostenido por encima del 70%, necesitas el siguiente paso.

Varias instancias de PgBouncer detrás de un load balancer

Simple, confiable, la respuesta de manual para ganar otro múltiplo de escala. Cada instancia de app habla con un PgBouncer local, y el pool detrás se reparte entre varios procesos de PgBouncer. El costo operativo está sobre todo en asegurarte de que los tamaños de pool siguen sumando algo que Postgres realmente pueda manejar.

Supavisor

El pooler en Elixir de Supabase, construido justamente porque PgBouncer se quedó sin pista a la escala multi-tenant de Supabase. Arquitectura multi-tenant, multi-nodo, cloud-native. Míralo si corres Postgres gestionado a escala o si estás construyendo tu propia database-as-a-service.

PgCat

Una alternativa a PgBouncer escrita en Rust, con sharding, failover y query routing integrados. Más opinionado que PgBouncer: ganas más features, firmas por más configuración.

Cuándo graduarse

CPU de PgBouncer sostenido por encima del 70%, p99 de tiempo de espera de conexión que va subiendo, o un footprint de despliegue donde correr N sidecars de PgBouncer por stack de app empieza a oler raro desde ops. Si no se cumple ninguna de esas, quédate en PgBouncer y deja la migración para después.

Escalado de series temporales: cuando BRIN y particionamiento no bastan

La escalera recomendaba índices BRIN más particionamiento declarativo para series temporales. Con eso llegas cómodo a decenas de millones de filas por día. Por encima, quieres TimescaleDB.

Lo que ganas sobre el particionamiento vanilla:

  • Hypertables. Particionamiento automático con defaults sensatos, sin gestión manual de particiones. Creas la hypertable, insertas, y ella se ocupa del resto.
  • Agregados continuos. Vistas materializadas que se actualizan incrementalmente a medida que llegan datos nuevos, en vez de hacer un refresh completo. Si sirves dashboards sobre miles de millones de puntos, esta es la feature que lo hace posible.
  • Compresión nativa. Ahorros de disco de 10-20x en particiones viejas con penalidad mínima en queries. Crítico para cualquiera que guarde más de unos meses de datos de alta cardinalidad.
  • Políticas de retención y data tiering como features de primera clase, no como cron jobs.

Para ser honestos, la pega: TimescaleDB tiene un modelo de licencia dual (vigente desde 2018, no es un cambio reciente por más confusión que haya online). El core es Apache 2.0, mientras que el tier 'Community' de features (compresión nativa, agregados continuos y sus políticas de refresh, políticas de retención) vive bajo la Timescale License (TSL). La TSL es permisiva para uso self-hosted; lo único que prohíbe es ofrecer TimescaleDB como database-as-a-service gestionada.

Por cierto, la empresa pasó a llamarse Tiger Data en junio de 2025 (antes era Timescale), lo que causó cierta confusión online sobre si la licencia había cambiado. No cambió. Lee la licencia antes de comprometerte, sobre todo si redistribuyes software, pero para la mayoría de los equipos es un no-tema.

Umbral de graduación: guardas más de 30 a 60 días de datos en una sola instancia de Postgres, las queries contra particiones viejas son mensurablemente más lentas que contra las recientes, y ya estás escribiendo scripts propios para gestionar el ciclo de vida de las particiones.

Sharding horizontal: cuando el escalado vertical se agota

Postgres escala verticalmente a tamaños notables. Una instancia cloud moderna te da 96 vCPU y 768GB de RAM sin despeinarse. Para la mayoría de los equipos ese techo es teórico y nunca lo tocan.

Para los que sí lo tocan, la respuesta es Citus. Adquirido por Microsoft en 2019, es el motor detrás de Azure Cosmos DB for PostgreSQL (que Microsoft está retirando para proyectos nuevos en favor de los elastic clusters de Azure Database for PostgreSQL, también construidos sobre Citus), y está disponible como extensión open source que puedes correr en cualquier infraestructura. Declaras qué tablas son distribuidas (shardeadas por una clave que tú eliges a través de los nodos worker) y cuáles son tablas de referencia (replicadas a cada worker para que los joins salgan baratos). Las queries se paralelizan entre workers de forma transparente cuando el planner puede.

Lo que Citus no te hace gratis: volver tu schema amigable con entornos distribuidos. Sigues teniendo que elegir una clave de shard por cada tabla distribuida, y las transacciones cross-shard no son gratis. La carga que mejor encaja es SaaS multi-tenant shardeado por tenant ID, porque las queries cross-tenant son raras y las intra-tenant se quedan locales a un solo shard. Los joins complejos entre entidades distintas y entre shards son justamente donde empiezas a sentir las restricciones.

Umbral de graduación: tu primary vive consistentemente CPU-bound, no puedes comprar una máquina más grande o la más grande ya resulta absurdamente cara, o tu volumen de escrituras superó lo que el WAL de un solo nodo puede sostener.

Escalado de lecturas: cuando un solo primary no alcanza

La replicación streaming (física) te da hot standbys. Envía el WAL byte por byte del primary a la réplica, así que la réplica es siempre una copia exacta del primary corriendo la misma versión mayor de Postgres. Puedes rutear tráfico de lectura hacia ellos. Es terreno muy pisado, soportado out of the box desde Postgres 9.

La replicación lógica es la prima subestimada. En lugar de enviar los bytes del WAL, decodifica el WAL en eventos a nivel de fila (INSERT/UPDATE/DELETE) y los envía a un suscriptor. El suscriptor puede correr una versión mayor distinta de Postgres, suscribirse solo a algunas tablas y tener sus propios índices. La infraestructura de logical decoding que hay debajo vive en Postgres desde la 9.4, pero la replicación lógica nativa publish/subscribe llegó en Postgres 10, y es la que la mayoría de los equipos usa hoy en el día a día. Replica tablas seleccionadas a otra instancia de Postgres, que incluso puede correr una versión mayor distinta. Casos de uso que vale la pena conocer:

  • Réplica analítica dedicada, con otros índices optimizados para queries de reporting. Tu primary OLTP se mantiene liviano; los reportes corren sobre una réplica con índices pensados para agregaciones.
  • Upgrades de versión mayor con cero downtime. Replicas de v15 a v17, verificas, haces el cutover. Sin ventana de mantenimiento larga.
  • CDC sin Debezium. La replicación lógica ya es CDC a nivel Postgres. Para muchos consumidores downstream (índice de búsqueda, sync a data warehouse), con eso basta.
  • Sharding selectivo por peso de tenant. Los tenants calientes se van a una máquina más grande vía replicación lógica de solo sus filas.

Herramientas que vale la pena conocer: CREATE PUBLICATION / CREATE SUBSCRIPTION nativos, y pglogical para los escenarios que el tooling nativo no cubre.

Cuándo irse de verdad

El test honesto es si el techo contra el que estás chocando es operativo o estructural.

Los techos operativos se quedan en el ecosistema Postgres:

  • Te quedas sin conexiones. Problema de pooler.
  • Te quedas sin disco por las particiones. Problema de TimescaleDB.
  • Te quedas sin CPU en el primary. Problema de Citus o de escalado vertical.
  • Los reportes van lentos. Problema de réplica de lectura.

Los techos estructurales son señal de que toca sumar una herramienta especializada:

  • Latencia sub-milisegundo bajo contención de escritura extrema. Los stores in-memory (Redis, DragonflyDB) existen por algo: la memoria no es el disco.
  • Escrituras activo-activo multi-región de verdad, con consistencia fuerte. CockroachDB, Spanner y YugabyteDB resuelven el consenso distribuido de forma nativa. Postgres no, y atornillárselo por encima con tooling de terceros solo cambia un set de problemas por otro peor.
  • Cargas analíticas reales sobre miles de millones de filas. ClickHouse, DuckDB y Snowflake son arquitecturas genuinamente distintas por una razón. Extensiones como pg_duckdb y ParadeDB acortan la distancia para necesidades OLAP más chicas, pero no la cierran en la gama alta.
  • Event streaming a escala Kafka. Si de verdad necesitas millones de eventos por segundo con replay, particionamiento, consumer groups y fan-out, usa Kafka.

El test que uso: si no puedes nombrar la extensión o sidecar concreto que resuelve tu problema dentro de Postgres, y el techo que estás pegando es arquitectónico más que operativo, ya toca meter otra herramienta. Si sí puedes nombrar la extensión que lo resuelve (y sobre todo si tiene una licencia razonable y una comunidad viva), quédate.

La mayoría de los equipos decide que necesita una base distribuida más o menos un año antes de necesitarla. Extender Postgres con la extensión adecuada te devuelve casi todo ese año, y a una fracción del costo.

Referencias

PostgreSQLDatabasesScalingArchitecture