Upgrading application schemas
=============================

ローリングアプリケーションスキーマのアップグレードにより、一度に1ノードずつクラスター全体にDDL変更を適用し、クラスター全体で利用可能な状態を維持できます。
PGDは、ノードが一時的に異なるスキーマバージョンにある場合でも、レプリケーションを実行し続けるように設計されていますが、各種類のスキーマ変更には特定の制約と準備要件があります。

一般的な代替案は、マルチステップのアプローチです。新しいNULL可能列などの新しい構造をスキーマに追加するだけを行うDDL変更を展開し、アプリケーションを更新してそれらを設定し、必要に応じて既存の行をバックフィルし、制約の追加などのクリーンアップDDLを適用しますそして列を削除します。このアプローチでは、競合リゾルバーの構成を回避しますが、
DDLとアプリケーションの変更との間の注意深い調整が必要です。

どちらのアプローチとも互換性のないスキーマ変更の場合、代わりにフルダウンタイムのアップグレードにフォールバックします。

ローリングアプリケーションスキーマのアップグレードの準備
--------------------------------------------------------

スキーマを変更する前に、正確なスキーマ変更とそれらが適用される順序についてアプリケーションチームと合意し、各ノードで適切なリゾルバーを構成します。

PGDは、スキーマの不一致を競合タイプとして分類し、競合リゾルバーを使用して各それぞれを処理します。
``bdr.alter_node_set_conflict_resolver``
を使用して、競合タイプごとにリゾルバーの値を構成します。競合タイプとそのリゾルバーの完全なリストについては、
:ref:`bdr_read_all_conflicts <bdr_read_all_conflicts>`  を参照してください。

..  Warning::
   リゾルバー設定は、クラスター全体の永続的な変更です。構成の誤りは、アップグレードに影響を与えるだけではありません。これは持続し、クラスター上の今後のすべてのレプリケーションに影響を与えます。

テーブルの追加
^^^^^^^^^^^^^^

1つのノードが新しいテーブルを追加するDDLを実行すると、
DDLをまだ受け取っていないノードは、持っていないテーブルへの書き込みを処理する必要があります。
PGDは\ ``target_table_missing`` 競合を発生させます。

このタイプのスキーマ変更を実行する前に、\ ``target_table_missing``
競合に\ ``skip`` リゾルバーを適用するようにすべてのノードを構成します。
``skip`` では、
DDLをまだ受け取っていないノードは、エラーを発生させずに、欠落しているテーブルに送られる行をサイレントに破棄します。

``<node_name>``
を実際のノード名に置き換えて、各ノードでこのクエリを個別に実行します。

.. code:: sql

   SELECT bdr.alter_node_set_conflict_resolver(<node_name>, target_table_missing, skip);

..  Warning::
   `skip` リゾルバーは、データの損失とクラスターの発散を発生させる可能性があります。アップグレードが完了した後、クラスター全体に対して `LiveCompare <https://www.enterprisedb.com/docs/livecompare/latest/>`_ を実行して、発散を検出および修正します。

列の追加
^^^^^^^^

1つのノードが列を追加するDDLを実行すると、
DDLをまだ受け取っていないノードは、持っていない列を含む書き込みを処理する必要があります。
PGDは\ ``target_column_missing`` 競合を発生させます。

このタイプのスキーマ変更を実行する前に、\ ``target_column_missing``
競合に\ ``ignore``
リゾルバーを適用するようにすべてのノードを構成します。 ``ignore`` では、
DDLをまだ受け取っていないノードは、エラーを発生させずに、欠落している列の値をドロップします。

``<node_name>``
を実際のノード名に置き換えて、各ノードでこのクエリを個別に実行します。

.. code:: sql

   SELECT bdr.alter_node_set_conflict_resolver(<node_name>, target_column_missing, ignore);

..  Warning::
   `ignore` リゾルバーは、データの損失とクラスターの発散を発生させる可能性があります。アップグレードが完了した後、クラスター全体に対して `LiveCompare <https://www.enterprisedb.com/docs/livecompare/latest/>`_ を実行して、相違を検出および修正します。

列の削除
^^^^^^^^

..  Note::
   ほとんどの場合、列を削除するための好ましいアプローチは、アプリケーションを更新して参照を停止し、更新をすべてのノードに展開して、通常のレプリケートDDLを使用して列をドロップすることです。ここで説明するローリングアプローチは、古いアプリケーションバージョンが一部のノードでまだ実行されているときに列を削除する必要がある場合にのみ適用されます。

1つのノードが列をドロップするDDLを実行すると、
DDLをまだ受け取っていないノードはまだそのデータを送信します。
PGDは\ ``source_column_missing`` 競合を発生させます。

この競合タイプのデフォルトのリゾルバーは\ ``use_default_value``
です。欠損値を列のデフォルトまたは\ ``NULL``
で埋め、レプリケーションを続行します。

列が\ ``NULL``
を受け入れるか、デフォルト値がある限り、アクションは必要ありません。列に\ ``NOT NULL``
制約があり、デフォルト値がない場合、レプリケーションは停止します。列を削除する前に、すべてのノードに\ ``NOT NULL``
制約を削除するか、デフォルト値を追加します。

.. code:: sql

   - - Option 1: remove the NOT NULL constraint
   ALTER TABLE <table_name> ALTER COLUMN <column_name> DROP NOT NULL;

   - - Option 2: add a default value
   ALTER TABLE <table_name> ALTER COLUMN <column_name> SET DEFAULT <default_value>;

..  Note::
   `NOT NULL` 制約の削除は、自分自身DDL変更であり、同じローリングアプローチに従います。スキーマの許容度がより高くなるため、列のドロップを続行する前に、一度に1つのノードを適用するのが安全です。

列タイプの変更
^^^^^^^^^^^^^^

1つのノードが列タイプを変更するDDLを実行すると、
DDLをまだ受け取っていないノードは古いタイプのデータを送信します。

タイプがバイナリ互換である場合、
PGDは基になるテーブルを書き換えずにメタデータの更新として変更を処理し、レプリケーションは続行されます。アクションは必要ありません。
2つのタイプがバイナリ互換性があるかどうかを確認するには、
 
`pg_cast <https://www.postgresql.org/docs/current/catalog-pg-cast.html>`_ を照会します。

.. code:: sql

   SELECT * FROM pg_cast
   WHERE castsource = <old_type>::regtype
     AND casttarget = <new_type>::regtype
     AND castmethod = b;

タイプの変更にテーブルの書き換えが必要な場合、このページで説明する競合リゾルバーのアプローチを使用して実行することはできず、より複雑なマルチステップのプロセスが必要です。変換トリガーは、ターゲットノードで着信データを古いタイプから新しいタイプに変換することにより、これらの場合に役立ちます。
:ref:`変換トリガーを使用してスキーマの違いをブリッジする <変換トリガーを使用してスキーマの違いをブリッジする>` を参照してください。

ローリングアプリケーションスキーマのアップグレードの実行
--------------------------------------------------------

すべてのノードで競合リゾルバーが構成されたら、一度に1つのノードにDDL変更を適用します。各ノードでDDL変更を実行する前に、セッションのDDLレプリケーションを無効にして、変更がローカルにのみ適用され、他のノードにレプリケートされないようにします。

1. 最初のノードに接続し、 DDLレプリケーションを無効にして、
   DDL変更を実行します。

.. code:: sql

       SET bdr.ddl_replication = off;
       -- DDL change here

1. 続行する前に、レプリケーションが他のすべてのノードに追いついたことを確認します。各ノードで、\ ``catchup_interval``
   がクリアされたことを確認します。

.. code:: sql

       SELECT target_name, catchup_interval
       FROM bdr.node_replication_rates;

1. すべてのノードが新しいスキーマになるまで、残りの各ノードで繰り返します。

変換トリガーを使用してスキーマの違いをブリッジする
--------------------------------------------------

適用される前に、変換トリガーを使用して、ターゲットノードの着信行の形状を変更します。データを単に破棄またはデフォルト値で埋めるのではなく、再形成が必要なより複雑なスキーマ変更の場合、変換トリガーが適切なツールです。例には、1つの列を2つに分割すること、または新しく追加された列の派生値を計算することが含まれます。構文、例、実行順序については、
:ref:`Stream triggers <Stream triggers>` を参照してください。

..  Warning::
   変換トリガーが実行するDMLは他のノードにレプリケートされず、ローカルトリガーを起動しないため、サイレントデータの不一貫性が発生しやすくなります。クラスターに展開する前にトリガーロジックを作成してテストするようにアプリケーションチームに依頼し、すべてのノードがアップグレードが完了したらトリガーを削除します。
