Retour au Blog
Databases & Backend19 novembre 202511 min de lecture

Scaler Postgres dans son écosystème : quand Citus, TimescaleDB et Supavisor deviennent la bonne réponse

DR

Doug Ribeiro

11 min de lecture

Le post sur l'échelle d'adoption rangeait les écritures multi-région, les 100M+ vecteurs et les millions d'événements par seconde dans le Palier 4 : à éviter. Ce plafond tient toujours pour une instance Postgres unique.

Ici, on parle du palier intermédiaire que la plupart des plaidoyers "just use Postgres" escamotent : cet espace entre "Postgres vanilla encaisse" et "il faut passer à un système distribué". Entre les deux, il y a de la place, et le conseil par défaut ("graduer vers un outil spécialisé") est souvent prématuré. Bien souvent, la bonne réponse consiste à graduer à l'intérieur de l'écosystème Postgres, en s'appuyant sur des extensions et des sidecars qui règlent un point de douleur précis sans sacrifier tout ce que Postgres fait bien : SQL, ACID, l'outillage, le bassin de recrutement, une histoire de backup mature.

Quatre axes de scaling couvrent l'essentiel de ce terrain intermédiaire. Voici ce que je sortirais sur chacun, et à peu près à quel moment.

Scaling des connexions : quand PgBouncer ne suffit plus

PgBouncer est une exigence de Palier 2 dans l'échelle. Pour la plupart des charges, c'est toute la réponse. Mais PgBouncer lui-même est single-threaded et CPU-bound. Dès que vous approchez des ~10 000 connexions clients et que son CPU reste durablement au-dessus de 70 %, il faut passer à l'étape suivante.

Plusieurs instances PgBouncer derrière un load balancer

Simple, fiable, la réponse de manuel pour gagner encore un facteur ou deux. Chaque instance applicative dialogue avec un PgBouncer local, et le pool derrière est réparti sur plusieurs processus PgBouncer. Le coût opérationnel se résume surtout à vérifier que la somme des tailles de pool reste dans ce que Postgres peut réellement encaisser.

Supavisor

Le pooler en Elixir de Supabase, bâti précisément parce que PgBouncer s'essoufflait à l'échelle multi-tenant de Supabase. Architecture multi-tenant, multi-nœuds, cloud-native. À regarder si vous opérez du Postgres managé à grande échelle, ou si vous construisez vous-même un database-as-a-service.

PgCat

Une alternative à PgBouncer écrite en Rust, avec sharding, failover et query routing intégrés. Plus opinionné que PgBouncer : vous gagnez des features, vous signez pour plus de configuration.

Quand graduer

CPU de PgBouncer soutenu au-dessus de 70 %, p99 du temps d'attente en connexion qui grimpe, ou une empreinte de déploiement où faire tourner N sidecars PgBouncer par stack applicative commence à sentir mauvais côté ops. Si rien de tout ça n'est vrai, restez sur PgBouncer et gardez la migration pour plus tard.

Scaling time-séries : quand BRIN et partitionnement ne suffisent plus

L'échelle recommandait BRIN plus partitionnement déclaratif pour le time-séries. Ça vous emmène confortablement à des dizaines de millions de lignes par jour. Au-delà, vous voulez TimescaleDB.

Ce que vous gagnez par-dessus le partitionnement vanilla :

  • Hypertables. Partitionnement automatique avec des défauts sensés, aucune gestion manuelle des partitions. Vous créez une hypertable, vous insérez, elle s'occupe du reste.
  • Agrégats continus. Des vues matérialisées qui se mettent à jour de manière incrémentale à mesure que les données arrivent, au lieu d'un refresh complet. Si vous servez des dashboards sur des milliards de points, c'est la feature qui rend la chose possible.
  • Compression native. 10 à 20x d'économie disque sur les anciennes partitions, pour une pénalité de requête minime. Critique dès que vous gardez plus de quelques mois de données à haute cardinalité.
  • Politiques de rétention et tiering de données comme citoyens de première classe, pas comme une collection de cron jobs.

Le vrai bémol : TimescaleDB a un modèle en double licence (en place depuis 2018, pas un changement récent, n'en déplaise à la confusion qui circule). Le cœur est sous Apache 2.0, tandis que le tier "Community" (la compression native, les agrégats continus et leurs politiques de refresh, les politiques de rétention) vit sous la Timescale License (TSL). La TSL est permissive en self-hosted : la seule activité qu'elle interdit, c'est d'offrir TimescaleDB lui-même en tant que database-as-a-service managée.

À noter : l'entreprise est passée de Timescale à Tiger Data en juin 2025, ce qui a semé pas mal de doute quant à un éventuel changement de licence. Il n'y en a pas eu. Lisez la licence avant de vous engager, surtout si vous redistribuez du logiciel, mais pour la plupart des équipes c'est un non-sujet.

Seuil de graduation : vous gardez plus de 30 à 60 jours de données sur une seule instance Postgres, les requêtes sur les vieilles partitions deviennent mesurablement plus lentes que sur les récentes, et vous commencez à écrire des scripts maison pour gérer le cycle de vie des partitions.

Sharding horizontal : quand le scaling vertical s'épuise

Postgres monte verticalement à des tailles impressionnantes. Une instance cloud moderne vous offre 96 vCPU et 768 Go de RAM sans effort. Pour la plupart des équipes, ce plafond est purement théorique, elles ne l'atteindront jamais.

Pour celles qui l'atteignent, Citus est la réponse. Racheté par Microsoft en 2019, c'est le moteur derrière Azure Cosmos DB for PostgreSQL (que Microsoft est en train de retirer pour les nouveaux projets au profit des elastic clusters d'Azure Database for PostgreSQL, eux aussi bâtis sur Citus), et il est disponible en extension open source installable sur n'importe quelle infra. Vous déclarez quelles tables sont distribuées (shardées par une clé choisie entre plusieurs nœuds workers) et lesquelles sont des tables de référence (répliquées sur chaque worker pour des jointures peu coûteuses). Les requêtes sont parallélisées entre workers de manière transparente, quand le planner le peut.

Ce que Citus ne fera pas : rendre votre schéma distributed-friendly gratuitement. Il faut toujours choisir une clé de shard par table distribuée, et les transactions cross-shard ne sont pas gratuites. La charge qui colle le mieux, c'est le SaaS multi-tenant shardé par tenant ID : les requêtes cross-tenant sont rares, les requêtes intra-tenant restent locales à un seul shard. C'est sur les jointures complexes cross-entité entre shards qu'on sent la contrainte.

Seuil de graduation : votre primaire est durablement CPU-bound, vous ne pouvez plus acheter de machine plus grosse ou elle devient absurdement chère, ou votre volume d'écriture dépasse ce que le WAL d'un seul nœud peut soutenir.

Scaling des lectures : quand un seul primaire ne peut plus servir vos lectures

La réplication en streaming (physique) vous donne des hot standbys. Elle expédie le WAL octet par octet du primaire vers la réplique, donc la réplique est en permanence une copie exacte du primaire sur la même version majeure de Postgres. Vous pouvez y router du trafic de lecture. Terrain bien balisé, supporté en natif depuis Postgres 9.

La réplication logique, elle, est la cousine sous-estimée. Au lieu de streamer les octets du WAL, elle décode le WAL en événements au niveau ligne (INSERT/UPDATE/DELETE) et expédie ceux-ci à un subscriber. Le subscriber peut tourner sur une version majeure de Postgres différente, ne s'abonner qu'à certaines tables, et avoir ses propres index. L'infrastructure de logical decoding est dans Postgres depuis 9.4, mais la réplication logique native en publish/subscribe est arrivée avec Postgres 10, et c'est celle que la plupart des équipes utilisent en pratique aujourd'hui. Quelques cas d'usage à avoir en tête :

  • Réplique analytique dédiée, avec des index différents optimisés pour les requêtes de reporting. Votre primaire OLTP reste léger, vos rapports tournent sur une réplique aux index aggregate-friendly.
  • Upgrades de version majeure sans interruption. Vous répliquez de v15 vers v17, vous vérifiez, vous basculez. Pas de longue fenêtre de maintenance.
  • CDC sans Debezium. La réplication logique, c'est déjà du CDC au niveau Postgres. Pour pas mal d'usages downstream (index de recherche, sync vers un data warehouse), ça suffit largement.
  • Sharding sélectif par poids de tenant. Les tenants chauds basculent sur une machine plus grosse via réplication logique de leurs seules lignes.

Outils à avoir en tête : CREATE PUBLICATION / CREATE SUBSCRIPTION en natif, et pglogical pour les scénarios que l'outillage natif ne couvre pas.

Quand il faut vraiment partir

Le vrai test, c'est de savoir si le plafond que vous touchez est opérationnel ou structurel.

Les plafonds opérationnels restent dans l'écosystème Postgres :

  • Plus assez de connexions. Problème de pooler.
  • Plus assez de disque à cause des partitions. Problème pour TimescaleDB.
  • Plus assez de CPU sur le primaire. Problème pour Citus ou pour du scaling vertical.
  • Rapports lents. Problème de réplique de lecture.

Les plafonds structurels, eux, signalent qu'il est temps d'ajouter un outil spécialisé :

  • Latence sous la milliseconde sous contention d'écriture extrême. Les stores en mémoire (Redis, DragonflyDB) existent parce que la mémoire n'est pas le disque.
  • Vraies écritures multi-région actif-actif avec forte cohérence. CockroachDB, Spanner et YugabyteDB résolvent nativement le problème du consensus distribué. Postgres, non ; et bricoler ça par-dessus avec de l'outillage tiers, c'est échanger un ensemble de problèmes contre un pire.
  • Vraies charges analytiques sur des milliards de lignes. ClickHouse, DuckDB et Snowflake reposent sur des architectures franchement différentes, et ce n'est pas un hasard. Des extensions comme pg_duckdb et ParadeDB réduisent l'écart pour les besoins OLAP modestes, mais ne le comblent pas au sommet.
  • Event streaming à l'échelle de Kafka. Si vous avez vraiment besoin de millions d'événements par seconde avec replay, partitioning, consumer groups et fan-out, prenez Kafka.

Mon test : si vous ne pouvez pas nommer l'extension ou le sidecar Postgres qui règle votre problème, et que le plafond est architectural plutôt qu'opérationnel, il est temps d'ajouter un autre outil. Si vous pouvez nommer l'extension qui le règle (et surtout si elle a une licence raisonnable et une communauté vivante), restez.

La plupart des équipes décident qu'elles ont besoin d'une base distribuée environ un an avant d'en avoir vraiment besoin. Étendre Postgres avec la bonne extension leur rachète presque toute cette année, le plus souvent pour une fraction du coût.

Références

PostgreSQLDatabasesScalingArchitecture