# _Réplication Logique_ avec PostgreSQL

<https://www.postgresql.org/docs/11/logical-replication.html>

## Mise en place

Sur le serveur primaire et réplica, on s'assure d'avoir deux bases :

~~~
postgres $ createuser -p 5432 -P foo
Enter password for new role:

postgres $ createdb -p 5432 -O foo foo
~~~

Sur le serveur primaire, on modifie la directive `wal_level` :

~~~
wal_level = logical
~~~

Puis l'on crée un utilisateur `replication` et une publication liée à cette base :

~~~
postgres $ createuser -p 5432 -P --replication replication

postgres $ psql -p 5432 foo

foo=# GRANT SELECT on ALL TABLES IN SCHEMA public TO replication;
GRANT

foo=# CREATE PUBLICATION alltables FOR ALL TABLES;
CREATE PUBLICATION
~~~

Sur le primaire, on autorise l'utilisateur replication à se connecter dans `pg_hba.conf` :

~~~
host base replication 192.168.0.1/24 md5
~~~

Ensuite, il faut créer les tables de façon identique sur les bases, pour cela on dump le schema de la ou des bases concernée depuis le primaire vers le secondaire :

~~~
pg_dump -h IP_PRIMAIRE -p 5432 -s base1 | psql -h IP_SECONDAIRE -p 5432 base1
~~~


Sur le serveur réplica, on crée une subscription liée à cette base :

~~~
postgres $ psql -p 5432 foo

foo=# CREATE SUBSCRIPTION mysub CONNECTION 'dbname=foo host=192.0.2.1 port=5436 user=replication password=PASSWORD' PUBLICATION alltables;
NOTICE:  created replication slot "mysub" on publisher
CREATE SUBSCRIPTION
~~~

Attention, contrairement à la réplication physique, rien n'empêche d'écrire sur le serveur réplica.


Enfin, l'insertion de données sur le serveur primaire, devrait provoquer le transfert de ces données sur le serveur réplica :

~~~
LOG:  logical replication table synchronization worker for subscription "mysub", table "t" has started
~~~

Si besoin, on peut « rafraichir » la subscription sur le serveur replica via la commande :

~~~
postgres $ psql -p 5432 foo

foo=# ALTER SUBSCRIPTION mysub REFRESH PUBLICATION;
~~~


## Administration

### Ajout / modification d'une table

Si l'on veut ajouter ou modifier une table, il faut :

- créer / modifier la table sur les 2 serveurs maître et réplica
- relancer la commande suivante sur le serveur maître :

~~~
postgres $ psql -p 5432 foo

foo=# GRANT SELECT on ALL TABLES IN SCHEMA public TO replication;
GRANT
~~~

- relancer la commande suivante sur le serveur réplica :

~~~
postgres $ psql -p 5432 foo

foo=# ALTER SUBSCRIPTION mysub REFRESH PUBLICATION;
~~~

On peut ensuite insérer des données sur la nouvelle table qui sera répliquée.

### Suspendre une réplication

Mettre en pause une réplication :

~~~
foo=# ALTER SUBSCRIPTION mysub DISABLE;

LOG:  le processus apply de réplication logique pour la souscription « mysub » s'arrêtera car la souscription a été désactivée
~~~

Reprendre une réplication :

~~~
foo=# ALTER SUBSCRIPTION mysub ENABLE;
~~~

### Suppression

~~~
DROP PUBLICATION alltables;
DROP SUBSCRIPTION mysub;
~~~

### Ajout d'un schema à une base / utilisateur répliqué

Si l'on ajoute un schéma à une base répliqué, pour que la syncho des données de ce shcéma se fasse, il faut que l'utilisateur SQL dédié à la réplication est le droit **USAGE** sur ce schéma :

~~~
foo=# GRANT USAGE ON SCHEMA bar TO replication;
~~~

Sinon la copié initiale des données de ce schéma n'est pas copié dans la réplication, on auras des erreurs de ce type dans les logs sur le master :

~~~
2020-05-30 00:06:26.163 CEST [40846] replication@foo ERROR: permission denied for schema bar
2020-05-30 00:06:26.163 CEST [40846] replication@foo STATEMENT: COPY bar."21GEHAVL" TO STDOUT
~~~

Sur le réplica on aura des erreurs de ce type :

~~~
020-05-30 15:55:57.594 CEST [986] LOG: le processus de synchronisation des tables en réplication logique pour la souscription « mysub_sql3 », table « 21NOHAII » a démarré
2020-05-30 15:55:57.601 CEST [985] ERREUR: n'a pas pu lancer la copie initiale du contenu de la table « bar.21GEHVRH » : ERROR: permission denied for schema bar
~~~

## Monitoring

Sur le serveur secondaire, on peut surveiller le bon fonctionnement de la réplication ainsi, mais cela indique seulement l'état sur le secondaire, il ne surveille pas la réplication par rapport au primaire :

~~~
postgres=# select * from pg_subscription ;
 subdbid | subname | subowner | subenabled |                                subconninfo                                 | subslotname | subsynccommit | subpublications 
---------+---------+----------+------------+----------------------------------------------------------------------------+-------------+---------------+-----------------
   16385 | mysub   |       10 | t          | dbname=foo host=192.0.2.1 port=5432 user=replication password=PASSWORD     | mysub       | off           | {alltables}
(1 row)

postgres=# select * from pg_stat_subscription ;
 subid | subname |  pid  | relid | received_lsn |      last_msg_send_time       |     last_msg_receipt_time     | latest_end_lsn |        latest_end_time        
-------+---------+-------+-------+--------------+-------------------------------+-------------------------------+----------------+-------------------------------
 16388 | mysub   | 12767 |       | 0/16A58D8    | 2019-09-17 21:09:16.512057+00 | 2019-09-17 21:09:16.512241+00 | 0/16A58D8      | 2019-09-17 21:09:16.512057+00
~~~

### check_pgactivity

Pour surveiller la réplication logique depuis le primaire on utilise le check_pgactivity : 
<https://github.com/OPMDG/check_pgactivity>

En Debian 10, on installe le paquet *check-pgactivity*

On l'utilise avec le service *replication_slots* qui regarde le nombre de fichier WAL et le nombre de fichier dans pg_replslot pour chaque slot de réplication.

Si les fichiers WAL s'accumule ainsi que les fichiers dans pg_replslot/ c'est qu'il y a un problème de réplication, le check passe en critique est indique quel slot est impacté.

On positionne des valeurs de *warning* et *critique* comme ceci :

~~~
postgres@serv:~$ /usr/lib/nagios/plugins/check_pgactivity -s replication_slots -w 'wal=250,replslot=40' -c 'wal=350,replslot=80'
~~~

Pour que ce check fonctionne, il faut utiliser un utilisateur sans privilège particulier, mais avec le droit EXECUTE sur la fonction *pg_ls_dir(text)*

On peut créer l'utilisateur comme ceci :

~~~
postgres=# CREATE ROLE monitor WITH LOGIN PASSWORD 'foo';
postgres=# GRANT EXECUTE ON FUNCTION pg_ls_dir(text) TO monitor;
~~~


