PostgreSQL vs CockroachDB : HA sans drame — ou HA avec de nouvelles douleurs

Cet article vous a aidé ?

On n’achète pas la « haute disponibilité » sur une diapositive. On l’exécute. À 2 h du matin. Quand un rack saute, un routeur redémarre,
un déploiement tourne mal et votre téléphone d’astreinte commence à faire du cardio.

PostgreSQL peut être ennuyeusement fiable en production — si vous acceptez que la HA soit principalement un système que vous construisez autour.
CockroachDB propose la HA comme une fonctionnalité de première classe — si vous acceptez que les systèmes distribués n’éliminent pas la douleur, ils la déplacent.

La vraie question : quel type de panne voulez-vous prendre en charge ?

L’erreur la plus fréquente dans le choix d’une base pour la HA est de penser que la question est « Quelle base est la plus disponible ? »
La disponibilité n’est pas un attribut de marque. C’est une propriété de bout en bout qui inclut vos pilotes clients, DNS, équilibreurs de charge,
habitudes de migration de schéma, discipline de sauvegarde, observabilité et le profil des personnes que vous embauchez.

La vraie question est : quelle panne êtes-vous capable de diagnostiquer sous stress ?

  • Avec PostgreSQL, l’histoire HA implique typiquement la réplication en streaming, un gestionnaire de bascule, et une séparation claire
    entre « le nœud unique en écriture » et « les réplicas qui peuvent être en retard ». Votre douleur porte généralement sur l’orchestration de la bascule,
    le routage lecture/écriture, le décalage de réplication et les bords opérationnels tranchants.
  • Avec CockroachDB, l’histoire HA est intégrée : réplication par consensus, rééquilibrage automatique et SQL qui semble familier.
    Votre douleur se déplace vers le débogage distribué : contention, fractionnement de ranges, hotspots, amplification de latence inter-régions
    et l’apprentissage du modèle d’état interne du cluster.

Choisissez la douleur que vous pouvez payer. Pas la douleur qui est jolie sur un schéma.

Positionnement rapide : quand chaque base est le bon marteau

Choisissez PostgreSQL HA quand vous pouvez centraliser les écritures et contrôler la complexité

PostgreSQL est un cheval de bataille. Vous obtenez une sémantique SQL mûre, un écosystème massif, des caractéristiques de performance prévisibles
et un modèle de panne que la plupart des équipes SRE peuvent raisonner. En pratique, la HA PostgreSQL c’est généralement :
un primaire, un ou plusieurs réplicas, plus un orchestrateur (Patroni, repmgr, Pacemaker/Corosync),
et une couche de routage (HAProxy, PgBouncer, équilibreur cloud ou logique côté application).

La HA PostgreSQL est le bon choix lorsque :

  • Votre charge d’écriture est lourde et sensible à la latence, et vous pouvez garder le primaire dans une seule région/zone.
  • Vous voulez la profondeur complète des fonctionnalités Postgres (extensions, indexation avancée, outils riches) sans la prudence du « presque compatible ».
  • Vous pouvez tolérer que la HA inter-régions soit généralement de la reprise après sinistre (DR), pas des « écritures actives-actives ».
  • Vous avez une équipe qui peut traiter la bascule comme un sous-système engineering avec des exercices réguliers.

Choisissez CockroachDB quand vous avez besoin que les écritures survivent à la perte de nœud sans pile HA sur-mesure

CockroachDB est du SQL distribué : un magasin clé-valeur répliqué par consensus avec du SQL par-dessus. L’argument commercial est séduisant :
« Il continue de fonctionner quand des machines meurent. » Souvent vrai. Mais vous payez avec de nouveaux modes de défaillance et plus de pièces mobiles par requête.
Votre posture opérationnelle passe de « protéger le primaire » à « garder le cluster équilibré et refroidir les hotspots ».

CockroachDB est le bon choix lorsque :

  • Vous avez besoin d’une bascule automatique des écritures sans promouvoir un réplica.
  • Vous avez une empreinte multi-zone et vous voulez que la base gère automatiquement le placement des réplicas et le quorum.
  • Votre organisation est prête à apprendre le profilage de performance distribué et accepter une latence de base plus élevée pour la sécurité.
  • Vous pouvez concevoir délibérément la localité des données (partitionnement/géo), pas par souhaitation.

Blague sèche #1 : Construire la HA Postgres, c’est comme assembler un meuble IKEA — stable et utile, sauf si vous « freestylez » une vis manquante.

Histoire et faits intéressants à utiliser

Le contexte compte parce que le design des bases de données est surtout le registre fossile d’anciennes pannes.
Voici des faits qui vous aident réellement à prendre des décisions :

  1. PostgreSQL descend de POSTGRES (UC Berkeley, milieu des années 1980), conçu à une époque où la correction comptait plus que les mots à la mode cloud.
    Cet ADN se voit dans son comportement conservateur et prévisible.
  2. Le write-ahead logging (WAL) est plus ancien que votre système CI. Postgres s’appuie sur le WAL pour garantir la durabilité et permettre la réplication.
    Si vous ne respectez pas le WAL et les checkpoints, vous finirez par avoir un incident au ralenti.
  3. La réplication en streaming dans Postgres est arrivée avec la 9.0 (2010), ce qui a rendu la réplication physique « réelle » grand public et rapide.
    Avant ça, la HA était plus mécanique et manuelle.
  4. Le hot standby Postgres (réplicas lisibles) a été introduit dans la 9.0. Cela a permis un schéma standard : scaler les lectures, protéger le primaire et planifier les bascules.
  5. CockroachDB s’est inspiré de Google Spanner (décrit publiquement en 2012). La thèse de Spanner : du SQL à l’échelle globale avec consistance forte,
    au prix de la coordination.
  6. CockroachDB utilise le consensus Raft pour la réplication. Raft est conçu pour être compréhensible (par rapport à Paxos), mais « compréhensible »
    implique toujours des termes comme quorums, baux et transferts de leadership.
  7. « Distributed SQL » est devenu une catégorie parce que le NoSQL ne pouvait pas faire semblant éternellement. Beaucoup d’équipes voulaient retrouver les transactions SQL
    après avoir appris que la consistance éventuelle est un mode de vie, pas un interrupteur.
  8. CAP n’est pas un outil de comparaison produit. Dans les systèmes réels, la tolérance aux partitions n’est pas optionnelle, et la « consistance » a plusieurs significations.
    La question utile est : quel compromis le système prend durant les partitions, et à quel point cela est-il visible pour votre appli ?
  9. Les extensions Postgres sont une superpuissance et un piège. Elles vous permettent de construire rapidement des systèmes spécialisés (PostGIS, pgcrypto, outils de type Timescale),
    mais elles vous ancrent aux sémantiques Postgres d’une façon que les systèmes « compatibles » peuvent ne pas reproduire.

Comment la HA fonctionne réellement : réplication Postgres vs consensus Cockroach

PostgreSQL HA : un seul écrivain avec des réplicas, plus de la colle d’orchestration

L’architecture de base de PostgreSQL est à écrivain unique. Vous pouvez scaler les lectures avec des réplicas et vous pouvez basculer en promouvant un réplica.
Mais « HA Postgres » n’est pas une seule fonctionnalité ; c’est une chorégraphie :

  • Réplication en streaming copie le WAL du primaire vers les réplicas.
  • Réplication synchrone peut garantir que les commits attendent l’accusé de réception d’un réplica (RPO plus bas, latence plus élevée).
  • Gestion de la bascule décide quel nœud devient primaire et recâble les clients.
  • Prévention du split-brain est votre responsabilité. Vous avez besoin de fencing, de quorum ou d’un verrou distribué fiable.

L’avantage Postgres est la clarté : il y a un primaire. Si les écritures échouent, vous cherchez le primaire ou en promouvez un.
Le risque Postgres est que votre pile HA est « batteries non fournies ». Cette pile peut être excellente, mais elle doit être ingénierée.

CockroachDB HA : réplication par consensus partout, et SQL en passager

CockroachDB distribue les données en ranges (shards). Chaque range est répliqué (souvent 3 réplicas par défaut),
et un réplica est le leader Raft qui gère les écritures pour ce range. Les transactions se coordonnent entre ranges.
La bascule est interne : si un nœud meurt, un autre réplica devient leader pour les ranges concernés, si le quorum subsiste.

L’avantage Cockroach est opérationnel : vous ne « promouvez » pas manuellement un nouveau primaire. Le système s’auto-répare dans les contraintes de quorum.
Le risque Cockroach est la prévisibilité des performances : une simple transaction peut impliquer plusieurs ranges et plusieurs groupes de consensus,
surtout à mesure que votre jeu de données et votre schéma évoluent.

RPO/RTO en termes concrets

Si vous achetez de la HA, vous achetez des RPO et RTO, pas des impressions.

  • Postgres réplication asynchrone : le RPO peut être non nul (vous pouvez perdre les dernières secondes de commits lors d’une bascule),
    le RTO dépend de l’orchestration et du routage client.
  • Postgres réplication synchrone : le RPO peut approcher zéro si correctement configuré, mais la latence de commit augmente et vous pouvez bloquer
    si les standbys synchrones sont indisponibles.
  • CockroachDB : le RPO est typiquement proche de zéro à l’intérieur du quorum ; le RTO est souvent rapide pour les pannes de nœud, mais les partitions et
    les clusters surchargés peuvent mener à un comportement « disponible mais triste » : timeouts, retries et débit dégradé.

Une citation pour rester honnête, paraphrasant Werner Vogels : « Tout échoue, tout le temps. » Traitez ceci comme une idée paraphrasée.

Latence, latence extrême, et pourquoi « multi-région » est un mot piège

Les conversations sur la HA deviennent romantiques à propos de la géographie. « Active-active entre régions. » « Écritures globales. »
Puis la physique arrive, sans invitation, comme l’auditeur que vous avez oublié de mettre en copie.

Avec Postgres, le chemin d’écriture est local au primaire. Si vous placez le primaire dans une région et exécutez des serveurs applicatifs dans une autre,
votre latence p99 se moquera de votre feuille de route. Ce n’est pas la faute de Postgres ; c’est la vôtre.
La HA en Postgres signifie généralement « garder le primaire proche des rédigents ; garder les réplicas proches des lecteurs ; basculer si nécessaire. »

Avec CockroachDB, le multi-région fait partie de la conception, mais ce n’est pas magique. Une écriture fortement cohérente nécessite un quorum.
Si vos réplicas sont répartis entre des régions lointaines, le temps aller-retour du quorum devient votre latence d’écriture de base.
Le système peut placer leaders et réplicas pour optimiser la localité, mais vous devez toujours choisir vos contraintes :
latence vs survivabilité vs modèle de consistance.

Blague sèche #2 : La consistance forte multi-région est la seule façon de transformer la « vitesse de la lumière » en facture mensuelle récurrente.

La latence extrême est l’endroit où les bases meurent

La latence médiane est jolie pour les tableaux de bord. La latence extrême est ce que vivent les utilisateurs.
Les systèmes distribués tendent à élargir la queue parce que plus de composants participent à chaque opération.
CockroachDB peut être excellent, mais si votre transaction touche plusieurs ranges sur plusieurs nœuds,
vous avez plus d’opportunités de files d’attente, de contention et de réplicas lents.

Postgres a ses propres problèmes de queue — autovacuum qui bloque, files d’attente de verrous, pics de checkpoints — mais la portée est souvent plus facile à borner :
les contraintes de ressources d’un seul primaire et un seul flux WAL.

Modes de défaillance : ce qui vous réveille

Modes de défaillance PostgreSQL (et ce que ça ressemble)

  • Split brain : deux nœuds croient être primaires. Symptômes : timelines divergentes, écritures conflictuelles, basculement « impossible de se connecter ».
    Causes racines : mauvais fencing, gestionnaire de bascule mal configuré, partitions réseau, scripts trop futés.
  • Décalage de réplication : les réplicas prennent du retard. Symptômes : lectures obsolètes, surprises lecture-après-écriture, une bascule perdrait des données.
    Causes racines : I/O lente, throttling réseau, transactions longues, rafales WAL lors de jobs batch, réplicas sous-dimensionnés.
  • Pression sur les checkpoints / WAL : pics de latence. Symptômes : blocages périodiques d’écriture, saturation I/O, augmentation des temps fsync.
    Causes racines : shared_buffers trop petit avec fort renouvellement, réglages agressifs de checkpoint, stockage lent, trop de buffers sales.
  • Dette d’autovacuum : tout ralentit puis chute. Symptômes : bloat, temps de requête en hausse, avertissements wraparound.
    Causes racines : seuils de vacuum non adaptés à fort churn, transactions longues, workers autovacuum insuffisants.
  • Bascule avec état incompatible : le réplica promu manque d’extensions, de configs ou a des paramètres différents.
    Symptômes : erreurs applicatives après bascule, plans de requête inattendus, rôles manquants.
    Cause racine : serveurs « snowflake » et dérive de configuration.

Modes de défaillance CockroachDB (et ce que ça ressemble)

  • Ranges chauds / hotspots : un espace de clés devient un aimant à trafic. Symptômes : latence élevée pour un sous-ensemble d’opérations, pics CPU sur quelques nœuds.
    Causes racines : clés monotones en augmentation, contention sur une seule ligne, mauvais schéma pour la distribution.
  • Retries de transaction : l’appli voit des erreurs de sérialisation ou des échecs retryables. Symptômes : latence accrue, timeouts par rafales, montée des retries.
    Causes racines : forte contention, transactions longues, écritures conflictuelles, backoff ou logique de retry client insuffisants.
  • Sous-réplication / perte de quorum : un range ne peut pas atteindre le quorum. Symptômes : indisponibilité pour certaines données, écritures bloquées.
    Causes racines : trop de nœuds en panne, cluster mal dimensionné, maintenance faite comme un derby de démolition.
  • Latence de quorum inter-régions : écritures lentes globalement. Symptômes : p95/p99 qui bondissent corrélés avec le RTT entre régions.
    Cause racine : politiques de placement des réplicas qui forcent des quorums à travers des régions éloignées.
  • Interférence du rééquilibrage en arrière-plan : le cluster « guérit » pendant que vous essayez de servir le trafic.
    Symptômes : disque et réseau soutenus, latence plus haute, variance accrue.
    Causes racines : décommission de nœuds sous charge, changements topologiques soudains, marge insuffisante.

Notez le thème : les pannes Postgres concernent souvent l’orchestration et les plafonds de ressources d’un seul nœud.
Les pannes Cockroach concernent souvent la coordination et les effets secondaires de la distribution.

Tâches pratiques : commandes, sorties et décisions (12+)

Ci‑dessous des tâches opérationnelles réelles avec des commandes à lancer, ce que la sortie signifie et la décision à prendre.
Elles sont séparées entre Postgres et CockroachDB, avec quelques vérifications au niveau OS. Pas de flafla ; c’est ce que fait l’astreinte.

PostgreSQL : identifier le primaire et la santé de la réplication

Tâche 1 : Ce nœud est‑il primaire ou standby ?

cr0x@server:~$ psql -XAtc "select pg_is_in_recovery();"
f

Signification : f signifie que ce nœud n’est pas en recovery : il agit comme primaire. t signifierait standby.
Décision : Si vous attendiez un standby mais obtenez f, arrêtez‑vous et vérifiez l’état de la bascule ; vous pourriez être en zone de split‑brain.

Tâche 2 : Vérifier le décalage de réplication en octets sur le primaire

cr0x@server:~$ psql -Xc "select application_name, client_addr, state, sent_lsn, write_lsn, flush_lsn, replay_lsn, pg_wal_lsn_diff(sent_lsn,replay_lsn) as byte_lag from pg_stat_replication;"
 application_name | client_addr |  state  | sent_lsn  | write_lsn | flush_lsn | replay_lsn |  byte_lag
------------------+-------------+---------+-----------+-----------+-----------+------------+-----------
 replica1         | 10.0.1.21   | streaming | 3/9A4F2C8 | 3/9A4F2C8 | 3/9A4F2C8 | 3/9A4F2C8  |         0
 replica2         | 10.0.2.37   | streaming | 3/9A4F2C8 | 3/9A4F2C8 | 3/9A4F2C8 | 3/9A4E100  |     54024

Signification : byte_lag indique le retard de chaque réplica par rapport à ce que le primaire a envoyé.
Un petit retard est normal ; une croissance persistante signifie que le réplica ne suit pas.
Décision : Si le retard augmente, évitez de basculer vers le réplica en retard ; investiguez I/O, CPU et réseau du réplica.

Tâche 3 : Sur un standby, mesurer le délai de replay en temps

cr0x@server:~$ psql -Xc "select now() - pg_last_xact_replay_timestamp() as replay_delay;"
 replay_delay
--------------
 00:00:02.184

Signification : Si ceci monte à plusieurs minutes en charge normale, votre « scaling des lectures » produit des lectures obsolètes.
Décision : Soit cessez d’envoyer des lectures sensibles à la latence ici, soit corrigez le goulot (souvent le disque).

Tâche 4 : Vérifier les transactions longues bloquant vacuum et contrôle du bloat

cr0x@server:~$ psql -Xc "select pid, usename, state, now()-xact_start as xact_age, left(query,80) as query from pg_stat_activity where xact_start is not null order by xact_age desc limit 5;"
 pid  | usename | state  |  xact_age  |                                      query
------+--------+--------+------------+--------------------------------------------------------------------------------
 8421 | app    | active | 00:42:11   | update orders set status='paid' where id=$1 returning id
 9173 | app    | idle in transaction | 01:13:02 | select * from customers where id=$1

Signification : « idle in transaction » pendant une heure est un signal d’alarme ; cela peut empêcher le nettoyage et causer du bloat.
Décision : Corrigez la gestion des connexions côté appli ; envisagez idle_in_transaction_session_timeout et tuez les fautifs pendant l’incident.

Tâche 5 : Trouver rapidement la contention de verrous

cr0x@server:~$ psql -Xc "select a.pid, now()-a.query_start as age, a.state, left(a.query,70) as query, l.mode, l.granted from pg_locks l join pg_stat_activity a on a.pid=l.pid where a.datname=current_database() order by l.granted, age desc limit 12;"
 pid  |   age    | state  |                               query                               |        mode         | granted
------+----------+--------+-------------------------------------------------------------------+---------------------+---------
 5211 | 00:01:12 | active | alter table invoices add column tax_region text                   | AccessExclusiveLock | f
 4182 | 00:01:08 | active | select * from invoices where account_id=$1 order by created_at    | AccessShareLock     | t

Signification : Un AccessExclusiveLock en attente bloque pratiquement tout sur cette table.
Décision : Annulez le DDL s’il est dangereux ; reprogrammez avec des options CONCURRENTLY ou des patterns de migration en ligne.

Tâche 6 : Vérifier la pression des checkpoints et le comportement des buffers

cr0x@server:~$ psql -Xc "select checkpoints_timed, checkpoints_req, buffers_checkpoint, buffers_backend, maxwritten_clean, checkpoint_write_time, checkpoint_sync_time from pg_stat_bgwriter;"
 checkpoints_timed | checkpoints_req | buffers_checkpoint | buffers_backend | maxwritten_clean | checkpoint_write_time | checkpoint_sync_time
-------------------+-----------------+--------------------+-----------------+------------------+-----------------------+----------------------
               102 |              47 |            9231142 |          312991 |            25013 |               1882210 |                402113

Signification : Un checkpoints_req élevé par rapport aux checkpoints temporels suggère une pression WAL forçant des checkpoints supplémentaires.
Décision : Ajustez les réglages de checkpoint et évaluez le débit du stockage ; envisagez d’étaler les rafales d’écriture et d’augmenter max_wal_size.

Tâche 7 : Valider que l’archivage WAL fonctionne vraiment (pour PITR)

cr0x@server:~$ psql -Xc "select archived_count, failed_count, last_archived_wal, last_archived_time, last_failed_wal, last_failed_time from pg_stat_archiver;"
 archived_count | failed_count | last_archived_wal |     last_archived_time     | last_failed_wal | last_failed_time
---------------+--------------+-------------------+----------------------------+-----------------+-----------------
         91822 |            0 | 00000001000000030000009A | 2025-12-30 02:18:12.104+00 |                 |

Signification : failed_count=0 est ce que vous voulez. Un archivage échoué signifie que votre histoire de récupération est de la fiction.
Décision : Si des échecs existent, arrêtez de prétendre avoir du PITR ; corrigez les permissions, le stockage ou la commande d’archivage avant le prochain incident.

PostgreSQL : posture de bascule et routage client

Tâche 8 : Vérifier la configuration de réplication synchrone

cr0x@server:~$ psql -Xc "show synchronous_commit; show synchronous_standby_names;"
 synchronous_commit
--------------------
 on

 synchronous_standby_names
--------------------------
 FIRST 1 (replica1,replica2)

Signification : Les commits attendent un standby. Si les deux standbys sont malsains, les écritures peuvent se bloquer.
Décision : Si vous êtes en panne et devez permettre les écritures, vous pouvez temporairement assouplir les paramètres sync — mais documentez‑le et revenez en arrière.

Tâche 9 : Confirmer que votre appli pointe vers le bon endpoint (exemple PgBouncer)

cr0x@server:~$ psql -h 127.0.0.1 -p 6432 -U pgbouncer -d pgbouncer -Xc "show clients;"
 type | user | database | state  | addr       | port  | local_addr | local_port | connect_time
------+------|----------|--------|------------|-------|------------|------------|----------------------------
 C    | app  | prod     | active | 10.4.7.19  | 49212 | 10.4.2.10  | 6432       | 2025-12-30 02:18:55.911+00

Signification : Vous voyez les clients actifs et leurs adresses sources. Utile lors d’une bascule : les clients atteignent‑ils le proxy ?
Décision : Si les clients ne se connectent pas, la BD peut être saine et la couche de routage est cassée (ou bloquée par firewall/DNS).

CockroachDB : santé du cluster et sanity de distribution

Tâche 10 : Vérifier l’état des nœuds et la liveness

cr0x@server:~$ cockroach node status --host localhost:26257
  id |    address     |     build     |  started_at           | is_live | replicas |  cpu | mem |  ssd | version
-----+----------------+---------------+-----------------------+---------+----------+------+-----+------+---------
   1 | 10.0.1.10:26257| v24.1.3       | 2025-12-30 00:11:02   | true    |     2210 | 0.42 | 64G |  30% | 24.1
   2 | 10.0.2.10:26257| v24.1.3       | 2025-12-30 00:11:08   | true    |     2198 | 0.55 | 64G |  29% | 24.1
   3 | 10.0.3.10:26257| v24.1.3       | 2025-12-30 00:10:59   | false   |     2175 | 0.00 | 64G |  31% | 24.1

Signification : Le nœud 3 n’est pas live. Avec une configuration à 3 réplicas, perdre un nœud est généralement survivable, mais vous êtes maintenant à une panne d’une situation dangereuse.
Décision : Suspendez toute maintenance ; restaurez la santé du nœud ou ajoutez de la capacité avant de faire quoi que ce soit de « malin ».

Tâche 11 : Vérifier rapidement la santé de la réplication

cr0x@server:~$ cockroach node status --ranges --host localhost:26257
  id | ranges | underreplicated | unavailable | leader_ranges
-----+--------+-----------------+-------------+--------------
   1 |   5600 |              12 |           0 |         1850
   2 |   5578 |              15 |           0 |         1902
   3 |   5489 |             301 |           2 |            0

Signification : Les ranges sous-répliqués et indisponibles indiquent des problèmes de placement des données et une potentielle indisponibilité.
Décision : Si unavailable est non nul, traitez‑le comme un incident impactant l’utilisateur. Restaurez le quorum (ramenez le nœud, ou reconfigurez).

Tâche 12 : Identifier les statements coûteux et les retries (introspection SQL)

cr0x@server:~$ cockroach sql --host localhost:26257 -e "select app_name, query, count(*) as execs, sum(retries) as retries from crdb_internal.statement_statistics where aggregated_ts > now() - interval '10 minutes' group by app_name, query order by retries desc limit 5;"
  app_name |                    query                    | execs | retries
----------+---------------------------------------------+-------+---------
  api     | update accounts set balance = balance + $1  |  1200 |     340

Signification : Des retries élevés pour un update suggèrent de la contention ou des clés chaudes.
Décision : Réduisez la contention : redesign du pattern d’écritures (shard counters, éviter les hotspots sur une seule ligne), raccourcir les transactions, ajouter du backoff pour les retries.

Tâche 13 : Vérifier la distribution des ranges et repérer les hotspots suspects

cr0x@server:~$ cockroach sql --host localhost:26257 -e "select range_id, replicas, lease_holder, queries_per_second from crdb_internal.ranges_no_leases order by queries_per_second desc limit 5;"
  range_id | replicas | lease_holder | queries_per_second
----------+----------+--------------+-------------------
    90211 | {1,2}    |            1 |              892.2
    90197 | {1,2,3}  |            2 |              521.7

Signification : Un range avec un QPS très élevé peut devenir un goulot ; notez aussi les ranges avec seulement deux réplicas.
Décision : Si des hotspots persistent, reconsidérez le schéma et la distribution des clés ; si le nombre de réplicas est bas, corrigez la perte de nœud et la re‑réplication.

Vérifications OS et stockage qui impactent les deux systèmes

Tâche 14 : Vérifier la latence disque et la saturation (iostat)

cr0x@server:~$ iostat -xz 1 3
Linux 6.5.0 (db01)  12/30/2025  _x86_64_  (16 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          12.33    0.00    5.41    9.88    0.00   72.38

Device            r/s     w/s   rkB/s   wkB/s  await  svctm  %util
nvme0n1         120.0   980.0  6400.0 51200.0  18.40   0.78  92.10

Signification : %util proche de 100% plus un await élevé signifient que le disque est un goulot.
Décision : Arrêtez de blâmer « la base ». Réduisez la pression d’écriture, ajoutez des IOPS, migrez vers un stockage plus rapide, ou ajoutez des nœuds (Cockroach) / ajustez les checkpoints (Postgres).

Tâche 15 : Confirmer l’espace filesystem et l’état des inodes

cr0x@server:~$ df -h /var/lib/postgresql /var/lib/cockroach
Filesystem      Size  Used Avail Use% Mounted on
/dev/nvme0n1p2  900G  812G   43G  95% /var/lib/postgresql
/dev/nvme1n1p1  900G  701G  153G  83% /var/lib/cockroach

Signification : 95% d’utilisation flirte avec la catastrophe ; Postgres déteste en particulier manquer d’espace disque en plein WAL.
Décision : Si supérieur à ~85–90% de manière soutenue, traitez‑le comme urgent : étendez le volume, purgez ou déplacez les données avant que ça devienne une indisponibilité.

Mode opératoire pour diagnostic rapide

Quand la base « est lente », vous n’avez pas le temps pour la philosophie. Vous avez besoin d’un triage rapide et reproductible qui restreint le goulot.
Voici une séquence pragmatique premier/deuxième/troisième pour les deux systèmes.

Premier : est‑ce la base, ou le chemin vers elle ?

  • Vérifiez les erreurs/timeouts de connexion client dans les logs applicatifs. Si c’est DNS/LB/firewall, la BD peut être innocente.
  • Vérifiez que l’endpoint résout vers les nœuds attendus. Le trafic mal routé est classique après une bascule.
  • Vérifiez la santé basique OS : steal CPU, disque plein, latence disque.

Second : le système est‑il bloqué (verrous/contention) ou saturé (I/O/CPU) ?

  • Postgres : regardez les verrous, transactions longues et I/O wait ; vérifiez le décalage de réplication si les lectures sont obsolètes.
  • CockroachDB : regardez les retries, hotspots (ranges) et sous-réplication ; vérifiez la liveness des nœuds et la disponibilité des ranges.

Troisième : est‑ce un événement topologique/HA ?

  • Postgres : confirmez exactement un primaire, confirmez que les réplicas sont connectés, confirmez l’état du gestionnaire de bascule.
  • CockroachDB : confirmez la santé du quorum, la sous-réplication, et si le rééquilibrage se bat contre votre charge.

La discipline est d’éviter de chasser les plans de requête avant d’avoir confirmé que le cluster n’est pas simplement affamé de disque ou bloqué par un verrou.
La plupart des incidents de « performance base de données » sont en réalité des « incidents de coordination et stockage portant un masque SQL ».

Trois mini-récits d’entreprise depuis le terrain

Mini-récit 1 : L’incident causé par une fausse supposition (bascule Postgres)

Une entreprise SaaS de taille moyenne exploitait Postgres avec un primaire dans une zone et un réplica en streaming dans une autre.
Ils avaient un gestionnaire de bascule et un VIP. Tout le monde se sentait en sécurité. Ils avaient même écrit « RPO proche de zéro » dans un document interne, ce qui est une belle invitation au destin.

Un après‑midi, le stockage du primaire a commencé à timeout. Le gestionnaire de bascule a promu le réplica. Le VIP a migré.
L’appli s’est rétablie rapidement — jusqu’à ce que le support client remarque des commandes « manquantes ». Peu nombreuses, mais suffisantes.
L’équipe supposait que la réplication synchrone était activée parce que « nous l’avons configurée il y a des mois ».

Il s’est avéré que les paramètres sync avaient été appliqués sur l’ancien primaire mais jamais déployés de façon cohérente.
Pire, l’appli utilisait des réplicas pour certains « écrans de confirmation », qui montraient maintenant une timeline différente du nouveau primaire.
La bascule elle‑même était correcte. L’hypothèse sur le RPO ne l’était pas.

La récupération a nécessité un triage douloureux : comparer les IDs de commande et les timestamps, réconcilier depuis les logs en amont, et expliquer au business pourquoi « hautement disponible »
ne signifiait pas « aucune perte de données ». La vraie correction était ennuyeuse : gestion de configuration, parité forcée des paramètres,
et une politique claire des requêtes autorisées sur les réplicas.

Après l’incident, ils ont changé leur playbook : la bascule n’est pas « finie » tant qu’ils n’ont pas vérifié le mode de réplication, le décalage des réplicas et le routage applicatif.
Ils ont aussi commencé à écrire des objectifs RPO/RTO explicites par fonctionnalité, pas par base de données.

Mini-récit 2 : L’optimisation qui a mal tourné (hotspot CockroachDB)

Une équipe e‑commerce a migré un service de panier à fort écriture vers CockroachDB pour la bascule automatique sur trois zones.
Les premiers retours étaient bons. La latence semblait stable. Puis la saison haute est arrivée et le parcours de checkout a commencé à produire retries et timeouts.

L’équipe avait « optimisé » les clés primaires pour être séquentielles pour la localité d’index, raisonnement hérité d’années d’expérience Postgres.
Dans CockroachDB, cela a créé un hotspot d’écriture : les inserts ont frappé un petit ensemble de ranges parce que les clés augmentaient de façon monotone.
Les splits de ranges ont aidé, mais la leadership des ranges les plus chauds s’est concentrée sur quelques nœuds et ceux‑ci sont devenus liés au CPU.

L’astreinte a vu que les nœuds étaient live et la réplication saine. Pourtant la latence montait et l’appli réessayait agressivement,
transformant la contention en un déni de service auto‑infligé. Le système était disponible, techniquement. Les utilisateurs ne pouvaient toujours pas finaliser leurs achats.

La correction était contre‑intuitive pour un esprit Postgres : changer la distribution des clés (utiliser des préfixes aléatoires ou hashés),
et refondre les « compteurs sur une seule ligne » en compteurs sharded. Ils ont aussi implémenté un backoff de retry sensé et plafonné la concurrence sur les chemins chauds.

Ils ont gardé CockroachDB, mais ont cessé d’essayer de le faire ressembler à une base mono‑nœud. Voilà la vraie migration.

Mini-récit 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise (PITR Postgres)

Une plateforme B2B exploitait Postgres avec réplication en streaming et un régime PITR strict : backups périodiques complets,
archivage WAL validé chaque jour, et exercices de restauration trimestriels. Personne n’aimait faire les drills. C’était le but.

Un ingénieur a lancé une migration qui a supprimé une colonne dans le mauvais schéma. L’appli n’a pas planté immédiatement ; elle a progressivement échoué à mesure que certains jobs s’exécutaient.
Le réplica a fidèlement répliqué l’erreur, parce que les réplicas sont obéissants comme ça.

L’équipe a fait face au dilemme habituel : essayer de patcher à chaud sous pression, ou revenir à un point dans le temps connu bon.
Parce qu’ils avaient des archives WAL fiables et des étapes répétées, ils ont restauré à un timestamp quelques minutes avant la migration et rejoué les changements sûrs.

La panne a duré des dizaines de minutes, pas des heures. Le postmortem a été misérablement court.
Les héros n’étaient pas ceux qui écrivaient des scripts malins ; c’étaient ceux qui testaient les restaurations quand rien n’était en feu.

La HA a réduit les interruptions liées aux pannes de nœud. Le PITR les a sauvés d’eux‑mêmes. Outils différents. Les deux obligatoires.

Erreurs courantes : symptômes → cause racine → correction

1) Symptom : après la bascule, les écritures réussissent mais les lectures montrent des données anciennes

Cause racine : l’application lit encore depuis un réplica en retard, ou votre routage lecture/écriture ne s’est pas mis à jour proprement.

Correction : imposez le routage via un endpoint unique avec connaissance des rôles ; ajoutez des règles lecture‑après‑écriture ; surveillez le délai de replay des réplicas.

2) Symptom : le primaire Postgres devient « lent toutes les quelques minutes »

Cause racine : pics de checkpoint ou pression de flush WAL dus aux limites du stockage et à une configuration de checkpoint agressive.

Correction : augmentez max_wal_size, ajustez les paramètres de checkpoint, et déplacez WAL/données vers un stockage plus rapide ; vérifiez les latences fsync.

3) Symptom : les réplicas Postgres prennent du retard pendant les jobs batch

Cause racine : rafales WAL + goulot I/O du réplica, souvent aggravé par des transactions longues retardant le nettoyage.

Correction : limitez les écritures batch, augmentez les ressources des réplicas, et éliminez les transactions longues ; envisagez le partitionnement logique ou programmer les jobs hors‑pic.

4) Symptom : CockroachDB affiche des « nœuds sains » mais l’appli timeoute

Cause racine : contention de transaction et retries, fréquemment dues à des clés chaudes ou des transactions trop bavardes.

Correction : redesign des hotspots (clés sharded, randomisation), raccourcir les transactions, implémenter des retries bornés avec backoff, et réduire les patterns de contention.

5) Symptom : CockroachDB devient instable pendant la maintenance des nœuds

Cause racine : marge insuffisante ; rééquilibrage et re‑réplication concurrents avec la charge production.

Correction : ajoutez de la capacité, drain/decommissionnez lentement, et évitez de sortir plusieurs nœuds ; planifiez des fenêtres de maintenance avec des limites conscientes du trafic.

6) Symptom : la HA Postgres « fonctionne », mais vous obtenez parfois deux primaires

Cause racine : split brain dû à une élection leader non fiable, absence de fencing ou comportement de partition réseau non modélisé.

Correction : implémentez un quorum/stockage de verrous (etcd/consul), fencing fiable (STONITH quand approprié), et testez les scénarios de partition.

7) Symptom : « On ne peut pas restaurer vite » malgré des backups

Cause racine : les backups n’ont jamais été restaurés en pratique ; archives WAL manquantes ; credentials/permissions périmés.

Correction : planifiez des drills de restauration, vérifiez l’archivage quotidiennement, et automatisez la validation ; traitez les restaurations comme une fonctionnalité de production.

Checklists / plan étape par étape

Checklist de décision : Postgres HA vs CockroachDB

  1. Définir RPO/RTO par service. Si vous ne pouvez pas l’écrire, vous devinez.
  2. Mesurer vos besoins de localité d’écriture. Où les écritures ont‑elles lieu ? Si « partout », prouvez‑le avec des traces.
  3. Classer la charge : OLTP à forte contention, append‑heavy, mixte lecture/écriture, transactions longues, analytics en arrière‑plan.
  4. Lister les fonctionnalités indispensables : extensions, comportement d’isolation spécifique, objets volumineux, index spécialisés, décodage logique, etc.
  5. Réalité staffing : avez‑vous des opérateurs capables de déboguer le consensus et la contention, ou une équipe maîtrisant l’interne Postgres ?
  6. Drills de panne : pouvez‑vous répéter la perte d’une région et prouver la récupération en staging avec une charge proche de la production ?

Étapes : construire une HA « ennuyeuse » sur PostgreSQL

  1. Choisir une topologie : primaire + 2 réplicas à travers les zones ; décider lesquels sont endpoints lecture seule.
  2. Implémenter la réplication : streaming replication ; décider async vs sync selon le budget latence.
  3. Ajouter l’orchestration : un vrai gestionnaire de bascule ; évitez les scripts DIY sauf si vous aimez l’archéologie.
  4. Ajouter le routage : endpoints stables pour les applis ; séparation explicite lecture/écriture si vous utilisez des réplicas.
  5. Prévenir le split brain : élection leader basée sur quorum ; stratégie de fencing ; tester les partitions.
  6. Backups et PITR : backups complets + archivage WAL ; valider quotidiennement ; répéter des restaurations.
  7. Observabilité : décalage de réplication, attentes de verrou, débit WAL, timing des checkpoints, latence disque.
  8. Runbooks : promote, demote, rewind, rebuild replica ; documenter « ce qu’il ne faut pas faire » en incident.

Étapes : exploiter CockroachDB sans s’automutiler

  1. Commencez avec 3+ nœuds sur 3 zones (ou plus pour la marge). Ne pas exécuter un cluster « juste suffisant » en production.
  2. Définissez la localité et les contraintes avant d’en avoir besoin. Décidez où les leases/leaders doivent vivre pour les tables critiques.
  3. Modélisez la contention tôt : identifiez compteurs, updates sur une seule ligne, clés séquentielles et patterns « dernier enregistrement ».
  4. Implémentez correctement les retries dans l’application avec backoff borné. Les retries non bornés sont une fonction de déni de service distribué.
  5. Surveillez la santé des ranges : sous-réplication, ranges indisponibles, concentration des leaseholders.
  6. Planifiez la maintenance autour du rééquilibrage : décommissionnez lentement, gardez de la marge, évitez les drains multi‑nœuds sous charge.
  7. Benchmarkez avec une concurrence réaliste, pas un script synthétique poli qui ne se conteste jamais.

FAQ

1) Postgres peut‑il être « active‑active » pour les écritures ?

Pas dans le modèle noyau à primaire unique. Vous pouvez approcher cela avec du sharding, du routage applicatif, ou des solutions multi‑maître spécialisées,
mais vous vous engagez à gérer les conflits et la complexité opérationnelle. Si vous avez vraiment besoin d’écritures multi‑auteurs avec consistance forte,
un système basé sur le consensus peut mieux convenir — si vous pouvez gérer les compromis.

2) CockroachDB garantit‑il un temps d’arrêt nul ?

Il peut survivre à de nombreuses pannes de nœuds sans promotion manuelle, mais le « zéro temps d’arrêt » dépend du quorum, de la marge de capacité,
et si votre charge déclenche contention/retries. C’est résilient, pas invincible.

3) Quelle est la configuration HA Postgres la plus simple qui ne vous déteste pas plus tard ?

Primaire + deux réplicas répartis sur les zones, un gestionnaire de bascule éprouvé (pas des scripts cron), un endpoint stable unique pour les rédacteurs,
et un PITR testé. Restez ennuyeux, automatisez la parité de configuration et répétez les bascules.

4) La réplication synchrone dans Postgres est‑elle toujours meilleure ?

Elle réduit le RPO, mais augmente la latence et peut bloquer les écritures si les standbys synchrones sont indisponibles.
Utilisez‑la lorsque vous pouvez supporter la latence et que la connectivité des standbys est fiable. Sinon, préférez l’asynchrone plus un PITR solide.

5) Pourquoi les applications CockroachDB ont‑elles besoin d’une logique de retry ?

Parce que les transactions sérialisables en contention peuvent être forcées à rejouer pour préserver la correction.
Si vous ne gérez pas correctement les retries, vous transformerez la contention passagère en erreurs visibles pour l’utilisateur.

6) Lequel est plus facile à déboguer à 2 h du matin ?

Postgres est généralement plus facile si votre incident est « un nœud est lent » ou « replica en retard », car les pièces mobiles sont moins nombreuses.
CockroachDB peut être plus simple pour des pannes de nœud évidentes, mais plus difficile pour les pathologies de performance et la contention.

7) Puis‑je « lift-and-shift » un schéma Postgres vers CockroachDB ?

Vous pouvez migrer beaucoup de SQL, mais n’assumez pas un comportement identique autour des verrous, des cas limites d’isolation, des patterns de sequences/serial,
et de la disponibilité des extensions. Le plus grand risque n’est pas la syntaxe — c’est le caractère de la charge comme la contention et la distribution des clés.

8) Qu’en est‑il des sauvegardes — CockroachDB élimine‑t‑il la nécessité de penser PITR ?

Non. La HA gère les pannes de nœud ; les sauvegardes gèrent les erreurs humaines et la corruption logique. Vous avez toujours besoin de procédures de restauration testées.
Base différente, même réalité.

9) Dois‑je utiliser des réplicas de lecture avec Postgres si je tiens à la correction ?

Oui, mais soyez explicite. Routez uniquement le trafic de lecture sûr vers les réplicas, ou implémentez des règles lecture‑après‑écriture.
Si vous traitez les réplicas comme totalement cohérents, vous finirez par livrer un bug en production.

10) CockroachDB est‑il toujours plus lent parce qu’il est distribué ?

Pas toujours, mais la coordination distribuée ajoute un coût de base, et la latence extrême peut s’élargir sous contention.
Pour certaines charges c’est excellent ; pour de l’OLTP à faible latence en une seule région avec fortes écritures, Postgres gagne souvent en vitesse brute.

Conclusion : prochaines étapes réalisables cette semaine

La HA PostgreSQL est un système que vous construisez ; la HA CockroachDB est un système que vous rejoignez. Les deux peuvent exécuter des charges de production de façon fiable.
Les deux peuvent ruiner votre week‑end si vous les traitez comme magiques.

Voici ce qu’il faut faire ensuite, dans l’ordre :

  1. Écrivez les RPO/RTO par service et obtenez l’accord du métier. Vous voulez moins de surprises que vos auditeurs.
  2. Faites un drill de panne : tuez un nœud, partitionnez un chemin réseau, et mesurez le temps de récupération bout à bout (incluant les clients).
  3. Implémentez le mode opératoire de diagnostic rapide comme runbook et répétez‑le. La vitesse vient de la répétition, pas du génie.
  4. Choisissez une douleur à éliminer en premier : prévention du split‑brain Postgres, ou mitigation de contention/hotspot Cockroach.
  5. Testez les restaurations. Pas « nous avons des backups », mais « nous avons restauré la sauvegarde de mardi dernier dans un environnement propre et vérifié la correction. »

Si vous voulez une HA sans drame, choisissez l’architecture qui correspond aux habitudes de votre organisation.
Si vous voulez une HA avec de nouveaux types de douleurs, choisissez celle qui correspond à la curiosité de votre organisation.
Dans tous les cas, restez ennuyeux là où ça compte : sauvegardes, drills et responsabilité claire.

← Précédent
Choix de l’ordonnanceur IO ZFS : mq-deadline vs none pour HDD, SSD et NVMe
Suivant →
AMD Chiplets : l’astuce qui a ressuscité Ryzen

Laisser un commentaire