PostgreSQLLa base de données la plus sophistiquée au monde.
Documentation PostgreSQL 16.2 » Administration du serveur » Réplication logique » Abonnement

31.2. Abonnement #

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.

31.2.1. Gestion des slots de réplication #

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.

31.2.2. Exemples : Mettre en place la réplication logique #

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)

31.2.3. Exemples : Création de slot de réplication différée #

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