Solutions
Le prompt #
indique une commande à exécuter avec l'utilisateur root
. Le prompt $
est utilisé pour les commandes de l'utilisateur postgres
.
Quelques informations sur la solution proposée ici
Cette solution se base sur un système CentOS 6, installé à minima. Des adaptations seraient à faire pour un autre système (Linux ou Windows).
La solution apportée ici se base beaucoup sur la commande sudo
qui permet à un utilisateur d'exécuter des commandes que seul un autre utilisateur est habituellement permis à exécuter. Si sudo
n'est pas installé, son installation est donc conseillée :
La configuration de sudo
se fait avec la commande visudo
. Le but est d'ajouter la ligne suivante :
utilisateur ALL=(ALL) ALL
En remplaçant utilisateur
par le nom de l'utilisateur à qui on veut autoriser l'exécution de toute commande via la commande sudo
.
Le site postgresql.org propose son propre dépôt RPM, nous allons donc l'utiliser.
# yum install -y https://download.postgresql.org/pub/repos/yum
/testing/10/redhat/rhel-6-x86_64/pgdg-centos10-10-1.noarch.rpm
Retrieving https://download.postgresql.org/pub/repos/yum
/testing/10/redhat/rhel-6-x86_64/pgdg-centos10-10-1.noarch.rpm
warning : /var/tmp/rpm-tmp.RxZogq: Header V4 DSA/SHA1 Signature,
key ID 442df0f8: NOKEY
Preparing... ########################################**[100%]
1 :pgdg-centos10 ########################################**[100%]
# yum install -y postgresql10-server postgresql10-contrib
Les paquets suivants seront installés :
(1/12): libicu-4.2.1-14.el6.x86_64.rpm
(2/12): libxslt-1.1.26-2.el6_3.1.x86_64.rpm
(3/12): perl-5.10.1-144.el6.x86_64.rpm
(4/12): perl-Module-Pluggable-3.90-144.el6.x86_64.rpm
(5/12): perl-Pod-Escapes-1.04-144.el6.x86_64.rpm
(6/12): perl-Pod-Simple-3.13-144.el6.x86_64.rpm
(7/12): perl-libs-5.10.1-144.el6.x86_64.rpm
(8/12): perl-version-0.77-144.el6.x86_64.rpm
(9/12): postgresql10-10.0-beta4_1PGDG.rhel6.x86_64.rpm
(10/12): postgresql10-contrib-10.0-beta4_1PGDG.rhel6.x86_64.rpm
(11/12): postgresql10-libs-10.0-beta4_1PGDG.rhel6.x86_64.rpm
(12/12): postgresql10-server-10.0-beta4_1PGDG.rhel6.x86_64.rpm
Réplication asynchrone avec un seul secondaire Commençons par créer une instance PostgreSQL 10 :
# service postgresql-10 initdb
Initialisation de la base de données : [ OK ]
Il reste maintenant à configurer ce serveur pour qu'il archive les journaux de transactions. Mais tout d'abord, il faut créer le répertoire d'archivage.
$ mkdir /var/lib/pgsql/10/archives
La commande ayant été exécutée par l'utilisateur postgres
, les droits sont automatiquement bons.
Modifions maintenant le fichier /var/lib/pgsql/10/data/postgresql.conf
pour que PostgreSQL archive les journaux de transactions et écrive ses logs en anglais. Voici les modifications à apporter :
archive_mode = on
archive_command = 'rsync %p /var/lib/pgsql/10/archives/%f'
lc_messages = 'C'
La commande rsync
n'est pas installée par défaut. S'il est nécessaire de l'installer, voici la commande adéquate :
Le paramètre archive_mode
étant modifié, il nous faut redémarrer PostgreSQL :
# service postgresql-10 start
Démarrage du service postgresql-10 : [ OK ]
Forçons PostgreSQL à changer de journal de transactions, pour voir si l'archivage fonctionne bien :
$ psql -c "SELECT pg_switch_wal()"
pg_switch_wal
----------------
0 /168B71C
(1 row )
$ ls -l /var/lib/pgsql/10 /archives/
total 16384
-rw------- 1 postgres postgres 16777216 Sep 29 10:04 000000010000000000000001
Parfait.
La valeur renvoyée par la fonction pg_switch_wal()
peut varier suivant la quantité de données écrites précédemment par PostgreSQL.
Maintenant que l'archivage fonctionne, ajoutons un secondaire. Nous pourrions utiliser pg_basebackup
, c'est d'ailleurs ce qu'on fera pour le troisième secondaire. Nous allons le faire manuellement pour cette fois.
Tout d'abord, nous devons utiliser la procédure stockée pg_start_backup()
:
$ psql -c "SELECT pg_start_backup('un_label', true)"
pg_start_backup
-----------------
0 /2000028
(1 row )
Ensuite, il faut copier le répertoire des données :
$ cp -rp /var/lib/pgsql/10/data /var/lib/pgsql/10/secondaire1
Il est possible d'appeler pg_stop_backup()
:
$ psql -c "SELECT pg_stop_backup()"
NOTICE: pg_stop_backup complete , all required WAL segments have been archived
pg_stop_backup
----------------
0 /20000B8
(1 row )
Sur CentOS, les fichiers de configuration se trouvent dans le répertoire de données, ils sont donc copiés automatiquement. Cependant, avant d'aller plus loin et de procéder à la configuration de la mise en réplication, il faut configurer cette instance pour démarrer sur le port 5433 et lui permettre de démarrer automatiquement.
Le fichier /etc/sysconfig/pgsql/secondaire1
va permettre de définir l'emplacement de l'instance et son port d'écoute par défaut :
PGDATA=/var/lib/pgsql/10/secondaire1
PGPORT=5433
Il faut également positionner le paramètre port
dans le fichier de configuration /var/lib/pgsql/10/secondaire1/postgresql.conf
:
port=5433
Enfin, il faut créer le script de démarrage /etc/init.d/secondaire1
qui nous permettra de démarrer l'instance :
# ln -s /etc/init.d/postgresql-10 /etc/init.d/secondaire1
Il ne reste plus qu'à configurer la restauration dans le fichier recovery.conf
. Attention, ce dernier, contrairement aux autres, se trouvera dans le répertoire des données (donc ici /var/lib/postgresql/10/secondaire1
). Pour se faciliter la vie, nous allons copier le fichier d'exemple :
$ cp /usr/pgsql-10/share/recovery.conf.sample \
/var/lib/pgsql/10/secondaire1/recovery.conf
Enfin, il reste à configurer le fichier /var/lib/pgsql/10/secondaire1/recovery.conf
en modifiant ces paramètres :
restore_command = 'cp /var/lib/pgsql/10/archives/%f %p'
standby_mode = on
Un peu de ménage au niveau des fichiers du secondaire :
$ rm /var/lib/pgsql/10/secondaire1/postmaster.pid
/var/lib/pgsql/10/secondaire1/pg_wal/*
Le message d'erreur sur le répertoire archive_status
peut être ignoré sans conséquence.
Il ne reste plus qu'à démarrer le secondaire :
# service secondaire1 start
Démarrage du service secondaire1 : [ OK ]
La commande ps
suivante permet de voir que les deux serveurs sont lancés :
La première partie concerne le serveur secondaire :
PID CMD
881 /usr/pgsql-10/bin/postmaster -D /var/lib/pgsql/10/secondaire1
883 \_ postgres: logger process
884 \_ postgres: startup process waiting for 000000010000000000000006
886 \_ postgres: checkpointer process
887 \_ postgres: writer process
888 \_ postgres: stats collector process
La deuxième partie concerne le serveur principal :
PID CMD
535 /usr/pgsql-10/bin/postmaster -D /var/lib/pgsql/10/data
537 \_ postgres: logger process
539 \_ postgres: checkpointer process
540 \_ postgres: writer process
541 \_ postgres: wal writer process
542 \_ postgres: autovacuum launcher process
543 \_ postgres: archiver process last was 000000010000000000000005.00000028.backup
544 \_ postgres: stats collector process
545 \_ postgres: bgworker: logical replication launcher
Pour différencier les deux instances, il est possible d'identifier le répertoire de données (l'option -D
), les autres processus sont des fils du processus postmaster. Il est aussi possible de configurer le paramètre cluster_name
.
Le processus de démarrage (startup process ) indique qu'il attend le journal 000000010000000000000006
. Pour le récupérer, il exécute la restore_command
en indiquant le journal à récupérer.
Lançons un peu d'activité sur le principal pour générer (et surtout archiver) quelques journaux de transactions :
$ createdb b1
$ psql b1
psql (10)
Type "help" for help.
b1=# CREATE TABLE t1(id integer);
CREATE TABLE
b1=# INSERT INTO t1 SELECT generate_series(1, 1000000);
INSERT 0 1000000
b1=# \! ps -o pid,cmd fx | egrep "(archiver|startup)"
884 \_ postgres: startup process waiting for 000000010000000000000007
543 \_ postgres: archiver process last was 000000010000000000000006
[...]
Le processus d'archivage (« archiver process ») a archivé le journal 000000010000000000000006
. Le secondaire attend donc le suivant, à savoir le 000000010000000000000007
. Les journaux applicatifs indiquent ceci :
$ grep "restored log file" /var/lib/pgsql/10/secondaire1/log/postgresql*.log
2017-09-06 10:13:32.862 EDT [790] LOG: restored log file
"000000010000000000000003" from archive
2017-09-06 10:14:30.358 EDT [884] LOG: restored log file
"000000010000000000000004" from archive
2017-09-06 10:44:07.671 EDT [884] LOG: restored log file
"000000010000000000000005" from archive
2017-09-06 10:44:10.069 EDT [884] LOG: restored log file
"000000010000000000000006" from archive
Essayons de nous connecter au secondaire et d'exécuter quelques requêtes :
$ psql -p 5433
psql (10 )
Type "help" for help.
postgres=# SELECT * FROM pg_tablespace;
spcname | spcowner | spcacl | spcoptions
------------+----------+--------+------------
pg_default | 10 | |
pg_global | 10 | |
(2 rows )
postgres=# CREATE TABLE t1(id integer );
ERROR: cannot execute CREATE TABLE in a read-only transaction
On peut se connecter, lire des données, mais pas écrire.
Le comportement est visible dans les logs de l'instance secondaire dans le répertoire /var/lib/pgsql/10/secondaire1/log
:
2017-09-06 10:05:44.546 EDT [535] LOG: database system is ready to accept connections
PostgreSQL indique bien qu'il accepte des connexions en lecture seule.
Réplication asynchrone avec un seul secondaire en Warm Standby
Depuis la version 10, le comportement de PostgreSQL a changé et un secondaire sera par défaut en Hot Standby . Un secondaire sera donc accessible par défaut en lecture seule. Pour désactiver ce comportement, il faut passer le paramètre hot_standby
à off
sur le secondaire.
Il faut redémarre l'instance secondaire pour prendre en comtpe les changements :
# service secondaire1 restart
Stopping secondaire1 service: [ OK ]
Starting secondaire1 service: [ OK ]
$ ps -o pid,cmd fx | egrep "(archiver|startup)"
884 \_ postgres: startup process waiting for 000000010000000000000007
543 \_ postgres: archiver process last was 000000010000000000000006
[...]
Si nous essayons de nous connecter à l'instance nous obtenons le message :
$ psql -p 5433
psql: FATAL: the database system is starting up
Il est impossible de se connecter à l'instance en Warm Standby
Réplication asynchrone en flux avec un seul secondaire
Depuis la version 10, le comportement de PostgreSQL a changé et la réplication est activée par défaut en local.
Nous allons cependant modifier le fichier /var/lib/pgsql/10/data/pg_hba.conf
pour que l'accès en réplication soit autorisé pour l'utilisateur repli
:
host replication repli 127.0.0.1/32 md5
Cette configuration indique que l'utilisateur repli
peut se connecter en mode réplication à partir de l'adresse IP 127.0.0.1
. L'utilisateur repli
n'existant pas, il faut le créer (nous utiliserons le mot de passe repli ) :
$ createuser -SRD --replication -P repli
Enter password for new role:
Enter it again:
Pour prendre en compte la configuration, la configuration de l'instance principale doit être rechargée :
$ psql -c 'SELECT pg_reload_conf()'
Maintenant, il faut configurer le secondaire pour qu'il se connecte au principal. Cela se fait au travers du fichier /var/lib/pgsql/10/secondaire1/recovery.conf
et voici les paramètres à modifier :
restore_command = 'cp /var/lib/pgsql/10/archives/%f %p'
standby_mode = on
primary_conninfo = 'host=127.0.0.1 port=5432 user=repli password=repli'
trigger_file = '/tmp/secondaire1_autonome'
Il faut que la restore_command
renvoie un code retour différent de zéro pour indiquer au moteur qu'il n'y a plus de journaux de transaction et de passer en réplication en flux. Notez aussi que le fichier trigger_file
ne doit pas exister. Une vérification est nécessaire avant de procéder à la suite.
Il ne reste plus qu'à redémarrer le secondaire :
# service secondaire1 restart
Voici la liste des processus :
$ ps -o pid,cmd fx
PID CMD
4971 /usr/pgsql-10/bin/postmaster -D /var/lib/pgsql/10/secondaire1
4973 \_ postgres: logger process
4974 \_ postgres: startup process recovering 000000010000000000000006
4976 \_ postgres: checkpointer process
4977 \_ postgres: writer process
5006 \_ postgres: stats collector process
5008 \_ postgres: wal receiver process streaming 0/7000000
4634 /usr/pgsql-10/bin/postmaster -D /var/lib/pgsql/10/data
4636 \_ postgres: logger process
4638 \_ postgres: checkpointer process
4639 \_ postgres: writer process
4640 \_ postgres: wal writer process
4641 \_ postgres: autovacuum launcher process
4642 \_ postgres: archiver process last was 000000010000000000000006
4643 \_ postgres: stats collector process
4644 \_ postgres: bgworker: logical replication launcher
5009 \_ postgres: wal sender process repli 127.0.0.1(45754) streaming 0/7D52830
Nous avons bien les deux processus de réplication en flux wal sender
et wal receiver
.
Réplication asynchrone en flux avec deux secondaires
Cette fois-ci, nous allons utiliser pg_basebackup
pour créer le deuxième secondaire.
$ pg_basebackup -D /var/lib/pgsql/10/secondaire2 -P -h 127.0.0.1 -U repli
Password:
61401/61401 kB (100%), 1/1 tablespace
Les emplacements et le numéro de port du deuxième secondaire doit être différents des deux autres serveurs. Il faut donc créer le fichier /etc/sysconfig/pgsql/secondaire2
pour indiquer :
PGDATA=/var/lib/pgsql/10/secondaire2
PGPORT=5434
Il faut également mettre en place le script de démarrage :
# ln -s /etc/init.d/postgresql-10 /etc/init.d/secondaire2
Il est aussi nécessaire de modifier le numéro de port d'écoute de l'instance dans /var/lib/pgsql/10/secondaire2/postgresql.conf
:
port = 5434
Le fichier de restauration doit être copié :
$ cp /var/lib/pgsql/10/secondaire1/recovery.conf
/var/lib/pgsql/10/secondaire2/recovery.conf
Et légèrement modifié sur le nouvel secondaire :
trigger_file = '/tmp/secondaire2_autonome'
Et on peut enfin démarrer le deuxième secondaire :
# service secondaire2 start
Testons cette nouvelle architecture :
$ psql -p 5434
psql (10 )
Type "help" for help.
postgres=# SELECT * FROM pg_tablespace;
spcname | spcowner | spcacl | spcoptions
------------+----------+--------+------------
pg_default | 10 | |
pg_global | 10 | |
(2 rows )
postgres=# CREATE TABLE t1(id integer );
ERROR: cannot execute CREATE TABLE in a read-only transaction
postgres=# \c "port=5432 dbname=b1"
Vous êtes maintenant connecté à la base de données « b1 » en tant qu'utilisateur
« postgres » via le socket dans « /var/run/postgresql » via le port « 5432 ».
b1=# INSERT INTO t1 SELECT generate_series(1, 1000);
INSERT 0 1000
postgres=# \c "port=5433 dbname=b1"
Vous êtes maintenant connecté à la base de données « b1 » en tant qu' utilisateur
« postgres » via le socket dans « /var/run/postgresql » via le port « 5433 ».
b1=# SELECT count (*) FROM t1;
count
---------
1001000
(1 row )
b1=# \c "port=5434 dbname=b1"
Vous êtes maintenant connecté à la base de données « b1 » en tant qu'utilisateur
« postgres » via le socket dans « /var/run/postgresql » via le port « 5434 ».
b1=# SELECT count(*) FROM t1;
count
---------
1001000
(1 row)
Nettoyage des journaux Actuellement il n'y a aucun nettoyage des journaux archivés même s'ils ont été rejoués sur les secondaires. Quel paramètre modifier pour supprimer les anciens journaux ?
Le paramètre archive_cleanup_command
du fichier recovery.conf permet de spécifier une commande exécutée à la fin d'un restartpoint
(équivalent d'un checkpoint sur un secondaire). L'outil pg_archivecleanup
est utilisé pour supprimer les journaux inutiles.
archive_cleanup_command =
'/usr/pgsql-10/bin/pg_archivecleanup -d /var/lib/pgsql/10/archives/ %r'
En générant de l'activité et en forçant des CHECKPOINT
, le moteur va recycler ses journaux :
$ psql -c "INSERT INTO t1 SELECT * FROM generate_series(1,1000);" b1
$ psql -c "CHECKPOINT;"
$ psql -p 5433 -c "CHECKPOINT;"
L'option -d
permet d'avoir des informations supplémentaires dans les traces :
pg_archivecleanup: keeping WAL file "/var/lib/pgsql/10/archives//000000010000000000000062"
and later
pg_archivecleanup: removing file "/var/lib/pgsql/10/archives//000000010000000000000060"
pg_archivecleanup: removing file "/var/lib/pgsql/10/archives//00000001000000000000005F"
pg_archivecleanup: removing file "/var/lib/pgsql/10/archives//000000010000000000000061"
Sachant que les deux secondaires puisent leur journaux depuis le même répertoire d'archive. Quel pourrait être le risque ?
Le premier secondaire pourrait supprimer des journaux indispensables au deuxième secondaire. Sans ces journaux, la réplication du deuxième secondaire serait impossible et nécessiterait la reconstruction de celui-ci.
Slots de réplication > Il est possible d'éviter d'avoir recours à l'archivage en utilisant les slots de réplication. Ces derniers permettent au serveur principal de connaître les journaux encore nécessaires au serveur secondaire.
Créer un slot de réplication et configurer le deuxième secondaire pour utiliser ce slot. Contrôler que le slot est bien actif.
Depuis la version 10, les slots de réplication sont activés par défaut. Le nombre maximum de slots est fixé à 10 :
# SHOW max_replication_slots;
max_replication_slots
-----------------------
10
(1 row)
La commande suivante permet de créer un slot de réplication sur le serveur principal :
$ psql -c "SELECT pg_create_physical_replication_slot('slot_secondaire2');"
pg_create_physical_replication_slot
-------------------------------------
(slot_secondaire2,)
(1 row )
Il faut ensuite spécifier le slot dans le fichier recovery.conf
:
primary_slot_name = 'slot_secondaire2'
Puis redémarrer le serveur secondaire.
# service secondaire2 restart
Stopping secondaire2 service: [ OK ]
Starting secondaire2 service: [ OK ]
Enfin, l'appel à la vue pg_replication_slots
permet de s'assurer que le slot est bien actif :
postgres=# select * from pg_replication_slots ;
-[ RECORD 1 ]-------+-----------------
slot_name | slot_secondaire2
plugin |
slot_type | physical
datoid |
database |
temporary | f
active | t
active_pid | 1666
xmin |
catalog_xmin |
restart_lsn | 0 /62D5ACD8
confirmed_flush_lsn |
Arrêter le deuxième secondaire et générer beaucoup d'activité. Où sont conservés les journaux de transaction ? Quel est le journal le plus ancien sur le serveur principal ? Et dans les journaux archivés ?
Pour générer de l'activité :
psql -c "INSERT INTO t1 SELECT * FROM generate_series(1,1000000);" b1
En regardant les journaux au niveau du serveur principal et au niveau des archives :
$ ls -alh /var/lib/pgsql/10/data/pg_wal/
(...)
-rw------- 1 postgres postgres 16M 7 sept. 05:58 00000001000000000000008C
-rw------- 1 postgres postgres 16M 7 sept. 05:58 00000001000000000000008D
$ ls -alh /var/lib/pgsql/10/archives/
(...)
-rw------- 1 postgres postgres 16M 7 sept. 06:06 000000010000000000000064
-rw------- 1 postgres postgres 16M 7 sept. 06:06 000000010000000000000065
On constate que le principal a conservé les anciens journaux dans le répertoire pg_wal
.
Que ce serait-il passé sans slot de réplication ?
Le deuxième secondaire n'aurait pas pu récupérer des journaux indispensables à la réplication.
Démarrer le deuxième secondaire et contrôler que les deux secondaires sont bien en Streaming Replication .
Au démarrage :
$ psql -x -c "SELECT * FROM pg_stat_replication"
-[ RECORD 1 ]----+------------------------------
pid | 1249
usesysid | 16388
usename | repli
application_name | walreceiver
client_addr | 127.0 .0.1
client_hostname |
client_port | 45932
backend_start | 2017 -09 -07 05 :47:43 .316465 -04
backend_xmin |
state | streaming
sent_lsn | 0 /66A951D0
write_lsn | 0 /66A951D0
flush_lsn | 0 /66A951D0
replay_lsn | 0 /66A951D0
write_lag |
flush_lag |
replay_lag |
sync_priority | 0
sync_state | async
-[ RECORD 2 ]----+------------------------------
pid | 1801
usesysid | 16388
usename | repli
application_name | walreceiver
client_addr | 127.0 .0.1
client_hostname |
client_port | 45946
backend_start | 2017 -09 -07 06 :08:02 .109247 -04
backend_xmin |
state | streaming
sent_lsn | 0 /66A951D0
write_lsn | 0 /66A951D0
flush_lsn | 0 /66A951D0
replay_lsn | 0 /66A951D0
write_lag | 00 :00:01 .766658
flush_lag | 00 :00:01 .934458
replay_lag | 00 :00:02 .142817
sync_priority | 0
sync_state | async
Au bout de quelques minutes il est enfin synchronisé :
$ psql -x -c "SELECT * FROM pg_stat_replication"
-[ RECORD 1 ]----+------------------------------
pid | 1249
usesysid | 16388
usename | repli
application_name | walreceiver
client_addr | 127.0 .0.1
client_hostname |
client_port | 45932
backend_start | 2017 -09 -07 05 :47:43 .316465 -04
backend_xmin |
state | streaming
sent_lsn | 0 /66A951D0
write_lsn | 0 /66A951D0
flush_lsn | 0 /66A951D0
replay_lsn | 0 /66A951D0
write_lag |
flush_lag |
replay_lag |
sync_priority | 0
sync_state | async
-[ RECORD 2 ]----+------------------------------
pid | 1801
usesysid | 16388
usename | repli
application_name | walreceiver
client_addr | 127.0 .0.1
client_hostname |
client_port | 45946
backend_start | 2017 -09 -07 06 :08:02 .109247 -04
backend_xmin |
state | streaming
sent_lsn | 0 /66A951D0
write_lsn | 0 /66A951D0
flush_lsn | 0 /66A951D0
replay_lsn | 0 /66A951D0
write_lag |
flush_lag |
replay_lag |
sync_priority | 0
sync_state | async
Réplication synchrone en flux avec deux secondaires Nous allons passer le premier secondaire en tant que secondaire synchrone. Pour cela, il doit avoir un nom, indiqué par le paramètre de connexion application_name
. Le fichier de configuration /var/lib/pgsql/10/secondaire1/recovery.conf
doit être modifié ainsi :
primary_conninfo = 'host=127.0.0.1 port=5432 user=repli password=repli
application_name=secondaire1'
Ensuite, nous devons indiquer le serveur secondaire
dans la liste des serveurs synchrones initialisée par le paramètre synchronous_standby_names
du fichier /var/lib/pgsql/10/data/postgresql.conf
:
synchronous_standby_names = 'secondaire1'
Il ne reste plus qu'à recharger la configuration pour les deux serveurs :
# service postgresql-10 reload
# service secondaire1 restart
Il n'est pas nécessaire de redémarrer les trois serveurs. Un reload
du principal et un redémarrage du premier secondaire suffisent.
Vérifions l'état de la réplication pour les deux secondaires :
$ psql -p 5432
psql (10 )
Type "help" for help.
postgres=# \x
Expanded display is on.
postgres=# SELECT application_name, backend_start, state, sync_state
FROM pg_stat_replication;
-[ RECORD 1 ]----+------------------------------
application_name | walreceiver
backend_start | 2017 -09 -07 06 :08:02 .109247 -04
state | streaming
sync_state | async
-[ RECORD 2 ]----+------------------------------
application_name | secondaire1
backend_start | 2017 -09 -07 06 :11:06 .67367 -04
state | streaming
sync_state | sync
Nous avons bien un serveur synchrone et un serveur asynchrone.
Exécutons une requête de modification sur le principal :
$ psql -p 5432 b1
psql (10 )
Type "help" for help.
b1=# CREATE TABLE t2(id integer );
CREATE TABLE
La table est bien créée, sans attendre. Maintenant, arrêtons le serveur secondaire synchrone et faisons une nouvelle modification sur le principal :
# service secondaire1 stop
$ psql -p 5432 b1
psql (10)
Type "help" for help.
b1=# CREATE TABLE t3(id integer);
La requête reste bloquée. En effet, le secondaire ne peut pas répondre à la demande de la réplication car il est éteint. Du coup, le principal est bloqué en écriture. Il faut soit démarrer le secondaire, soit modifier la configuration du paramètre synchronous_standby_names
.
Démarrons le secondaire synchrone à partir d'un autre terminal et la requête se termine.
Nous allons maintenant passer le deuxième secondaire en synchrone avec le application_name
positionné à secondaire2 afin de les différencier (il est possible d'utiliser le même application_name
). Ensuite ajoutons secondaire2
à la liste des synchronous_standby_names
sur l'instance principale.
# service postgresql-10 reload
# service secondaire2 restart
$ psql -x -p 5432 -c "SELECT application_name, backend_start, state, sync_state
FROM pg_stat_replication;"
-[ RECORD 1 ]----+------------------------------
application_name | secondaire2
backend_start | 2017 -09 -07 06 :15:40 .969515 -04
state | streaming
sync_state | potential
-[ RECORD 2 ]----+------------------------------
application_name | secondaire1
backend_start | 2017 -09 -07 06 :12:46 .841986 -04
state | streaming
sync_state | sync
$ psql -p 5432 -c "SHOW synchronous_standby_names"
synchronous_standby_names
---------------------------
secondaire1,secondaire2
Cette fois les deux serveurs correspondent au synchronous_standby_names
, on peut constater qu'un serveur est sync
et l'autre potential
. Il ne peut y avoir qu'un seul serveur synchrone avec le principal. Si les deux serveurs avaient le même application_name
, il n'y aurait eu qu'un seul serveur sync
.
Arrêtons ensuite le premier secondaire synchrone :
# service secondaire1 stop
$ psql -p 5432 -c "SELECT application_name, backend_start, state, sync_state
FROM pg_stat_replication;"
-[ RECORD 1 ]----+------------------------------
application_name | secondaire2
backend_start | 2017 -09 -07 06 :15:40 .969515 -04
state | streaming
sync_state | sync
Et faisons une modification sur le principal :
$ psql -p 5432 -c "CREATE TABLE t4(id integer);" b1
CREATE TABLE
Cette fois, tout se passe bien. Le premier secondaire n'est pas disponible mais le second l'est. Il prend donc la suite du premier secondaire en tant que secondaire synchrone.
Si on souhaite avoir deux serveurs synchrones, il faut utiliser la syntaxe suivante pour le paramètre synchronous_standby_names
:
synchronous_standby_names = 'N (
liste serveurs)'
N
= nombre de serveurs synchrones
Dans notre cas synchronous_standby_names = '2(secondaire1,secondaire2)'
Après un reload du principal on constate bien que les deux serveurs sont synchrones :
$ psql -c "SELECT application_name, backend_start, state, sync_state
FROM pg_stat_replication;"
-[ RECORD 1 ]----+------------------------------
application_name | secondaire2
backend_start | 2017 -09 -07 06 :15:40 .969515 -04
state | streaming
sync_state | sync
-[ RECORD 2 ]----+------------------------------
application_name | secondaire1
backend_start | 2017 -09 -07 06 :19:59 .248803 -04
state | streaming
sync_state | sync
Pour repasser sur un seul serveur synchrone :
synchronous_standby_names = 'secondaire1,secondaire2'
ou :
synchronous_standby_names = '1(secondaire1,secondaire2)'
ou encore depuis la version 10 :
synchronous_standby_names = 'FIRST 1(secondaire1,secondaire2)'
L'ordre de cette liste donne la priorité qui sera utilisée pour l'application de la réplication synchrone. La version 10 permet de se baser sur un quorum :
synchronous_standby_names = 'ANY 1(secondaire1,secondaire2)'
On obtient alors un sync_state
à la valeur quorum
:
$ psql -c "SELECT application_name, backend_start, state, sync_state
FROM pg_stat_replication;"
FROM pg_stat_replication;"
-[ RECORD 1 ]----+------------------------------
application_name | secondaire2
backend_start | 2017 -09 -07 06 :15:40 .969515 -04
state | streaming
sync_state | quorum
-[ RECORD 2 ]----+------------------------------
application_name | secondaire1
backend_start | 2017 -09 -07 06 :19:59 .248803 -04
state | streaming
sync_state | quorum
Réplication synchrone : cohérence des lectures (optionnel) > Exécuter la commande SELECT pg_wal_replay_pause();
sur le premier secondaire synchrone. Ajouter des données sur le principal et contrôler leur présence sur le secondaire. Que constatez-vous ?
$ psql -p 5433 -c "SELECT pg_wal_replay_pause()"
$ psql -p 5432 b1
b1=# INSERT INTO t4 VALUES ('1' );
INSERT 0 1
b1=# SELECT * FROM t4;
c1
----
1
(1 row )
$ psql -p 5433 -c "SELECT * FROM t4;" b1
id
----
(0 rows )
$ psql -p 5434 -c "SELECT * FROM t4;" b1
id
----
1
(1 row )
La table est vide sur le premier secondaire synchrone mais elle est bien remplie sur le deuxième secondaire !
Est-ce que les instances sont bien synchrones (utilisez la vue pg_stat_replication
) ? Relancer le rejeu et contrôler la présence des enregistrements sur les deux serveurs.
$ psql -p 5432
postgres=# \x
Expanded display is on.
postgres=# SELECT application_name, backend_start, state, sent_lsn,
write_lsn, flush_lsn, replay_lsn, sync_state
FROM pg_stat_replication;
-[ RECORD 1 ]----+------------------------------
application_name | secondaire2
backend_start | 2017 -09 -07 06 :15:40 .969515 -04
state | streaming
sent_lsn | 0 /66AC5098
write_lsn | 0 /66AC5098
flush_lsn | 0 /66AC5098
replay_lsn | 0 /66AC5098
sync_state | quorum
-[ RECORD 2 ]----+------------------------------
application_name | secondaire1
backend_start | 2017 -09 -07 06 :19:59 .248803 -04
state | streaming
sent_lsn | 0 /66AC5098
write_lsn | 0 /66AC5098
flush_lsn | 0 /66AC5098
replay_lsn | 0 /66AC4F18
sync_state | quorum
Le premier secondaire est bien synchrone, on constate que les deux ont bien reçu les enregistrements mais seul le secondaire2 a rejoué les journaux.
On réactive le rejeu sur le premier secondaire :
$ psql -p 5433 -c "SELECT pg_wal_replay_resume()"
pg_wal_replay_resume
-----------------------
(1 row )
$ psql -p 5432
postgres=# \x
Expanded display is on.
postgres=# SELECT application_name, backend_start, state, sent_lsn,
write_lsn, flush_lsn, replay_lsn, sync_state
FROM pg_stat_replication;
-[ RECORD 1 ]----+------------------------------
application_name | secondaire2
backend_start | 2017 -09 -07 06 :15:40 .969515 -04
state | streaming
sent_lsn | 0 /66AC5098
write_lsn | 0 /66AC5098
flush_lsn | 0 /66AC5098
replay_lsn | 0 /66AC5098
sync_state | quorum
-[ RECORD 2 ]----+------------------------------
application_name | secondaire1
backend_start | 2017 -09 -07 06 :19:59 .248803 -04
state | streaming
sent_lsn | 0 /66AC5098
write_lsn | 0 /66AC5098
flush_lsn | 0 /66AC5098
replay_lsn | 0 /66AC5098
sync_state | quorum
Cette fois, le secondaire1 a bien rejoué les journaux. Les enregistrements sont bien présents dans la table t4 :
$ psql -p 5433 -c "SELECT * FROM t4;" b1
id
----
1
(1 row )
Quel paramètre modifier pour obtenir les mêmes résultats sur les deux instances ?
Par défaut la réplication synchrone garantie qu'aucune transaction n'est perdue mais elle ne s'assure pas que le secondaire synchrone a bien rejoué la transaction. Pour cela, il faut placer le paramètre synchronous_commit
à remote_apply
sur le principal.
Appliquer ce paramètre et effectuer la même opération (pause du rejeu puis insertion d'enregistrement sur le principal). Que constatez-vous?
Dans /var/lib/pgsql/10/data/postgresql.conf
:
synchronous_commit = remote_apply
Faire un rechargement de la configuration du serveur principal :
# service postgresql-10 reload
$ psql -p 5433 -c "SELECT pg_wal_replay_pause()"
pg_wal_replay_pause
----------------------
(1 row )
$ psql -p 5434 -c "SELECT pg_wal_replay_pause()"
pg_wal_replay_pause
----------------------
(1 row )
$ psql -p 5432 -c "INSERT INTO t4 VALUES ('2');" b1
Cette fois la requête est bloquée, il faut relancer le rejeu sur au moins un secondaire pour qu'elle puisse s'effectuer.
Réplication en cascade (optionnel) Nous allons créer un troisième secondaire et le placer en cascade du premier secondaire.
Créons le troisième secondaire avec pg_basebackup
:
$ pg_basebackup -D /var/lib/pgsql/10/secondaire3 -P -h 127.0.0.1 -U repli
Password:
178657/178657 Ko (100%), 1/1 tablespace
Remarquez que nous le créons à partir du principal. Nous aurions très bien pu le créer à partir du secondaire.
Les emplacements et le numéro de port du troisième secondaire doivent être différents des trois autres serveurs. Il faut donc créer le fichier /etc/sysconfig/pgsql/secondaire3
pour indiquer :
PGDATA=/var/lib/pgsql/10/secondaire3
PGPORT=5435
Il faut également mettre en place le script de démarrage :
# ln -s /etc/init.d/postgresql-10 /etc/init.d/secondaire3
Il est aussi nécessaire de modifier le numéro de port d'écoute de l'instance dans /var/lib/pgsql/10/secondaire3/postgresql.conf
:
port = 5435
Le fichier de restauration doit être copié :
$ cp /var/lib/pgsql/10/secondaire1/recovery.conf
/var/lib/pgsql/10/secondaire3/recovery.conf
Et légèrement modifié sur le nouveau secondaire :
primary_conninfo = 'host=127.0.0.1 port=5433 user=repli password=repli
application_name=secondaire3'
trigger_file = '/tmp/secondaire3_autonome'
C'est dans cette dernière modification que tout se joue : notez que le numéro de port indique bien le premier secondaire et non pas le principal. Il faut également désactiver l'archive_cleanup_command
.
Avant d'aller plus loin, il faut également s'assurer que les connections de préplication sont autorisées sur l'instance secondaire1
. Le fichier /var/lib/pgsql/10/secondaire1/pg_hba.conf
doit contenir la ligne suivante :
host replication repli 127.0.0.1/32 md5
Suite à la modification des paramètres de sécurité, la configuration du secondaire1 doit être rechargée :
$ psql -p 5433 -c "SELECT pg_reload_conf()"
pg_reload_conf
----------------
t
( 1 ligne)
On peut enfin démarrer le troisième secondaire :
# service secondaire3 start
Le secondaire1 a bien un processus walreceiver et walsender :
-bash-4.1$ ps -o pid,cmd fx
PID CMD
2603 /usr/pgsql-10/bin/postmaster -D /var/lib/pgsql/10/secondaire3
2605 \_ postgres: logger process
2606 \_ postgres: startup process recovering 000000010000000000000068
2608 \_ postgres: checkpointer process
2609 \_ postgres: writer process
2611 \_ postgres: stats collector process
2612 \_ postgres: wal receiver process streaming 0/68000060
2406 /usr/pgsql-10/bin/postmaster -D /var/lib/pgsql/10/secondaire1
2408 \_ postgres: logger process
2409 \_ postgres: startup process recovering 000000010000000000000068
2411 \_ postgres: checkpointer process
2412 \_ postgres: writer process
2413 \_ postgres: stats collector process
2414 \_ postgres: wal receiver process streaming 0/68000060
2613 \_ postgres: wal sender process repli 127.0.0.1(36364) streaming 0/68000060
2262 /usr/pgsql-10/bin/postmaster -D /var/lib/pgsql/10/secondaire2
2264 \_ postgres: logger process
2265 \_ postgres: startup process recovering 000000010000000000000068
2267 \_ postgres: checkpointer process
2268 \_ postgres: writer process
2269 \_ postgres: stats collector process
2270 \_ postgres: wal receiver process streaming 0/68000060
443 /usr/pgsql-10/bin/postmaster -D /var/lib/pgsql/10/data
445 \_ postgres: logger process
447 \_ postgres: checkpointer process
448 \_ postgres: writer process
449 \_ postgres: wal writer process
450 \_ postgres: autovacuum launcher process
451 \_ postgres: archiver process last was 000000010000000000000067.00000028.backup
452 \_ postgres: stats collector process
453 \_ postgres: bgworker: logical replication launcher
2271 \_ postgres: wal sender process repli 127.0.0.1(45956) streaming 0/68000060
2415 \_ postgres: wal sender process repli 127.0.0.1(45958) streaming 0/68000060
Une connexion sur l'instance secondaire1 permettra de vérifier :
$ psql -p 5433 -c "\x" -c "SELECT * FROM pg_stat_replication;"
Affichage étendu activé.
-[ RECORD 1 ]----+------------------------------
pid | 2613
usesysid | 16388
usename | repli
application_name | secondaire3
client_addr | 127.0 .0.1
client_hostname |
client_port | 36364
backend_start | 2017 -09 -07 06 :33:48 .669222 -04
backend_xmin |
state | streaming
sent_lsn | 0 /68000140
write_lsn | 0 /68000140
flush_lsn | 0 /68000140
replay_lsn | 0 /68000140
write_lag | 00 :00:00 .000479
flush_lag | 00 :00:00 .015034
replay_lag | 00 :00:00 .015219
sync_priority | 0
sync_state | async