Managing DDL with PGD replication
=================================

DDLのインパクトの最小化
-----------------------

DDLの影響を最小限に抑えることは、データベースにとって良い運用上のアドバイスです。
PGDでは、これらの点がさらに重要になります。

-  DDLの影響を最小限にするには、
   DDLを実行するトランザクションを短くし、大量の行変更と組み合わせず、長時間実行される外部キーまたはその他の制約の再チェックを回避します。

-  ``ALTER TABLE`` の場合、 ``ADD CONSTRAINT``
   を単独で使用するのではなく、 ``ADD CONSTRAINT NOT VALID``
   を使用し、その後に\ ``VALIDATE CONSTRAINT``
   との別のトランザクションを使用します。 ``VALIDATE CONSTRAINT``
   は、すべてのノードで再生されるまで待機するため、確認の受信に顕著な遅延が発生します。

-  インデックスを作成するときは、可能な限り\ ``CONCURRENTLY``
   オプションを使用します。

長時間実行DDLを実行する別の方法は、
DDLレプリケーションを無効にして、各ノードで個別にDDLステートメントを実行することです。次の例に示すように、単一のSQLステートメントを使用してこれを行うことができます。グローバルロックルールは引き続き適用されるため、このタイプの使用法で自分自身をロックアウトしないように注意してください。これは回避策です。

.. code:: sql

   SELECT bdr.run_on_all_nodes($ddl$
           CREATE INDEX CONCURRENTLY index_a ON table_a(i);
   $ddl$);

``CREATE INDEX CONCURRENTLY``
はマルチトランザクションコマンドであるため、セッション全体でDDLレプリケーションを無効にする必要があることに注意して、
``CREATE INDEX CONCURRENTLY``
で :ref:`bdr.run_on_all_nodes() <System functions>` テクニックを使用することをお勧めします。実行中の書き込みを防止するため、実稼働システムでは\ ``CREATE INDEX``
を避けてください。 ``REINDEX``
は、3.6までのバージョンでレプリケートされていますが、PGD
3.7以降ではレプリケートされません。 ``REINDEX``
は、AccessExclusiveLocksを保持するため、使用を避けてください。

代わりに、PG12+または2QPG11+で使用可能な\ ``REINDEX CONCURRENTLY``
または\ ``reindexdb --concurrently`` を使用します。

次のようなコマンドラインユーティリティを使用する場合、
DDLレプリケーションを無効にできます。

.. code:: sql

   $ export PGOPTIONS="-c bdr.ddl_replication=off"
   $ pg_restore --section=post-data

複数のDDLステートメントは、個々のステートメントとして起動されるよりも単一のトランザクションに束ねたほうがメリットがある場合があるため、
DDLロックは1回のみを取ります。テーブルレベルのロックが通常の操作を妨げる場合、これは望ましくない場合があります。

DDLがシステムを長時間保持している場合は、 psqlの **Control-C**
または\ ``pg_cancel_backend()``
を使用して、元のノードでDDLを安全にキャンセルできます。他のノードからDDLロックをキャンセルすることはできません。

オプションのグローバルロックタイムアウト設定を使用して、グローバルロックにかかる時間を制御できます。
 :ref:`bdr.global_lock_timeout <bdr.global_lock_timeout>` は、グローバルロックの取得がキャンセルされるまでの待機時間を制限します。
 :ref:`bdr.global_lock_statement_timeout <bdr.global_lock_statement_timeout>` は、グローバルロックを保持するトランザクション内のステートメントの実行タイム長を制限し、
 :ref:``bdr.global_lock_idle_timeout` <`bdr.global_lock_idle_timeout`>` は、グローバルロックを保持するトランザクションの最大許可アイドル時間ステートメント間の時間を設定します。値をゼロに設定することにより、これらのタイムアウトをすべて無効にできます。

元のノードでDDL操作がコミットされると、キャンセルまたは中止することはできません。
PGDグループは、グローバルロックを確認した他のノードに正常に適用され、それらが再生を確認するまで待機する必要があります。このため、
DDLトランザクションは短くて速いままにしてください。

ダウンしたノードでのDDLの処理
-----------------------------

グローバルDDLロックを開始するノードがグローバルロックDDLまたはDMLを取得した後にダウンした場合、ロックはアクティブなままです。タイムアウトが設定されていても、グローバルロックはタイムアウトしません。ノードが復旧した場合、ノードは保持しているすべてのグローバルロックを解放します。

長時間または無期限にダウンしたままになる場合は、PGDグループからノードを削除してグローバルロックを解放します。これが、
bdr_superuserとして\ ``SET`` コマンドを使用して緊急DDLを実行し、
 :ref:`bdr.ddl_locking <bdr.ddl_locking>` 値を更新する理由の1つです。

グローバルロックを確認した後、それを取得するコマンドが実行される前に、他のノードのいずれかがダウンした場合、ノードがアップしているかのように、ロックを要求するそのコマンドの実行は続行されます。

前述のように、グローバルDDLロックはノードの大部分のみが応答する必要があるため、大部分が実行され到達可能である限り、クラスターの一部がダウンしている場合に機能します。ただし、クラスター全体が利用できない限りDMLロックは取得できません。

グローバルDDLまたはグローバルDMLロックでは、別のノードがダウンすると、コマンドは通常に続行され、ロックが解放されます。

ステートメント固有のDDLレプリケーションの問題
---------------------------------------------

すべてのコマンドを自動的に複製できるわけではありません。
 :ref:`bdr.ddl_replication <bdr.ddl_replication>` をオフにしてDDLレプリケーションをオフにしない限り、このようなコマンドは許可されません。

PGDは、データベースでアクティブなときに一部のDDLステートメントの実行を防止します。これにより、正しくレプリケートできないステートメント、またはレプリケーションがまだサポートされていないステートメントを禁止することにより、システムの一貫性が保護されます。

ステートメントがPGDで許可されない場合、多くの場合、同じことを行う別の方法を見つけることができます。たとえば、揮発性のデフォルト値を含む列を追加する\ ``ALTER TABLE``
は実行できません。しかし、一般的には、機能する一連の独立した\ ``ALTER TABLE``
および\ ``UPDATE`` ステートメントとして言い換えることができます。

通常、サポートされていないステートメントは実行されず、
``feature_not_supported`` SQLSTATE ``0A000`` エラーが発生します。

一時オブジェクトを参照または依存するDDLは、PGDでレプリケートできず、
DDLレプリケーションを有効にして実行するとエラーがスローされます。
