Dans ce précédent article, nous vous présentions l’évolution des fonctionnalités de la réplication logique apportées par les différentes versions de PostgreSQL et dans cet autre article, nous avions vu la mise en place d’une réplication logique.
Réplication logique et haute disponibilité
Dans le cadre de la haute disponibilité en réplication physique avec un primaire et plusieurs secondaires, la bascule lors d’un incident va bloquer la réplication logique.
Pour rappel, la mise en place de la réplication logique implique de créer un slot de réplication sur le primaire, ce slot permettant de conserver les fichiers WAL requis par les souscriptions (un slot de réplication par souscription).
Lors d’une bascule, le slot de réplication logique créé initialement sur le primaire ne sera pas présent sur le secondaire promu. La souscription abonnée à cette publication se retrouvera alors bloquée car ne recevant plus les changements émis par la publication, le temps de recréer un slot de réplication logique, avec le risque de perdre des données pendant la bascule.
Nativement, PostgreSQL n’offre pas encore de solution intégrée pour la gestion des slots de réplication pendant une bascule. Mais la prochaine version majeure de PostgreSQL, la version 17, va commencer à intégrer des changements pour y pallier.
En attendant, vous pouvez utiliser l’extension pg_failover_slots
. Sinon, si vous utilisez Patroni
, il est possible de configurer ce dernier pour gérer les slots de réplication. C’est l’objet de la suite de l’article.
Patroni, c’est quoi déjà ?
Patroni est un framework de gestion de cluster PostgreSQL pour assurer la haute disponibilité de service, en assurant une bascule automatique et offrant une gestion de la configuration centralisée.
La mise en place d’un cluster Patroni pourra faire l’objet d’un prochain article, mais pour l’instant nous allons considérer que vous possédez déjà un cluster Patroni opérationnel avec une instance primaire, et deux instances secondaires.
Configuration
Première étape à la mise en place d’une réplication logique avec Patroni, il faut passer le paramètre wal_level
à logical
afin d’ajouter dans les fichiers WAL toutes les informations nécessaires au support du décodage logique.
Nous utiliserons la commande patronictl
qui permet de gérer le cluster Patroni et vérifier son état.
Vérifier le statut et la topologie actuelle d’un cluster avec l’option topology
:
postgres@pgdeb01:~$ patronictl -c /etc/patroni/patroni.yml topology
+ Cluster: loxodemo (7382147555638198668) ------+----+-----------+
| Member | Host | Role | State | TL | Lag in MB |
+-----------+-------------+---------+-----------+----+-----------+
| pgdeb01 | 10.200.0.11 | Leader | running | 1 | |
| + pgdeb02 | 10.200.0.12 | Replica | streaming | 1 | 0 |
| + pgdeb03 | 10.200.0.13 | Replica | streaming | 1 | 0 |
+-----------+-------------+---------+-----------+----+-----------+
Consulter la configuration du cluster avec l’option show-config
:
postgres@pgdeb01:~$ patronictl -c /etc/patroni/patroni.yml show-config
loop_wait: 10
maximum_lag_on_failover: 1048576
postgresql:
parameters:
archive_command: pgbackrest --stanza=loxodemo archive-push %p
archive_mode: 'on'
recovery_conf:
restore_command: pgbackrest --stanza=loxodemo archive-get %f %p
use_pg_rewind: false
use_slots: false
retry_timeout: 10
ttl: 30
Nous modifions le paramètre PostgreSQL wal_level
à logical
avec l’option edit-config
:
postgres@pgdeb01:~$ patronictl -c /etc/patroni/config.yml edit-config
---
+++
@@ -4,6 +4,7 @@
parameters:
archive_command: pgbackrest --stanza=loxodemo archive-push %p
archive_mode: 'on'
+ wal_level: 'logical'
recovery_conf:
restore_command: pgbackrest --stanza=loxodemo archive-get %f %p
use_pg_rewind: false
Apply these changes? [y/N]: y
Configuration changed
Le changement de ce paramètre nécessite un redémarrage des instances PostgreSQL. Le redémarrage est indiqué par Patroni dans la sortie de l’option list
, dans la colonne Pending restart
:
postgres@pgdeb01:~$ patronictl -c /etc/patroni/config.yml list
+ Cluster: loxodemo (7382147555638198668) ----+----+-----------+-----------------+-----------------------------+
| Member | Host | Role | State | TL | Lag in MB | Pending restart | Pending restart reason |
+---------+-------------+---------+-----------+----+-----------+-----------------+-----------------------------+
| pgdeb01 | 10.200.0.11 | Leader | running | 1 | | * | wal_level: replica->logical |
| pgdeb02 | 10.200.0.12 | Replica | streaming | 1 | 0 | * | wal_level: replica->logical |
| pgdeb03 | 10.200.0.13 | Replica | streaming | 1 | 0 | * | wal_level: replica->logical |
+---------+-------------+---------+-----------+----+-----------+-----------------+-----------------------------+
Il faut alors procéder au redémarrage en utilisant l’option restart
:
postgres@pgdeb01:~$ patronictl -c /etc/patroni/config.yml restart --force loxodemo
+ Cluster: loxodemo (7382147555638198668) ----+----+-----------+-----------------+-----------------------------+
| Member | Host | Role | State | TL | Lag in MB | Pending restart | Pending restart reason |
+---------+-------------+---------+-----------+----+-----------+-----------------+-----------------------------+
| pgdeb01 | 10.200.0.11 | Leader | running | 1 | | * | wal_level: replica->logical |
| pgdeb02 | 10.200.0.12 | Replica | streaming | 1 | 0 | * | wal_level: replica->logical |
| pgdeb03 | 10.200.0.13 | Replica | streaming | 1 | 0 | * | wal_level: replica->logical |
+---------+-------------+---------+-----------+----+-----------+-----------------+-----------------------------+
Success: restart on member pgdeb01
Success: restart on member pgdeb02
Success: restart on member pgdeb03
Il peut être intéressant aussi de revoir la configuration des paramètres suivants, qui demandent aussi un redémarrage de PostgreSQL :
max_replication_slots: 10
max_wal_senders: 10
max_worker_processes: 10
max_replication_slots
(défaut à 10) indiquant le nombre de slots de réplication maximum (réplication logique et physique). Doit être au moins égal au nombre de réplica et de souscription (pour chaque souscription, unapply worker
et plusieurstablesync worker
);max_wal_senders
(défaut à 10) doit être équivalent ou plus au nombre de slots de réplication, plus le nombre de réplica présents;max_worker_processes
(défaut à 8) doit être supérieur aux valeurs précédentes, et supérieures sur les réplicas;
Du côté des souscriptions, il peut aussi être utile de modifier les paramètres suivants propre aux souscriptions :
max_logical_replication_workers: 4
max_sync_workers_per_subscription: 2
Il convient à présent de déclarer les slots de réplication dans la configuration de Patroni, afin de les rendre permanents et qu’ils soient préservés lors d’une bascule.
La déclaration dans la configuration va créer les slots de réplication logique sur tous les nœuds secondaires, et Patroni se chargera d’avancer leur position via l’appel de la fonction pg_replication_slot_advance
. Le paramètre hot_standby_feedback
est activé aussi par Patroni sur les secondaires.
postgres@pgdeb01:~$ patronictl -c /etc/patroni/config.yml edit-config
---
+++
@@ -8,6 +8,11 @@
recovery_conf:
restore_command: pgbackrest --stanza=loxodemo archive-get %f %p
use_pg_rewind: false
- use_slots: false
+ use_slots: true
retry_timeout: 10
ttl: 30
+slots:
+ logical_slot_emp:
+ database: employees
+ plugin: pgoutput
+ type: logical
Apply these changes? [y/N]: y
Configuration changed
Un exemple de configuration :
loop_wait: 10
maximum_lag_on_failover: 1048576
postgresql:
parameters:
archive_command: pgbackrest --stanza=loxodemo archive-push %p
archive_mode: 'on'
wal_level: logical
recovery_conf:
restore_command: pgbackrest --stanza=loxodemo archive-get %f %p
use_pg_rewind: false
use_slots: true
retry_timeout: 10
slots:
logical_slot_emp:
database: employees
plugin: pgoutput
type: logical
ttl: 30
use_slots
permet d’activer les slots de réplication;- section
slots
permet de définir les slots de réplication permanents; logical_slot_emp
correspond au nom du slot de réplication;database
est le nom de la base de données depuis laquelle la publication est créée;plugin
est le plugin de décodage utilisé (le pluginpgoutput
est le plugin natif de PostgreSQL);type
est le type de réplication, icilogical
(sinonphysical
);
Il est aussi possible de créer le slot de réplication logique auparavant, et de spécifier le nom de ce slot dans la configuration de Patroni. Par contre, la suppression de la définition de ce slot dans la configuration supprimera également le slot.
Attention au nom du slot donné dans la configuration, qui ne doit pas rentrer en conflit avec les slots de réplication physique.
On peut vérifier l’existence des slots de réplications avec la vue pg_replication_slots
, en s’intéressant à celui de type logical
:
employees=# select * from pg_replication_slots;
slot_name | plugin | slot_type | datoid | database | temporary | active | active_pid | xmin | catalog_xmin | restart_lsn | confirmed_flush_lsn | wal_status | safe_wal_size | two_phase | conflicting
------------------+----------+-----------+--------+-----------+-----------+--------+------------+------+--------------+-------------+---------------------+------------+---------------+-----------+-------------
logical_slot_emp | pgoutput | logical | 16389 | employees | f | f | | | 817 | 0/170001C0 | 0/170001F8 | reserved | | f | f
catalog_xmin
correspondant à la transaction la plus ancienne affectant le catalogue système et requis par le slot;restart_lsn
la position du plus ancien WAL requis par la souscription;confirmed_flush_lsn
la dernière position reçue et rejouée côté souscription;
Supervision
L’essentiel à suite de la mise en place de cette configuration est de superviser la réplication logique et son état d’avancement.
Pour ce faire, il existe de nombreuses vues système pour la supervision.
Côté publication, nous pouvons utiliser les vues suviantes :
Et côté souscription :
pg_stat_subscription
pg_stat_subscription_stats
(depuis la version 16)
Par exemple avec la vue pg_replication_slots
et la requête suivante :
SELECT slot_name,
active,
confirmed_flush_lsn,
pg_current_wal_lsn(),
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained_walsize,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), confirmed_flush_lsn)) AS subscriber_lag
FROM pg_replication_slots;
slot_name | active | confirmed_flush_lsn | pg_current_wal_lsn | retained_walsize | subscriber_lag
-----------------------------------------+--------+---------------------+--------------------+------------------+----------------
logical_slot_emp | t | 0/72B96B50 | 0/72B96B50 | 385 kB | 0 bytes
active
donnant le status du slot de réplication;confirmed_flush_lsn
la dernière position reçue et rejouée côté souscription;retained_walsize
la quantité de WAL (en octet) retenue par le slot côté publicationsubscriber_lag
le retard de réplication logique (en octet) entre la publication et la souscription.
Qu’attendre de la version 17 de PostgreSQL ?
Comme nous l’avons vu, les versions actuelles ne prennent pas en charge les bascules des slots de réplication logique, ce qui dans un contexte de haute disponibilité de service assuré par Patroni, rend la mise en place de la réplication logique plus contraignante.
La version 17 de PostgreSQL va permettre de gérer nativement la bascule des slots de réplication et rendre plus aisée l’utilisation de la réplication logique.
Cependant, il reste encore des fonctionnalités attendues comme la réplication des séquences ou des schémas de bases de données, qui doivent se faire manuellement avant la mise en place d’une souscription.