Un abonnement est le côté aval de la réplication logique. Le nœud où un abonnement a été défini est nommé abonné. Un abonnement définit la connexion à une autre base de données et un ensemble de publications (une ou plus) auxquelles l'abonné veut souscrire.
La base de données abonnée se comporte comme n'importe quelle base de données d'une instance PostgreSQL et peut être utilisée comme éditeur pour d'autres bases de données en définissant ses propres publications.
Un nœud abonné peut avoir plusieurs abonnements si besoin. Il est possible de définir plusieurs abonnements entre une même paire publieur - abonné. Dans ce cas, il faut faire attention à ce que les objets des publications auxquelles l'abonné a souscrit ne se chevauchent pas.
Chaque abonnement recevra les changements par un slot de réplication (voir Section 27.2.6). Des slots de réplications supplémentaires peuvent être nécessaires pour la synchronisation initiale des données d'une table contenant des données pré-existantes mais ils seront supprimés à la fin de la synchronisation des données.
Un abonnement de réplication logique peut être réalisé sur un serveur
secondaire pour de la réplication synchrone (voir Section 27.2.8). Le nom du serveur secondaire
correspond par défaut au nom de l'abonnement. Un nom alternatif peut être
indiqué avec le paramètre application_name
dans les
informations de connexion à l'abonnement.
Les abonnements sont sauvegardés par pg_dump
si
l'utilisateur courant a des droits de superutilisateur. Si ce n'est pas le
cas, un message d'avertissement est renvoyé et les abonnements ne sont pas
sauvegardés. En effet, les informations d'abonnements contenues dans
pg_subscription
ne sont pas consultables par des
utilisateurs dotés de droits moins importants.
Un abonnement est ajouté en utilisant CREATE
SUBSCRIPTION
. Il peut être arrêté/repris à n'importe quel
moment en utilisant la commande ALTER SUBSCRIPTION
,
et il peut être supprimé par la commande DROP SUBSCRIPTION
.
Quand un abonnement est supprimé puis recréé, les informations de synchronisation sont perdues. Cela signifie que les données doivent être resynchronisées ensuite.
La définition d'un schéma n'est pas répliquée, et les tables publiées doivent exister sur la base abonnée. Seules des tables standards peuvent accueillir des données répliquées. Par exemple, il n'est pas pas possible de répliquer dans une vue.
La correspondance entre les tables du publieur et de l'abonné est réalisée en utilisant le nom entièrement qualifié de la table. La réplication entre des tables portant un nom différent sur la base abonnée n'est pas supportée.
La correspondance sur les colonnes d'une table se fait aussi par nom.
L'ordre des colonnes dans la table sur le serveur abonné ne correspond pas
forcément à l'ordre sur le serveur publieur. Les types de données n'ont pas
non plus besoin de correspondre, à partir du moment où la représentation
textuelle de la donnée peut être convertie vers le type de données cible.
Par exemple, vous pouvez répliquer depuis une colonne de type integer
vers une colonne de type bigint
. La table cible peut aussi avoir
des colonnes supplémentaires non fournies par la table publiée. Ce type de
colonne sera rempli avec la valeur par défaut fournie dans la définition de
la table cible. Cependant, la réplication logique en format binaire est plus
restrictive. Voir l'option
binary
de CREATE SUBSCRIPTION
pour les détails.
Comme présenté plus tôt, chaque abonnement (actif) reçoit les changements depuis un slot de réplication du serveur distant (publication).
Des slots de synchronisation de tables supplémentaires sont normalement
temporaires, créés en interne pour réaliser la synchronisation initiale des
tables et supprimés automatiquement quand elles ne sont plus nécessaires.
Ces slots de synchronisation de table ont des noms générés
automatiquement : « pg_%u_sync_%u_%llu
»
(paramètres oid
de la souscription,
relid
de la table, sysid
pour
l'identifiant du système).
Normalement, le slot de réplication distant est créé automatiquement en
utilisant la commande CREATE SUBSCRIPTION
et il est
supprimé automatiquement en utilisant la commande DROP
SUBSCRIPTION
. Dans certaines situations, il peut être utile ou
nécessaire de manipuler les abonnements ainsi que les slots de réplication
sous-jacents de façon séparées. Voici quelques exemples :
Lorsqu'en créant un abonnement, le slot de réplication correspondant
existe déjà. Dans ce cas, l'abonnement peut être créé en utilisant
l'option create_slot = false
pour réaliser
l'association avec le slot existant ;
Lorsqu'en créant un abonnement, le serveur distant n'est pas disponible
ou dans un état indéfini. Dans ce cas, l'abonnement peut être créé en
utilisant l'option connect = false
. Le serveur
distant ne sera alors jamais contacté. C'est la méthode utilisée par
pg_dump. Le slot de réplication distant devra
alors être créé manuellement avant que l'abonnement ne puisse être
activé ;
Lorsqu'on supprime un abonnement et que le slot de réplication doit être
conservé, par exemple lorsqu'une base abonnée est déplacée vers un
serveur différent et sera activée depuis cette nouvelle localisation.
Dans ce cas, il faut dissocier le slot de réplication de l'abonnement
correspondant en utilisant la commande ALTER
SUBSCRIPTION
avant de supprimer l'abonnement ;
Lorsque l'on supprime un abonnement et que le serveur distant n'est pas
joignable. Dans ce cas, il faut aussi dissocier le slot de réplication de
l'abonnement correspondant en utilisant ALTER
SUBSCRIPTION
avant de supprimer l'abonnement. Si l'instance
distante n'existe plus, aucune action supplémentaire n'est nécessaire.
Si, par contre, l'instance distante est simplement temporairement
injoignable, le slot de réplication (et tout slot de synchronisation de
table restant) devrait être supprimé manuellement, sinon l'instance va
persévérer à conserver ses fichiers WAL jusqu'à saturation de l'espace
disque disponible. Ces cas doivent être traités avec beaucoup de
précautions.
Créer des tables tests sur le publieur.
test_pub=# CREATE TABLE t1(a int, b text, PRIMARY KEY(a)); CREATE TABLE test_pub=# CREATE TABLE t2(c int, d text, PRIMARY KEY(c)); CREATE TABLE test_pub=# CREATE TABLE t3(e int, f text, PRIMARY KEY(e)); CREATE TABLE
Créer les mêmes tables sur l'abonné.
test_sub=# CREATE TABLE t1(a int, b text, PRIMARY KEY(a)); CREATE TABLE test_sub=# CREATE TABLE t2(c int, d text, PRIMARY KEY(c)); CREATE TABLE test_sub=# CREATE TABLE t3(e int, f text, PRIMARY KEY(e)); CREATE TABLE
Insérer des données sur les tables du côté publieur.
test_pub=# INSERT INTO t1 VALUES (1, 'one'), (2, 'two'), (3, 'three'); INSERT 0 3 test_pub=# INSERT INTO t2 VALUES (1, 'A'), (2, 'B'), (3, 'C'); INSERT 0 3 test_pub=# INSERT INTO t3 VALUES (1, 'i'), (2, 'ii'), (3, 'iii'); INSERT 0 3
Créer les publications pour les tables. Les publications
pub2
et pub3a
interdisent certaines
opérations publish
.
La publication pub3b
a un filtre de lignes (voir Section 31.3).
test_pub=# CREATE PUBLICATION pub1 FOR TABLE t1; CREATE PUBLICATION test_pub=# CREATE PUBLICATION pub2 FOR TABLE t2 WITH (publish = 'truncate'); CREATE PUBLICATION test_pub=# CREATE PUBLICATION pub3a FOR TABLE t3 WITH (publish = 'truncate'); CREATE PUBLICATION test_pub=# CREATE PUBLICATION pub3b FOR TABLE t3 WHERE (e > 5); CREATE PUBLICATION
Créer les abonnements (souscriptions) pour les publications. La
souscription sub3
s'abonne à pub3a
et
pub3b
. Toutes les souscriptions copieront les données
initiales par défaut.
test_sub=# CREATE SUBSCRIPTION sub1 test_sub-# CONNECTION 'host=localhost dbname=test_pub application_name=sub1' test_sub-# PUBLICATION pub1; CREATE SUBSCRIPTION test_sub=# CREATE SUBSCRIPTION sub2 test_sub-# CONNECTION 'host=localhost dbname=test_pub application_name=sub2' test_sub-# PUBLICATION pub2; CREATE SUBSCRIPTION test_sub=# CREATE SUBSCRIPTION sub3 test_sub-# CONNECTION 'host=localhost dbname=test_pub application_name=sub3' test_sub-# PUBLICATION pub3a, pub3b; CREATE SUBSCRIPTION
Observez que les données initiales des tables sont copiées, quelque soit
l'opération publish
de la publication.
test_sub=# SELECT * FROM t1; a | b ---+------- 1 | one 2 | two 3 | three (3 rows) test_sub=# SELECT * FROM t2; c | d ---+--- 1 | A 2 | B 3 | C (3 rows)
De plus, comme la copie initiale de données ignore l'opération
publish
et comme la publication pub3a
n'a pas de filtre de lignes, cela signifie que la table
t3
copiée contient toutes les lignes même quand elles ne
correspondent pas au filtre de lignes de la publication
pub3b
.
test_sub=# SELECT * FROM t3; e | f ---+----- 1 | i 2 | ii 3 | iii (3 rows)
Insérer plus de données dans les tables du côté publieur.
test_pub=# INSERT INTO t1 VALUES (4, 'four'), (5, 'five'), (6, 'six'); INSERT 0 3 test_pub=# INSERT INTO t2 VALUES (4, 'D'), (5, 'E'), (6, 'F'); INSERT 0 3 test_pub=# INSERT INTO t3 VALUES (4, 'iv'), (5, 'v'), (6, 'vi'); INSERT 0 3
Maintenant, les données du côté publieur ressemblent à ceci :
test_pub=# SELECT * FROM t1; a | b ---+------- 1 | one 2 | two 3 | three 4 | four 5 | five 6 | six (6 rows) test_pub=# SELECT * FROM t2; c | d ---+--- 1 | A 2 | B 3 | C 4 | D 5 | E 6 | F (6 rows) test_pub=# SELECT * FROM t3; e | f ---+----- 1 | i 2 | ii 3 | iii 4 | iv 5 | v 6 | vi (6 rows)
Observez que, durant la réplication normale, les opérations
publish
appropriées sont utilisées. Cela signifie que les
publications pub2
et pub3a
ne
répliquent pas les opérations INSERT
. De plus, la
publication pub3b
répliquera seulement les données
correspondant au filtre de lignes de pub3b
. Maintenant,
les données du côté abonné ressemblent à ceci :
test_sub=# SELECT * FROM t1; a | b ---+------- 1 | one 2 | two 3 | three 4 | four 5 | five 6 | six (6 rows) test_sub=# SELECT * FROM t2; c | d ---+--- 1 | A 2 | B 3 | C (3 rows) test_sub=# SELECT * FROM t3; e | f ---+----- 1 | i 2 | ii 3 | iii 6 | vi (4 rows)
Dans certains cas (i.e
Section 31.2.1), si le slot de réplication
n'a pas été créé automatiquement, l'utilisateur doit le créer manuellement
avant que la souscription ne soit activée. Les étapes pour créer le slot et activer
la souscription sont indiquées dans les exemples suivants.
Ces exemples spécifient le plugin de décodage logique standard
(pgoutput
), qui est utilisé par la réplication logique interne.
D'abord, créer une publication pour les exemples.
test_pub=# CREATE PUBLICATION pub1 FOR ALL TABLES; CREATE PUBLICATION
Exemple 1 : Quand l'abonnement indique connect = false
Créez l'abonnement.
test_sub=# CREATE SUBSCRIPTION sub1 test_sub-# CONNECTION 'host=localhost dbname=test_pub' test_sub-# PUBLICATION pub1 test_sub-# WITH (connect=false); WARNING: subscription was created, but is not connected HINT: To initiate replication, you must manually create the replication slot, enable the subscription, and refresh the subscription. CREATE SUBSCRIPTION
Sur le publieur, créer manuellement le slot. Comme son nom n'a pas été
spécifié lors de CREATE SUBSCRIPTION
, le nom du slot à créer
est le même que le nom d'abonnement, i.e. "sub1".
test_pub=# SELECT * FROM pg_create_logical_replication_slot('sub1', 'pgoutput'); slot_name | lsn -----------+----------- sub1 | 0/19404D0 (1 row)
Sur l'abonné, complétez l'activation de l'abonnement. Après cela,
les tables de pub1
commenceront à répliquer.
test_sub=# ALTER SUBSCRIPTION sub1 ENABLE; ALTER SUBSCRIPTION test_sub=# ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION; ALTER SUBSCRIPTION
Exemple 2 : Quand l'abonnement indique connect = false
,
mais spécifie aussi l'option
slot_name
.
Créez l'abonnement.
test_sub=# CREATE SUBSCRIPTION sub1 test_sub-# CONNECTION 'host=localhost dbname=test_pub' test_sub-# PUBLICATION pub1 test_sub-# WITH (connect=false, slot_name='myslot'); WARNING: subscription was created, but is not connected HINT: To initiate replication, you must manually create the replication slot, enable the subscription, and refresh the subscription. CREATE SUBSCRIPTION
Sur le publieur, créez manuellement un slot en utilisant le même nom que
celui spécifié lors de CREATE SUBSCRIPTION
, i.e. "myslot".
test_pub=# SELECT * FROM pg_create_logical_replication_slot('myslot', 'pgoutput'); slot_name | lsn -----------+----------- myslot | 0/19059A0 (1 row)
Sur l'abonné, les étapes restantes d'activation d'abonnement sont les mêmes que précédemment.
test_sub=# ALTER SUBSCRIPTION sub1 ENABLE; ALTER SUBSCRIPTION test_sub=# ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION; ALTER SUBSCRIPTION
Exemple 3 : Quand l'abonnement indique slot_name = NONE
Créez l'abonnement. Quand slot_name = NONE
alors
enabled = false
et create_slot = false
sont aussi nécessaires.
test_sub=# CREATE SUBSCRIPTION sub1 test_sub-# CONNECTION 'host=localhost dbname=test_pub' test_sub-# PUBLICATION pub1 test_sub-# WITH (slot_name=NONE, enabled=false, create_slot=false); CREATE SUBSCRIPTION
Sur le publieur, créez manuellement un slot en utilisant n'importe quel nom, i.e. "myslot".
test_pub=# SELECT * FROM pg_create_logical_replication_slot('myslot', 'pgoutput'); slot_name | lsn -----------+----------- myslot | 0/1905930 (1 row)
Sur l'abonné, associez l'abonnement avec le nom de slot juste créé.
test_sub=# ALTER SUBSCRIPTION sub1 SET (slot_name='myslot'); ALTER SUBSCRIPTION
Les étapes restantes d'activation d'abonnement sont les mêmes que précédemment.
test_sub=# ALTER SUBSCRIPTION sub1 ENABLE; ALTER SUBSCRIPTION test_sub=# ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION; ALTER SUBSCRIPTION