Replication sets
================

レプリケーションセットは、PGDノードがサブスクライブできるテーブルのグループです。レプリケーションセットを使用して、各ノードが他のノードの正確なコピーである通常の対称マルチマスタートポロジよりも複雑なレプリケーショントポロジを作成できます。

すべてのPGDグループは、グループと同じ名前のレプリケーションセットを作成します。このレプリケーションセットは、すべてのユーザーテーブルとDDLレプリケーションに使用されるデフォルトのレプリケーションセットです。すべてのノードがそれにサブスクライブされます。つまり、デフォルトでは、すべてのユーザーテーブルがすべてのノード間でレプリケートされます。

レプリケーションセットを使用する
--------------------------------

`bdr.create_replication_set <https://www.enterprisedb.com/docs/pgd/latest/reference/tables-views-functions/repsets-management#bdrcreate_replication_set>`_ 
を使用して、挿入、更新、削除、または切り捨てアクションを含めるかどうかを指定して、レプリケーションセットを作成できます。
1つのオプションでは既存のテーブルをセットに追加でき、2番目のオプションでは作成時にテーブルを追加するかどうかを定義します。

テーブルを手動で定義して、レプリケーションセットに追加または削除することもできます。

レプリケーションセットに含まれるテーブルは、ノードがクラスターに参加したときおよびその後に維持されます。

ノードが参加したら、レプリケーションセットからテーブルを削除できますが、再同期操作を使用して新しいテーブルを追加する必要があります。

デフォルトでは、新しく定義されたレプリケーションセットはDDLまたはPGD管理ファンクション呼び出しをレプリケートしません。
`bdr.replication_set_add_ddl_filter <https://www.enterprisedb.com/docs/pgd/latest/reference/tables-views-functions/repsets-ddl-filtering#bdrreplication_set_add_ddl_filter>`_ を使用する

複製するコマンドを定義します。

PGDは、すべてのノードでレプリケーションセット定義を作成します。次に、
`bdr.alter_node_replication_sets <https://www.enterprisedb.com/docs/pgd/latest/reference/tables-views-functions/repsets-management#bdralter_node_replication_sets>`_ を使用して各レプリケーションセットをパブリッシュまたはサブスクライブするように各ノードを定義できます。

ファンクションを使用して、これらの定義を後で変更したり、レプリケーションセットを削除したりできます。

..  Note::
   選択的レプリケーションにデフォルトのレプリケーションセットを使用しないでください。クラスター内のPGDノードにあるデフォルトのレプリケーションセットは、 DDLレプリケーションおよび管理ファンクション呼び出しにもデフォルトで使用されるため、ドロップまたは変更しないでください。

パーティション分割テーブルの動作
--------------------------------

PGDは、パーティション化テーブルを透過的にサポートします。つまり、パーティション化されたテーブルをレプリケーションセットに追加できます。

パーティションに関係する変更は、ダウンストリームにレプリケートされます。

..  Note::
   パーティションがパーティションテーブルを介してレプリケートされる場合、パーティションで直接実行されるステートメントは、親テーブルで実行されたときと同じようにレプリケートされます。例外は`TRUNCATE` コマンドであり、これは常に影響を受けるテーブルまたはパーティションのリストを使用して複製します。

個々のパーティションをレプリケーションセットに追加できます。この場合、それらは通常のテーブルと同様に、つまり、ダウンストリームのパーティションと同じ名前のテーブルにレプリケートされます。パーティショニング定義がプロバイダーとサブスクライバーの両方で同じである場合、パーティショニングロジックを実行する必要がないため、この動作にはいくつかのパフォーマンスの利点があります。

..  Note::
   ルートパーティションテーブルがレプリケーションセットの一部である場合、個々のパーティションのメンバーシップは無視されます。そのルートテーブルのメンバーシップのみが考慮されます。

外部キーを使用した動作
----------------------

外部キー制約は、参照テーブルの各行が参照先テーブルの行と一致することを保証します。したがって、参照テーブルがレプリケーションセットのメンバーである場合、参照されるテーブルも同じレプリケーションセットのメンバーである必要があります。

PGDの現在のバージョンは、この条件をチェックまたは適用しません。レプリケーションセットにテーブルを追加するとき、データベース管理者は、外部キーによって参照されるすべてのテーブルも追加されていることを確認する必要があります。

次のクエリを使用して、この要件を満たさないすべての外部キーとレプリケーションセットをリストできます。参照テーブルはレプリケーションセットのメンバーですが、参照されるテーブルはレプリケーションセットのメンバーではありません。

.. code:: sql

   SELECT t1.relname,
          t1.nspname,
          fk.conname,
          t1.set_name
     FROM bdr.tables AS t1
     JOIN pg_catalog.pg_constraint AS fk
       ON fk.conrelid = t1.relid
      AND fk.contype = f
    WHERE NOT EXISTS (
     SELECT *
       FROM bdr.tables AS t2
      WHERE t2.relid = fk.confrelid
        AND t2.set_name = t1.set_name
   );

このクエリの出力は次のようになります。

.. code:: sql

    relname | nspname |  conname  | set_name
   - --------+---------+-----------+----------
    t2      | public  | t2_x_fkey | s2
   (1 row)

この出力は、テーブル\ ``t2`` がレプリケーションセット\ ``s2``
のメンバーであるが、外部キー\ ``t2_x_fkey``
によって参照されるテーブルがそうでないことを意味します。

``TRUNCATE CASCADE``
コマンドは、コマンドをレプリケートする前にレプリケーションセットのメンバーシップを考慮します。例

.. code:: sql

   TRUNCATE table1 CASCADE;

これは、レプリケーションセットのみの一部であるすべてのテーブルでカスケードなしの\ ``TRUNCATE``
になります。

.. code:: sql

   TRUNCATE table1, referencing_table1, referencing_table2 ...

レプリケーションセットのメンバーシップ
--------------------------------------

1つ以上のレプリケーションセットにテーブルを追加または削除できます。そうすると、これらのテーブルの変更DMLのレプリケーションにのみ影響します。スキーマ変更DDLは、
DDLレプリケーションセットフィルターによって処理されます
:ref:`DDLレプリケーションフィルタリング <DDLレプリケーションフィルタリング>` を参照してください。

レプリケーションは、ノードレプリケーションセット構成を使用してレプリケーションセットのテーブルメンバーシップを使用して、レプリケートするアクションとそれらのレプリケート先のノードを決定します。決定は、すべてのメンバーシップとレプリケーションセットオプションの和集合を使用して行われます。テーブルが、
INSERTアクションのみをレプリケートするレプリケーションセットAと、
UPDATEアクションのみをレプリケートするレプリケーションセットBのメンバーであるとします。ターゲットノードがレプリケーションセットAおよびBの両方にサブスクライブされている場合、
INSERTおよびUPDATEアクションは両方ともレプリケートされます。

`bdr.replication_set_add_table <https://www.enterprisedb.com/docs/pgd/latest/reference/tables-views-functions/repsets-membership#bdrreplication_set_add_table>`_ および `bdr.replication_set_remove_table <https://www.enterprisedb.com/docs/pgd/latest/reference/tables-views-functions/repsets-membership#bdrreplication_set_remove_table>`_ を使用してメンバーシップを制御できます。

レプリケーションセットのリスト
------------------------------

次のクエリーを使用して、既存のレプリケーションセットをリストできます。

.. code:: sql

   SELECT set_name
   FROM bdr.replication_sets;

このクエリを使用して、特定のレプリケーションセット内のすべてのテーブルをリストできます。

.. code:: sql

   SELECT nspname, relname
   FROM bdr.tables
   WHERE set_name = myrepset;

:ref:`外部キーを使用した動作 <外部キーを使用した動作>` は、参照されるテーブルが参照テーブルと同じレプリケーションセットに含まれていないすべての外部キーをリストするクエリーを示しています。

次のSQLを使用して、現在のノードが発行およびサブスクライブするレプリケーションセットを表示します。

.. code:: sql

    SELECT node_id,
           node_name,
           pub_repsets,
           sub_repsets
      FROM bdr.local_node_summary;

このコードは、次のような出力を生成します。

.. code:: sql

     node_id   | node_name |    pub_repsets     |   sub_repsets
   - -----------+-----------+----------------------------------------
    1834550102 | s01db01   | {bdrglobal,bdrs01} | {bdrglobal,bdrs01}
   (1 row)

クラスター内のすべてのノードに対して同じクエリーを実行するには、次のクエリーを使用できます。このアプローチでは、すべてのノードに関連付けられたレプリケーションセットを同時に取得します。

.. code:: sql

   WITH node_repsets AS (
     SELECT jsonb_array_elements(
       bdr.run_on_all_nodes($$
           SELECT
             node_id,
             node_name,
             pub_repsets,
             sub_repsets
           FROM bdr.local_node_summary;
       $$)::jsonb
     ) AS j
   )
   SELECT j->response->command_tuples->0->>node_id AS node_id,
          j->response->command_tuples->0->>node_name AS node_name,
          j->response->command_tuples->0->>pub_repsets AS pub_repsets,
          j->response->command_tuples->0->>sub_repsets AS sub_repsets
   FROM node_repsets;

これは、例を示します。

.. code:: sql

     node_id   | node_name |    pub_repsets     |    sub_repsets
   - -----------+-----------+----------------------------------------
    933864801  | s02db01   | {bdrglobal,bdrs02} | {bdrglobal,bdrs02}
    1834550102 | s01db01   | {bdrglobal,bdrs01} | {bdrglobal,bdrs01}
    3898940082 | s01db02   | {bdrglobal,bdrs01} | {bdrglobal,bdrs01}
    1102086297 | s02db02   | {bdrglobal,bdrs02} | {bdrglobal,bdrs02}
   (4 rows)

DDLレプリケーションフィルタリング
---------------------------------

デフォルトでは、サポートされているすべてのDDLのレプリケーションは、デフォルトのPGDグループレプリケーションセットを介して発生します。このレプリケーションは、PGDグループと同じ名前のDDLフィルターを使用して実現されます。このフィルタは、
PGDグループの作成時に、デフォルトのPGDグループレプリケーションセットに追加されます。

既存のすべてのレプリケーションセットのDDLレプリケーションフィルターを変更することにより、この動作を調整できます。これらのフィルターは、レプリケーションセットのテーブルメンバーシップとは独立しています。データ変更と同様に、各DDLステートメントは、複数のレプリケーションセットの複数のフィルターと一致する場合でも、1回だけレプリケートされます。

次のクエリーを使用して既存のDDLフィルターをリストできます。これは、フィルターごとに、コマンドタグとロール名に適用された正規表現を示します。

.. code:: sql

   SELECT * FROM bdr.ddl_replication;

`bdr.replication_set_add_ddl_filter <https://www.enterprisedb.com/docs/pgd/latest/reference/tables-views-functions/repsets-ddl-filtering#bdrreplication_set_add_ddl_filter>`_ および `bdr.replication_set_remove_ddl_filter <https://www.enterprisedb.com/docs/pgd/latest/reference/tables-views-functions/repsets-ddl-filtering#bdrreplication_set_remove_ddl_filter>`_ を使用して、
DDLフィルターを操作できます。これらは\ ``DDL`` であると考えられるため、
DDLレプリケーションとグローバルロックの対象になります。

選択的レプリケーションの例
--------------------------

この例では、テーブルを特定のノードのグループに選択的にレプリケートするようにEDB
Postgres分散を構成します。

クラスター構成
^^^^^^^^^^^^^^

この例では、6つのデータノード、\ ``data-a1`` 〜\ ``data-a3``
および\ ``data-b1`` 〜\ ``data-b3``
のクラスターが2つの場所にあることを前提としています。これらがメンバーである2つのロケーションは、\ ``region_a``
および\ ``region_b`` グループとして表されます。

また、お勧めのように、この例では記載されていませんが、 ``region-c``
に\ ``witness``
という名前の監視ノードがあります。クラスターの名前は\ ``sere`` です。

応募要項
^^^^^^^^

この例は、音楽作品の公演に出席した人々の意見を記録するアプリケーションで動作します。出席者用のテーブル、作品用のテーブル、そしてオピニオンテーブルがあります。オピニオンテーブルには、各出席者が見た各作品、どこで、いつ見たか、および作品をどのように採点したかが記録されます。データ規制のため、この例では、意見データは、意見が記録されたリージョンにのみ存在する必要があることを前提としています。

テーブルの作成
^^^^^^^^^^^^^^

最初のステップは、適切なテーブルを作成することです。

.. code:: sql

   CREATE TABLE attendee (
      id bigserial PRIMARY KEY, 
      email text NOT NULL
   );

   CREATE TABLE work (
       id int PRIMARY KEY,
       title text NOT NULL,
       author text NOT NULL
   );

   CREATE TABLE opinion (
       id bigserial PRIMARY KEY,
       work_id int NOT NULL REFERENCES work(id),
       attendee_id bigint NOT NULL REFERENCES attendee(id),
       country text NOT NULL,
       day date NOT NULL,
       score int NOT NULL
   );

グループとレプリケーションセットの表示
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

デフォルトでは、 EDB
Postgres分散は、各テーブル全体を各すべてのノードにレプリケートするように構成されます。これは、レプリケーションセットを介して管理されます。

初期構成のデフォルトレプリケーションセットを表示するには、次のコマンドを実行します。

.. code:: sql

   SELECT node_group_name, default_repset, parent_group_name
   FROM bdr.node_group_summary;

.. code:: console

    node_group_name | default_repset | parent_group_name
   - ----------------+----------------+-------------------
    sere            | sere           |
    region_a        | region_a       | sere
    region_b        | region_b       | sere
    region_c        | region_c       | sere

出力には、トップレベルのグループ\ ``sere`` があり、 ``sere``
という名前のデフォルトのレプリケーションセットがあることがわかります。
3つのサブグループには、それぞれ、サブグループと同じ名前のレプリケーションセットがあります。
``region_a`` グループには、\ ``region_a``
デフォルトレプリケーションセットがあります。

デフォルトでは、既存のすべてのテーブルと新しいテーブルは、トップレベルグループのレプリケーションセットのメンバーになります。

レプリケーションセットへのテーブルの追加
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

次の手順は、リージョンを表すグループに属するレプリケーションセットにテーブルを追加することです。前述のように、新しいテーブルはすべて\ ``sere``
レプリケーションセットに自動的に追加されます。次を実行して、それを確認できます。

.. code:: sql

   SELECT relname, set_name FROM bdr.tables ORDER BY relname, set_name;

.. code:: console

    relname  | set_name 
   - ---------+----------
    attendee | sere
    opinion  | sere
    work     | sere
   (3 rows)

``opinion`` テーブルは、\ ``region_a`` でのみ、別に\ ``region_b``
でのみレプリケートする必要があります。これを行うには、各リージョンのレプリカセットにテーブルを追加します。

.. code:: sql

   SELECT bdr.replication_set_add_table(opinion, region_a);
   SELECT bdr.replication_set_add_table(opinion, region_b);

しかし、\ ``opinion`` はまだ\ ``sere``
レプリケーションセットのメンバーであるため、これで完了ではありません。テーブルが複数のレプリケーションセットのメンバーである場合、各テーブルでレプリケートされます。ただし、各行は各ターゲットノードで1回のみレプリケートされるため、これはパフォーマンスに影響しません。
``opinion``
をすべてのノードでレプリケートしたくないため、トップレベルグループのレプリケーションセットから削除する必要があります。

.. code:: sql

   SELECT bdr.replication_set_remove_table(opinion, sere);

これらの変更を確認できるようになりました。

.. code:: sql

   SELECT relname, set_name FROM bdr.tables ORDER BY relname, set_name;

.. code:: console

    relname  | set_name
   - ---------+-------------------
    attendee | sere
    opinion  | region_a
    opinion  | region_b
    work     | sere
   (4 rows)

このプロセスにより、目的の選択的レプリケーションが提供されます。行われたかどうかを確認するには、次の手順を使用してテストします。

選択的レプリケーションのテスト
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

最初にいくつかのテストデータ2つの作品と出席者を作成します。 ``data-a1``
に直接接続して、次のコードを実行します。

.. code:: sql

   INSERT INTO work VALUES (1, Aida, Verdi);
   INSERT INTO work VALUES (2, Lohengrin, Wagner);
   INSERT INTO attendee (email) VALUES (gv@example.com);

これらのテーブルにデータが追加されたため、外部キー制約に違反せずに\ ``opinion``
テーブルに挿入できます。

.. code:: sql

   INSERT INTO opinion (work_id, attendee_id, country, day, score)
   SELECT work.id, attendee.id, Italy, 1871-11-19, 3
     FROM work, attendee
    WHERE work.title = Lohengrin
      AND attendee.email = gv@example.com;

挿入が完了したら、同じノードでデータベースの内容を検証できます。

.. code:: sql

   SELECT a.email
   , o.country
   , o.day
   , w.title
   , w.author
   , o.score
   FROM opinion o
   JOIN work w ON w.id = o.work_id
   JOIN attendee a ON a.id = o.attendee_id;

.. code:: console

       email       | country |    day     |   title   | author | score 
   - ---------------+---------+------------+-----------+--------+-------
    gv@example.com | Italy   | 1871-11-19 | Lohengrin | Wagner |     3
   (1 row)

ここで、ノード\ ``data-a2`` および\ ``data-a3``
に接続し、同じクエリーを実行すると、同じ結果が得られます。データは\ ``region_a``
で複製されています。 ``data-b1`` 、\ ``data-b2`` 、または\ ``data-b3``
に接続する場合、クエリーは行を返しません。これは、 ``attendee``
および\ ``work``
テーブルにデータが入力されていますが、選択する\ ``opinion``
行がないためです。これは、 ``region_a`` での\ ``opinion``
のレプリケーションがそのリージョンでのみ発生するためです。

次に、 ``data-b1`` に接続し、そこに意見を挿入します。

.. code:: sql

   INSERT INTO attendee (email) VALUES (fb@example.com);

   INSERT INTO opinion (work_id, attendee_id, country, day, score)
   SELECT work.id, attendee.id, Germany, 1850-08-27, 9
     FROM work, attendee
    WHERE work.title = Lohengrin
      AND attendee.email = fb@example.com;

この意見は\ ``region_b`` でのみ複製されます。 ``data-b1``
、\ ``data-b2`` 、および\ ``data-b3`` では、次を実行できます。

.. code:: sql

   SELECT a.email
   , o.country
   , o.day
   , w.title
   , w.author
   , o.score
   FROM opinion o
   JOIN work w ON w.id = o.work_id
   JOIN attendee a ON a.id = o.attendee_id;

.. code:: console

       email       | country |    day     |   title   | author | score 
   - ---------------+---------+------------+-----------+--------+-------
    fb@example.com | Germany | 1850-08-27 | Lohengrin | Wagner |     9
   (1 row)

各\ ``region_b`` データノードで同じ結果が表示されます。 ``region_a``
ノードでクエリーを実行すると、この特定のエントリが表示されません。

最後に、 ``attendee``
テーブルがすべてのノードで同様に共有されていることに注意してください。任意のノードで、クエリを実行します。

.. code:: sql

   SELECT * FROM attendee;

.. code:: console

            id         |    email
   - -------------------+----------------
    904252679641903104 | gv@example.com
    904261037006536704 | fb@example.com
   (2 rows)
