Aller au contenu
medium

Réplication PostgreSQL : streaming, hot standby et failover

36 min de lecture

logo postgres

La réplication streaming de PostgreSQL copie en continu les journaux de transactions (WAL) du primary vers un ou plusieurs standby. Le standby peut servir de hot standby (lectures en temps réel) et prendre le relais en cas de panne via un failover (promote). Ce guide monte un cluster à deux nœuds de bout en bout, de la configuration du primary jusqu'au promote du standby, avec toutes les commandes testées sur PostgreSQL 18.

  • Configurer un primary pour la réplication streaming (wal_level, rôle dédié, slot, pg_hba)
  • Initialiser un standby avec pg_basebackup -R et comprendre standby.signal
  • Surveiller la réplication : pg_stat_replication, pg_stat_wal_receiver, pg_wal_lsn_diff()
  • Utiliser le hot standby pour les lectures et comprendre ses limites
  • Exécuter un failover manuel avec pg_promote() et vérifier le changement de timeline
  • Distinguer réplication physique et logique pour choisir la bonne approche

La réplication streaming répond à des besoins concrets d'administration :

  • Vous administrez un PostgreSQL en production et vous voulez éliminer le point de défaillance unique (SPOF)
  • Vous avez besoin d'un standby en lecture pour décharger le primary des requêtes de reporting
  • Vous préparez une maintenance planifiée (montée de version, migration) avec basculement contrôlé
  • Vous devez garantir une reprise rapide (RTO court) en cas de crash du serveur principal
  • Vous préparez la certification LFCS ou RHCSA et devez maîtriser la réplication PostgreSQL

Trois sujets voisins reviennent systématiquement dès qu'on parle de réplication, et les mélanger conduit à des architectures bancales. Voici où ils sont traités :

  • La sauvegarde et le PITR (Point-In-Time Recovery) → voir le guide sauvegarde-restauration
  • L'orchestration automatique du failover (Patroni, repmgr, pg_auto_failover) → mentionnée en fin de guide
  • La réplication logique détaillée → aperçu en fin de guide, guide dédié à venir

pg_basebackup apparaît ici comme outil d'initialisation du standby, pas comme outil de sauvegarde.

  • Deux serveurs avec PostgreSQL installé (même version majeure)
  • Un réseau permettant la connexion TCP entre les deux nœuds (port 5432)
  • Les bases du guide d'installation et de configuration
  • Accès superuser (postgres) ou un rôle avec REPLICATION

Lab utilisé dans ce guide :

RôleHostnameIPOSPostgreSQL
Primarypg-primary192.168.122.61Debian 1218.3
Standbypg-standby192.168.122.62Debian 1218.3

C'est la confusion la plus coûteuse en exploitation, et elle vient d'un raccourci compréhensible : « mes données sont sur deux machines, donc elles sont protégées ». La réplication protège contre la perte d'une machine, pas contre la perte de données. Les deux risques appellent deux dispositifs différents, et l'un ne remplace jamais l'autre.

PostgreSQL propose deux types de réplication. Ce guide se concentre sur la réplication physique (streaming), la plus courante pour la haute disponibilité.

La réplication physique (streaming) copie les journaux WAL octet par octet. Le standby est une copie exacte du primary : même structure, mêmes bases, mêmes données, même état.

Caractéristiques :

  • Copie tout le cluster (toutes les bases, tous les rôles)
  • Le standby est en lecture seule (hot standby)
  • Très faible latence (quasi temps réel)
  • Le standby peut servir de base pour un failover

Logique : réplication sélective par publication/souscription

Section intitulée « Logique : réplication sélective par publication/souscription »

La réplication logique transmet des changements au niveau des lignes (INSERT, UPDATE, DELETE) pour des tables choisies. Elle utilise le mécanisme PUBLICATION / SUBSCRIPTION introduit en PostgreSQL 10.

Caractéristiques :

  • Réplique des tables spécifiques, pas le cluster entier
  • Le souscripteur est un serveur indépendant (lecture-écriture)
  • Permet la réplication entre versions différentes de PostgreSQL
  • Utile pour la migration, l'agrégation de données ou le partage sélectif

La ligne Objectif principal suffit à trancher dans la grande majorité des cas : si vous cherchez à survivre à la perte d'un serveur, c'est la réplication physique ; si vous cherchez à déplacer ou partager des données, c'est la logique. La ligne Versions différentes est celle qui décide dans un cas particulier mais fréquent, la montée de version majeure : la réplication physique impose la même version majeure des deux côtés, donc elle ne peut pas servir à migrer d'un PostgreSQL 16 vers un 18.

CritèrePhysique (streaming)Logique
Objectif principalHaute disponibilité, failoverMigration, réplication sélective
GranularitéCluster entierTables choisies
Standby en lectureOui (hot standby)Le souscripteur est en lecture-écriture
FailoverOui (pg_promote)Non conçu pour
Versions différentesNon (même version majeure)Oui
LatenceTrès faibleFaible à modérée
ComplexitéSimplePlus complexe (conflits possibles)

La configuration du primary se fait en quatre étapes : paramètres de réplication, rôle dédié, slot de réplication et autorisation réseau.

Les paramètres essentiels se trouvent dans postgresql.conf. Sur Debian, on utilise ALTER SYSTEM pour les modifier proprement :

-- Niveau WAL nécessaire à la réplication.
-- C'est déjà la valeur par défaut : cette ligne sert à s'en assurer,
-- notamment si le serveur a été passé en 'minimal' pour du chargement massif.
ALTER SYSTEM SET wal_level = 'replica';
-- Nombre maximum de connexions de réplication simultanées
ALTER SYSTEM SET max_wal_senders = 5;
-- Nombre maximum de slots de réplication
ALTER SYSTEM SET max_replication_slots = 10;
-- Quantité de WAL conservée pour les standby en retard
ALTER SYSTEM SET wal_keep_size = '256MB';
-- Écouter sur toutes les interfaces (pas uniquement localhost)
ALTER SYSTEM SET listen_addresses = '*';

Après redémarrage, vérifiez que les paramètres sont bien appliqués :

SELECT name, setting
FROM pg_settings
WHERE name IN (
'wal_level', 'max_wal_senders', 'max_replication_slots',
'wal_keep_size', 'listen_addresses'
);
name | setting
------------------------+---------
listen_addresses | *
max_replication_slots | 10
max_wal_senders | 5
wal_keep_size | 256
wal_level | replica

Le standby doit s'authentifier auprès du primary pour ouvrir son flux de WAL. Utiliser le rôle postgres (superuser) pour cette connexion est une mauvaise pratique : le mot de passe se retrouve stocké côté standby, et il ouvrirait un accès total au primary si le standby était compromis. Créez un rôle dédié avec uniquement les privilèges LOGIN et REPLICATION :

CREATE ROLE replicator WITH LOGIN REPLICATION PASSWORD 'motdepasse_fort';

Ce rôle ne peut ni créer de bases, ni lire vos données, ni modifier quoi que ce soit. Il sert exclusivement à la connexion de réplication.

Un slot de réplication garantit que le primary conserve les WAL nécessaires au standby, même si celui-ci est temporairement déconnecté. Sans slot, un standby déconnecté trop longtemps peut perdre la synchronisation si les WAL ont été recyclés.

SELECT pg_create_physical_replication_slot('standby_slot');

Vérification :

SELECT slot_name, slot_type, active FROM pg_replication_slots;
slot_name | slot_type | active
--------------+-----------+--------
standby_slot | physical | f

Le slot est inactif (active = f) tant qu'aucun standby ne l'utilise.

Supprimer un slot orphelin à la main suppose de s'en apercevoir à temps. PostgreSQL fournit deux garde-fous qui rendent cette surveillance beaucoup moins critique.

Le fichier pg_hba.conf contrôle les connexions autorisées. Ajoutez une ligne pour autoriser le rôle replicator depuis l'IP du standby :

# TYPE DATABASE USER ADDRESS METHOD
host replication replicator 192.168.122.62/32 scram-sha-256

Rechargez la configuration :

Fenêtre de terminal
sudo systemctl reload postgresql

Vérification, les règles actives sont visibles dans pg_hba_file_rules :

SELECT line_number, type, database, user_name, address, netmask, auth_method
FROM pg_hba_file_rules
WHERE 'replication' = ANY(database);
line_number | type | database | user_name | address | netmask | auth_method
-------------+------+---------------+--------------+----------------+---------------+---------------
117 | host | {replication} | {replicator} | 192.168.122.62 | 255.255.255.255 | scram-sha-256

Le standby est initialisé en copiant l'intégralité du cluster du primary avec pg_basebackup. Cette commande s'exécute sur le standby.

Avant de lancer pg_basebackup, le répertoire de données du standby doit être vide. Si PostgreSQL a été installé et initialisé automatiquement, videz-le :

Fenêtre de terminal
# Arrêter PostgreSQL sur le standby
sudo systemctl stop postgresql
# Vider le répertoire de données
sudo -u postgres find /var/lib/postgresql/18/main -mindepth 1 -delete

L'option -R est cruciale : elle génère automatiquement standby.signal et écrit le primary_conninfo dans postgresql.auto.conf.

Fenêtre de terminal
sudo -u postgres pg_basebackup \
-h 192.168.122.61 \
-U replicator \
-D /var/lib/postgresql/18/main \
-Fp -Xs -P -R
OptionRôle
-hAdresse du primary
-URôle de réplication
-DRépertoire de données cible
-FpFormat plain (fichiers)
-XsStreaming des WAL pendant la copie
-PAfficher la progression
-RGénérer standby.signal + primary_conninfo

Un dernier point avant de lancer la commande : pg_basebackup s'exécute avec les droits du compte système local et fait confiance à ce que le primary lui envoie. Cette confiance a déjà été exploitée.

Deux éléments sont créés automatiquement par l'option -R :

1. Le fichier standby.signal, un fichier vide qui indique à PostgreSQL de démarrer en mode standby :

Fenêtre de terminal
ls -la /var/lib/postgresql/18/main/standby.signal
-rw------- 1 postgres postgres 0 Apr 13 11:44 standby.signal

2. Le paramètre primary_conninfo dans postgresql.auto.conf, la chaîne de connexion vers le primary :

Fenêtre de terminal
grep primary_conninfo /var/lib/postgresql/18/main/postgresql.auto.conf
primary_conninfo = 'user=replicator password=motdepasse_fort channel_binding=prefer
host=192.168.122.61 port=5432 sslmode=prefer sslnegotiation=postgres
sslcompression=0 sslcertmode=allow sslsni=1 ssl_min_protocol_version=TLSv1.2
gssencmode=prefer krbsrvname=postgres gssdelegation=0
target_session_attrs=any load_balance_hosts=disable'

pg_basebackup inscrit tous les paramètres libpq avec leur valeur effective, y compris ceux que vous n'avez pas fournis. La ligne est donc longue, mais seuls user, host, port et password viennent de votre commande ; le reste reprend les valeurs par défaut du client.

Il reste un paramètre à ajouter, que pg_basebackup -R ne génère pas : le nom du slot. Sans lui, le standby se connectera bien au primary, mais sans réserver de WAL, et le slot créé plus haut restera inutilisé. Renseignez primary_slot_name avant le premier démarrage :

Fenêtre de terminal
echo "primary_slot_name = 'standby_slot'" | \
sudo -u postgres tee -a /var/lib/postgresql/18/main/postgresql.auto.conf

À ce stade, le répertoire de données contient une copie complète du primary, le fichier standby.signal et la chaîne de connexion. PostgreSQL détecte standby.signal au démarrage et entre directement en mode recovery au lieu d'ouvrir la base en lecture-écriture. Le démarrage peut prendre quelques secondes de plus qu'un démarrage normal, le temps de rejouer les WAL accumulés pendant la copie.

Fenêtre de terminal
sudo systemctl start postgresql

Vérifiez immédiatement que le standby est bien en mode recovery :

SELECT pg_is_in_recovery();
pg_is_in_recovery
-------------------
t

t (true) confirme que PostgreSQL fonctionne en mode standby.

Une fois le standby démarré, il faut vérifier que la réplication streaming est active et fonctionnelle. Les deux vues clés sont pg_stat_replication (côté primary) et pg_stat_wal_receiver (côté standby).

Cette vue montre les connexions de réplication actives :

SELECT pid, state, sync_state,
sent_lsn, write_lsn, flush_lsn, replay_lsn
FROM pg_stat_replication;
pid | state | sync_state | sent_lsn | write_lsn | flush_lsn | replay_lsn
------+-----------+------------+-----------+-----------+-----------+------------
4536 | streaming | async | 0/30067F0 | 0/30067F0 | 0/30067F0 | 0/30067F0
ColonneSignification
statestreaming = réplication active en temps réel
sync_stateasync = réplication asynchrone (par défaut)
sent_lsnDernière position WAL envoyée au standby
write_lsnDernière position écrite en mémoire par le standby
flush_lsnDernière position écrite sur disque par le standby
replay_lsnDernière position rejouée par le standby

Quand les quatre colonnes LSN sont identiques, le standby est parfaitement synchronisé.

Cette vue montre la connexion de réplication active côté standby :

SELECT status, sender_host, sender_port,
slot_name, flushed_lsn
FROM pg_stat_wal_receiver;
status | sender_host | sender_port | slot_name | flushed_lsn
-----------+----------------+-------------+--------------+-------------
streaming | 192.168.122.61 | 5432 | standby_slot | 0/30067F0

Le status = streaming confirme que le standby reçoit les WAL en continu. Contrôlez aussi la colonne slot_name : si elle est vide, le standby n'utilise pas le slot que vous avez créé sur le primary, parce que primary_slot_name n'a pas été renseigné. La réplication fonctionne quand même, mais vous perdez la garantie de rétention des WAL et le slot restera active = f sur le primary.

Pour mesurer l'écart entre le primary et le standby en octets :

-- Sur le primary
SELECT
client_addr,
pg_wal_lsn_diff(sent_lsn, replay_lsn) AS replay_lag_bytes
FROM pg_stat_replication;
client_addr | replay_lag_bytes
-----------------+------------------
192.168.122.62 | 0

Un lag de 0 octets signifie que le standby a rejoué tout ce que le primary lui a envoyé. En production sous charge, un léger lag (quelques Ko) est normal.

Le test le plus parlant : écrire sur le primary, lire sur le standby.

Sur le primary, insérez une ligne :

INSERT INTO app.servers (hostname, ip_address, os, role, env, cpu_cores, ram_gb)
VALUES ('srv-repl-test', '10.0.0.42', 'Debian 12', 'test', 'dev', 2, 4);

Sur le standby, vérifiez immédiatement :

SELECT hostname, ip_address FROM app.servers
WHERE hostname = 'srv-repl-test';
hostname | ip_address
---------------+------------
srv-repl-test | 10.0.0.42

La ligne est visible quasi instantanément. Le standby reçoit et rejoue les WAL en continu.

Le hot standby permet d'exécuter des requêtes en lecture sur un standby pendant qu'il continue de recevoir et rejouer les WAL. Le paramètre hot_standby contrôle cette fonctionnalité, il est à on par défaut depuis PostgreSQL 10 et n'a d'effet que sur un serveur en recovery/standby. Aucune configuration n'est nécessaire sur le primary.

Le hot standby accepte les opérations en lecture seule :

-- Requêtes SELECT
SELECT * FROM app.servers WHERE env = 'prod';
-- Vues statistiques
SELECT * FROM pg_stat_user_tables;
-- Plans d'exécution
EXPLAIN ANALYZE SELECT * FROM app.servers WHERE hostname LIKE 'srv%';

C'est idéal pour :

  • Le reporting et les requêtes analytiques longues
  • Les lectures distribuées (connecter les applications en lecture au standby)
  • Le debug et l'analyse de performances sans impacter le primary

Toute opération en écriture est refusée :

-- INSERT → erreur
INSERT INTO app.servers (hostname) VALUES ('test');
ERROR: cannot execute INSERT in a read-only transaction
-- DDL → erreur
CREATE TABLE app.test (id serial);
ERROR: cannot execute CREATE TABLE in a read-only transaction

C'est un comportement normal et attendu : le standby est une copie en lecture seule du primary.

Parfois, le rejeu des WAL sur le standby entre en conflit avec des requêtes de lecture longues. Par exemple, si le primary fait un VACUUM qui supprime des lignes mortes, le standby doit rejouer cette opération, mais une requête SELECT est peut-être en train de lire ces lignes.

Le paramètre max_standby_streaming_delay contrôle combien de temps le standby attend avant d'annuler la requête en conflit :

SHOW max_standby_streaming_delay;
max_standby_streaming_delay
-----------------------------
30s

Par défaut, le standby attend 30 secondes avant d'annuler la requête conflictuelle. Augmentez cette valeur si vos requêtes de reporting sont longues :

ALTER SYSTEM SET max_standby_streaming_delay = '120s';
SELECT pg_reload_conf();

Le failover consiste à promouvoir le standby en nouveau primary quand l'ancien primary est indisponible (panne, maintenance, migration).

Avant de promouvoir, assurez-vous que le standby est bien synchronisé :

-- Sur le standby : vérifier qu'on est bien en recovery
SELECT pg_is_in_recovery();
-- → t
-- Vérifier la timeline actuelle
SELECT timeline_id FROM pg_control_checkpoint();
-- → 1
-- Vérifier que standby.signal existe
-- ls -la /var/lib/postgresql/18/main/standby.signal

La promotion se fait en une seule commande SQL, exécutée sur le standby :

SELECT pg_promote();
pg_promote
------------
t

Après le promote, plusieurs changements sont immédiatement observables sur l'ex-standby :

1. Plus en recovery :

SELECT pg_is_in_recovery();
pg_is_in_recovery
-------------------
f

f (false), le serveur n'est plus un standby, il accepte les écritures.

2. Nouvelle timeline :

SELECT timeline_id FROM pg_control_checkpoint();
timeline_id
-------------
2

La timeline passe de 1 à 2. Chaque promote crée une nouvelle timeline pour éviter toute confusion avec les WAL de l'ancienne.

3. standby.signal supprimé :

Fenêtre de terminal
ls /var/lib/postgresql/18/main/standby.signal
ls: cannot access '/var/lib/postgresql/18/main/standby.signal': No such file or directory

PostgreSQL a automatiquement supprimé le fichier.

4. pg_stat_wal_receiver vide :

SELECT * FROM pg_stat_wal_receiver;
(0 rows)

Le standby ne reçoit plus de WAL, il est maintenant un primary autonome.

5. Les écritures fonctionnent :

INSERT INTO app.servers (hostname, ip_address, os, role, env, cpu_cores, ram_gb)
VALUES ('srv-after-promote', '10.0.0.99', 'Debian 12', 'test', 'dev', 2, 4);
INSERT 0 1

L'INSERT passe, le DDL aussi. Le serveur est pleinement opérationnel en lecture-écriture.

Après le promote, l'ancien primary est toujours sur la timeline 1 et ne sait pas que le standby a été promu. Il y a trois options :

OptionQuand l'utiliser
pg_basebackup completL'ancien primary a beaucoup divergé ou est corrompu
pg_rewindL'ancien primary a peu divergé (recommandé)
Reconstruire entièrementChangement de version ou de configuration

pg_rewind est l'option la plus efficace quand l'ancien primary n'a accepté que peu d'écritures après la déconnexion du standby :

Fenêtre de terminal
# Arrêter l'ancien primary
sudo systemctl stop postgresql
# Resynchroniser avec le nouveau primary
sudo -u postgres pg_rewind \
--target-pgdata=/var/lib/postgresql/18/main \
--source-server="host=192.168.122.62 user=postgres dbname=postgres"

Ensuite, créez un standby.signal et configurez primary_conninfo pour que l'ancien primary devienne le nouveau standby et pointe vers le nouveau primary.

Jusqu'ici, tout ce qui a été monté fonctionne en mode asynchrone, le comportement par défaut de PostgreSQL. Cette section décrit le mode qui garantit qu'aucune transaction validée n'est perdue, au prix d'un couplage fort entre le primary et son standby. C'est un choix de RPO, pas d'architecture : le montage reste exactement le même, seuls deux paramètres changent.

La réplication synchrone garantit qu'un COMMIT n'est validé côté client que lorsque le standby a confirmé avoir écrit (ou rejoué) les WAL correspondants. C'est le mode le plus sûr pour le RPO (zéro perte de données).

Le point qui trompe le plus souvent : synchronous_commit vaut déjà on par défaut, et pourtant la réplication reste asynchrone. La raison est que synchronous_commit = on garantit seulement l'écriture locale du WAL sur disque avant de rendre la main au client. Le mode synchrone à proprement parler s'active en désignant au moins un standby dans synchronous_standby_names, qui est vide par défaut. C'est ce paramètre, et lui seul, qui fait passer sync_state de async à sync.

-- Sur le primary
-- synchronous_commit = on est déjà la valeur par défaut
-- C'est synchronous_standby_names qui active le mode synchrone
ALTER SYSTEM SET synchronous_standby_names = 'FIRST 1 (standby1)';
SELECT pg_reload_conf();
ParamètreRôle
synchronous_commit = onDéjà la valeur par défaut, le COMMIT attend la confirmation du standby
synchronous_standby_namesL'activateur réel : quels standby sont synchrones (par application_name)

Après configuration, pg_stat_replication affiche sync au lieu de async :

SELECT application_name, sync_state FROM pg_stat_replication;
application_name | sync_state
------------------+------------
walreceiver | sync

Lisez ce tableau ligne par ligne, en couple : chaque avantage a un prix direct en face. La deuxième ligne est la plus lourde de conséquences, et elle surprend souvent en production : la disponibilité du primary devient dépendante de celle du standby. Vous avez déplacé le point de défaillance unique, vous ne l'avez pas supprimé. C'est pourquoi la réplication synchrone se déploie avec deux standby au minimum.

AvantageInconvénient
RPO = 0 (zéro perte de données)Latence accrue sur chaque COMMIT
Garantie de cohérenceSi le standby tombe, le primary bloque les COMMIT
Confiance pour les données critiquesNécessite un réseau fiable et rapide

La réplication logique est un sujet à part entière. Voici un aperçu rapide du mécanisme pour comprendre quand l'envisager.

Le primary crée une publication sur des tables choisies. Un autre serveur PostgreSQL crée une souscription pour recevoir les changements :

-- Sur le primary (publisher)
CREATE PUBLICATION ma_publication FOR TABLE app.servers, app.deployments;
-- Sur le souscripteur (un serveur indépendant)
CREATE SUBSCRIPTION ma_souscription
CONNECTION 'host=192.168.122.61 dbname=labdb user=replicator password=motdepasse_fort'
PUBLICATION ma_publication;

Les trois premiers cas ont un point commun : la source et la destination sont deux serveurs autonomes qui poursuivent des objectifs différents. Le quatrième point est une limite à retenir avant de partir sur cette voie par erreur, car un souscripteur ressemble à un standby vu de loin : il reçoit des données en continu et reste à jour. Mais il n'a ni timeline commune avec la source, ni notion de promotion, et rien ne garantit qu'il contient l'intégralité du cluster.

  • Migration de version : répliquer d'un PostgreSQL 16 vers un 18
  • Réplication sélective : ne transmettre que certaines tables
  • Agrégation : collecter des données de plusieurs sources
  • Pas pour le failover : le souscripteur n'est pas un standby (pas de timeline, pas de promote)

Le failover manuel décrit plus haut suppose qu'un humain constate la panne, vérifie l'état du standby et lance la promotion. En pratique, cela représente plusieurs minutes d'indisponibilité, et un risque bien pire : le split-brain, quand l'ancien primary redevient joignable et accepte des écritures alors que le standby a déjà été promu. Automatiser le failover, c'est avant tout se doter d'un mécanisme de consensus capable de désigner un seul primary légitime.

Suivez ces quatre vérifications dans l'ordre, du réseau vers l'applicatif : chacune élimine une couche. L'étape 3 est la plus discriminante, parce qu'elle reproduit exactement ce que fait le standby : une connexion en mode réplication, qui emprunte une ligne différente de pg_hba.conf qu'une connexion psql ordinaire. Un psql classique qui passe alors que la réplication échoue signale presque toujours une ligne replication manquante ou mal placée.

  1. Vérifier la connectivité réseau :

    Fenêtre de terminal
    # Depuis le standby
    pg_isready -h 192.168.122.61 -p 5432
  2. Vérifier pg_hba.conf sur le primary, le rôle de réplication doit être autorisé depuis l'IP du standby :

    SELECT user_name, address, auth_method
    FROM pg_hba_file_rules
    WHERE 'replication' = ANY(database);
  3. Vérifier le mot de passe, testez la connexion manuellement :

    Fenêtre de terminal
    psql "host=192.168.122.61 user=replicator dbname=postgres replication=database" -c "IDENTIFY_SYSTEM;"
  4. Vérifier les logs du standby :

    Fenêtre de terminal
    sudo tail -20 /var/log/postgresql/postgresql-18-main.log

Un lag qui croît sans jamais redescendre ne se traite pas comme un pic ponctuel : cela signifie que le standby rejoue les WAL moins vite que le primary ne les produit, et l'écart ne se comblera pas tout seul. Avant de parcourir ce tableau, isolez la couche fautive en comparant les colonnes de pg_stat_replication : si sent_lsn est loin devant flush_lsn, le problème est réseau ou disque côté standby ; si flush_lsn est proche de sent_lsn mais que replay_lsn traîne, c'est le rejeu qui bloque, souvent à cause d'une requête de lecture en conflit.

Cause possibleSolution
Charge d'écriture très élevée sur le primaryAugmenter wal_keep_size, vérifier les IOPS disque du standby
Requêtes longues sur le standby (conflits)Augmenter max_standby_streaming_delay ou déporter les requêtes
Réseau saturéVérifier la bande passante entre primary et standby
Slot de réplication non utiliséVérifier pg_replication_slots, supprimer les slots orphelins

pg_promote() renvoie f plutôt qu'une erreur quand la promotion n'aboutit pas dans le délai imparti, ce qui donne un diagnostic pauvre. Les trois contrôles ci-dessous couvrent les causes réelles, et le dernier est le plus rentable : le journal du serveur explique toujours pourquoi la bascule a été refusée.

  • Vérifiez que pg_is_in_recovery() retourne t avant le promote
  • Vérifiez que l'utilisateur a les droits (superuser ou rôle avec pg_promote)
  • Consultez les logs : sudo tail -30 /var/log/postgresql/postgresql-18-main.log

Le symptôme se manifeste sur le primary, pas sur le standby : pg_wal grossit sans limite alors que l'activité n'a pas changé. La requête ci-dessous quantifie le problème avant d'agir, en traduisant l'écart de LSN retenu par chaque slot inactif en volume lisible. Ne supprimez un slot qu'après avoir confirmé que le standby correspondant ne reviendra pas : la suppression est définitive, et un standby qui se reconnecterait ensuite devrait être reconstruit.

-- Identifier les slots inactifs
SELECT slot_name, active, pg_size_pretty(
pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)
) AS retained_wal
FROM pg_replication_slots
WHERE NOT active;
-- Supprimer un slot orphelin
SELECT pg_drop_replication_slot('standby_slot');
  1. La réplication streaming copie les WAL en continu du primary vers le standby, en production, combinez-la avec l'archivage WAL (restore_command) pour couvrir les déconnexions prolongées
  2. La réplication n'est pas une sauvegarde : un DROP TABLE est immédiatement répliqué
  3. pg_basebackup -R initialise le standby et génère automatiquement standby.signal + primary_conninfo
  4. Depuis PostgreSQL 12, recovery.conf n'existe plus, tout passe par les paramètres normaux
  5. Les slots de réplication empêchent le recyclage prématuré des WAL, configurez max_slot_wal_keep_size et idle_replication_slot_timeout pour éviter un disque plein
  6. Le hot standby accepte les lectures mais refuse toutes les écritures (INSERT, UPDATE, DELETE, DDL)
  7. pg_promote() transforme le standby en primary autonome sur une nouvelle timeline, c'est irréversible
  8. La réplication synchrone s'active via synchronous_standby_names, elle garantit zéro perte de données mais bloque si le standby tombe
  9. En production, utilisez un outil d'orchestration automatique (Patroni, repmgr) pour le failover

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn