Conflicts

BDRは、アクティブ/アクティブまたはマルチマスターのDBMSです。非同期で使用すると、マルチプルの異なるノードから同じ行または関連する行に書き込むと、標準データ型を使用しているときにデータの競合が発生する可能性があります。

競合はエラーではありません。ほとんどの場合、これらはBDRが発生時に検出および解決できるイベントです。解決策はアプリケーションの性質とデータの意味によって異なるため、 BDRがアプリケーションに競合を解決する方法に関するレンジな選択肢を提供することが重要です。

デフォルトでは、競合行レベルで解決されます。 2つのノードからの変更が競合する場合、ローカルまたはリモートのタプルが選択され、もう一方は破棄されます。例、2つの競合する変更のコミットのタイムスタンプを比較し、新しい方を保持します。このアプローチにより、すべてのノードが同じ結果に収束し、クラスター全体でコミットオーダーのようなセマンティクスが確立されます。

列レベルの競合解決の有効化と無効化 で説明されているように、競合ハンドリングは構成可能です。

Stream Triggers で説明されている競合トリガーを使用して、テーブルごとに競合を検出して処理できます。

CLCD で説明されているように、列レベルの競合検出と解決はBDRで使用できます。

競合を回避したい場合は、 BDRでこれらの機能を使用できます。

デフォルトでは、すべての競合はbdr.conflict_history に記録されます。競合が発生する可能性がある場合、テーブルの所有者は競合をモニタして回避する方法を分析するか、アプリケーションタスクとして定期的にハンドルする計画をmake必要があります。

LiveCompareによるデータ検証 ツールを使用して、発散を定期的にスキャンすることもできます。

一部のクラスタリングシステムは、分散ロックメカニズムを使用してデータへの同時アクセスを防ぎます。これらは、サーバーが互いに非常に近い場合は合理的に実行できますが、許容可能なパフォーマンスのために非常に低いレイテンシが重要である地理的に分散したアプリケーションをサポートできません。

分散ロックは本質的に悲観的なアプローチです。 BDRは、可能な場合は競合を回避しますが、特定の種類の競合の発生を許可し、発生時に解決するという楽観的なアプローチを提唱しています。

競合はどのように発生するか

ノード間の競合は、関連するすべてのトランザクションが同じノードで同時に発生する場合に発生しないイベントの結果として発生します。ノードはトランザクションがコミットした後にのみ変更を交換するため、各トランザクションはコミットされたノードで個別に有効です。他の競合する作業を同時に行った別のノードに適用された場合、無効です。

BDRレプリケーションは本質的に他のノードでトランザクションをリプレイするため、適用されるトランザクションと受信ノードでコミットされたトランザクションの間に競合がある場合、リプレイオペレーションが失敗する可能性があります。

Postgresには、 UNIQUE インデックス、 SEQUENCE 操作、行とリレーションのロック、 SERIALIZABLE 依存関係の追跡など、それを防ぐためのトランザクション間通信メカニズムがあるため、すべてのトランザクションが単一のノードで実行されている場合、ほとんどの競合は発生しません。これらのメカニズムはすべて、進行中のトランザクション間で通信して、望ましくない同時実行の問題を防ぐ方法です。

BDRには分散トランザクションマネージャまたはロックマネージャがありません。これが、レイテンシとネットワークパーティションで優れたパフォーマンスを発揮する理由のパートです。その結果、デフォルトの遅延レプリケーションを使用すると、異なるノード上のトランザクションが互いに完全に独立して実行されます。ノード間の独立性が低いと競合を完全に回避できます。そのため、 BDRはこれが重要な場合にEager Replicationも提供します。

競合の種類

PRIMARY KEYまたはUNIQUEの競合

最も一般的な競合行の競合です。2 つの操作が、単一のノードではできない方法で同じキーの行に影響します。 BDRはそれらのほとんどを検出でき、 update_if_newer 競合リゾルバを適用します。

行の競合には次のものが含まれます。

  • INSERT とINSERT

  • UPDATE とUPDATE

  • UPDATE とDELETE

  • INSERT とUPDATE

  • INSERT とDELETE

  • DELETE とDELETE

ビューbdr.node_conflict_resolvers は、既知のすべての競合タイプの競合解決が現在どのように構成されているかに関する情報を提供します。

INSERT / INSERTの競合

最も一般的な競合 INSERT /INSERT は、2つの異なるノードでのINSERT 操作が同じPRIMARY KEY 値(または、 PRIMARY KEY が存在しない場合は単一のUNIQUE 制約)を持つタプルを作成する場合に発生します。

BDRは、この動作がユーザ定義の競合ハンドラによってオーバーライドされない限り、発信元ノードのタイムスタンプに従って、2つのうちの最後に挿入されたタプルを保持することにより、このシチュエーションを処理します。

この競合は insert_exists 競合タイプを生成します。これは、デフォルトでは、より新しい(コミット時間に基づいて)行を選択し、その行のみを保持することで解決されます( update_if_newer リゾルバ)。他のリゾルバーを構成できます。詳細は 列レベルの競合解決の有効化と無効化 を参照してください。

この競合の種類を解決するには、列レベルの競合解決とユーザー定義の競合トリガーを使用することもできます。

BDRグローバルシーケンス を使用すると、この種の競合を効果的に解消できます。

マルチプルのUNIQUEコンストレインに違反するINSERT操作

INSERT /INSERT の競合は、複数のUNIQUE 制約に違反する場合があります(そのうちの1つがPRIMARY KEY である場合があります)。新しい行が複数のUNIQUE 制約に違反し、他の複数の行に対して競合が発生する場合、レプリケーション変更を適用するとmultiple_unique_conflicts 競合が発生します。

このような競合が発生した場合、レプリケーションを続行するにはいくつかの行を削除する必要があります。 multiple_unique_conflicts のリゾルバ設定に応じて、適用プロセスはエラーで終了するか、入力行をスキップするか、一部の行を削除します。削除では、正しい PRIMARY KEY で行を保持し、他の行を削除しようとします。

警告

この方法でマルチプルの行が競合している場合、競合解決の結果が挿入オペレーションを続行する場合、一部のデータは常に削除されます。

競合トリガーを使用して別の動作を定義することもできます。

UPDATE/UPDATEの競合

異なるノードでの2つの同時のUPDATE 操作が同じタプル(ただし、そのPRIMARY KEY ではない)を変更する場合、 UPDATE / リプレイの競合が発生する可能性があります。

これらは、構成とシチュエーションに基づいてさまざまな種類の競合を生成できます。テーブルが 行バージョンの競合検出 で構成されている場合、オリジナルの(キー)行がローカル行と比較されます。それらが異なる場合、 update_differing 競合が生成されます。 オリジンの競合検出を使用する場合、行のオリジンがチェックされます(オリジンは現在のローカル行の元のノードです)。それが変更された場合、 update_origin_change 競合が生成されます。他のすべての場合、通常は競合を生成せずにUPDATE が適用されます。

これらの競合は両方とも、 INSERT / INSERTの競合 で説明されている insert_exists と同じ方法で解決されます。

PRIMARY KEYでのUPDATEの競合

BDRは現在、 PRIMARY KEY がUPDATE オペレーションによって変更された競合解決を実行できません。プライマリキーは更新できますが、既存の値との競合が発生しないことを保証必要があります。

プライマリキーの更新の競合は 発散する競合 であり、マニュアル介入が必要です。

Postgresでプライマリキーを更新できますが、 PostgresとBDRの両方に問題があります。

シンプルスキーマは、次のことを説明する例を提供します。

CREATE TABLE pktest (pk integer primary key, val integer);
INSERT INTO pktest VALUES (1,1);

主キー列を更新できるため、次のSQLは成功します。

UPDATE pktest SET pk=2 WHERE pk=1;

ただし、テーブルにマルチプルの行があるとします。

INSERT INTO pktest VALUES (3,3);

一部の UPDATE は成功します。

UPDATE pktest SET pk=4 WHERE pk=3;

SELECT * FROM pktest;
 pk | val
- ---+-----
  2 |   1
  4 |   3
(2 rows)

他の UPDATE は制約エラーで失敗します。

UPDATE pktest SET pk=4 WHERE pk=2;
ERROR:  duplicate key value violates unique constraint "pktest_pkey"
DETAIL:  Key (pk)=(4) already exists

したがって、プライマリキーを更新するPostgresアプリケーションの場合、 BDRがなくてもランタイムエラーを回避するように注意してください。

BDRでは、同時にマルチプルの場所からのUPDATEが許可されている場合、シチュエーションはより複雑になります。

これら2つの変更を同時に実行すると機能します。

node1: UPDATE pktest SET pk=pk+1 WHERE pk = 2;
node2: UPDATE pktest SET pk=pk+1 WHERE pk = 4;

SELECT * FROM pktest;
 pk | val
- ---+-----
  3 |   1
  5 |   3
(2 rows)

これらの次の2つの変更を同時に実行すると、両方の変更が受け入れられるため、発散エラーが発生します。ただし、他のノードに変更を適用すると、 update_missing 競合が発生します。

node1: UPDATE pktest SET pk=1 WHERE pk = 3;
node2: UPDATE pktest SET pk=2 WHERE pk = 3;

このシナリオでは、各ノードでデータが異なります。

  node1:
  SELECT * FROM pktest;
   pk | val
  - ---+-----
    1 |   1
    5 |   3
  (2 rows)

  node2:
  SELECT * FROM pktest;
   pk | val
  - ---+-----
    2 |   1
    5 |   3
  (2 rows)

:ref:`LiveCompareによるデータ検証<LiveCompareによるデータ検証>`  を使用して、このシチュエーションを特定して解決できます。

競合が同時に発生すると問題が発生します。これら2つの変更を同時に実行するのは簡単ではありません。

node1: UPDATE pktest SET pk=6, val=8 WHERE pk = 5;
node2: UPDATE pktest SET pk=6, val=9 WHERE pk = 5;

両方の変更がローカルに適用されるため、ノード間で相違が発生します。ただし、ターゲットでの適用は、重複キー値違反エラーで両方のノードで失敗します。これにより、レプリケーションが停止し、マニュアル解決が必要になります。

この重複キー違反エラーを回避できるようになり、conflict_type update_pkey_exists をskip 、update 、またはupdate_if_newer に設定してもレプリケーションは中断されません。これは、更新の性質によっては依然として発散につながる可能性があります。

update_pkey_exists をupdate_if_newer に設定することで、同じ古いキーが同じ新しいキーによって同時に更新されている場合の発散を回避できます。ただし、特定の状況では、 update_if_newer でも発散が発生します。つまり、2つの異なる行が両方とも同じ新しいプライマリキーに更新されます。

その結果、アプリケーション、特にBDRでプライマリキーの UPDATE 操作を許可しないことを強くお勧めします。アプリケーションの一部がプライマリキーを変更する場合、同時変更を回避するには、Eager Replicationを使用しmakeます。

警告

update_pkey_exists conflictの競合解決が更新の場合、常に行の1つが削除されます。

マルチプルのUNIQUEコンストレインに違反するUPDATE操作

マルチプルのUNIQUEコンストレインに違反するINSERT操作 と同様に、着信 UPDATE が複数のUNIQUE

インデックス(またはPRIMARY KEY )に違反すると、 BDRはmultiple_unique_conflicts 競合を発生させます。

BDRは、遅延の一意のコンストレインをサポートしています。トランザクションがソースでコミットできる場合、競合が見つからない限り、ターゲットに正常に適用されます。ただし、遅延プライマリキーはREPLICA IDENTITYとして使用できないため、ユースケースはそれとマルチプルの一意のコンストレインの使用に関するワーニングによって既に制限されています。

UPDATE/DELETEの競合

あるノードが行を更新し、別のノードが同時に削除する可能性があります。この場合、再生時に UPDATE / リプレイの競合が発生する可能性があります。

削除され行がまだ検出可能な場合(削除された行はVACUUM によって削除されませんでした)、 update_recently_deleted の競合が生成されます。デフォルトでは UPDATE はスキップされたされますが、この解像度を構成できます。詳細は 列レベルの競合解決の有効化と無効化 を参照してください。

ローカルノードのレプリケーションが遅れている場合、削除され行は UPDATE を受け取るまでにデータベースからクリーンアップできます。この場合、 BDRはUPDATE /DELETE の競合と INSERT / UPDATEの競合 を区別できず、update_missing の競合を生成します。

競合するDELETE とUPDATE の別のタイプは、行がローカルで更新された後のDELETE です。このシチュエーション、結果は使用される競合検出の種類によって異なります。デフォルトの

オリジンの競合検出 を使用すると、競合はまったく検出されないため、 DELETE

が適用され、行が削除されます。 行バージョンの競合検出 を有効にすると、 delete_recently_updated の競合が生成されます。この競合タイプのデフォルトの解決策は、 DELETE を適用して行を削除することですが、これを構成するか、競合トリガーで処理できます。

INSERT / UPDATEの競合

デフォルトの非同期オペレーションモードを使用する場合、ノードはオリジナルのINSERT を受け取る前に行のUPDATE を受け取る場合があります。これは、3つ以上のノードがアクティブな場合にのみ発生します(

これが発生すると、 update_missing 競合が生成されます。デフォルトの競合リゾルバはinsert_or_skip ですが、代わりにinsert_or_error またはskip を使用できます。挿入またはアクションを行うリゾルバーは、可能な場合(行全体を受け取ったとき)、最初にUPDATE からのデータに基づいて新しい行をINSERT しようとします。行の再構築を可能にするには、テーブルにニーズが含まれているか、行にトーストされたデータが含まれていない必要があります。

トーストされたデータの詳細については、 TOASTサポート詳細 を参照してください。

INSERT/DELETEの競合

INSERT /UPDATE の競合と同様に、ノードは、まだINSERT を受け取っていない行でDELETE オペレーションを受け取る場合があります。これも3つ以上のノードが設定されている場合にのみ可能です(

BDRは現在、この競合タイプを検出できません。 INSERT オペレーションは競合タイプを生成せず、 INSERT が適用されます。

DELETE オペレーションは常にdelete_missing の競合を生成します。デフォルトでは、オペレーションをスキップすることで解決されます。

DELETE/DELETEの競合

2つの異なるノードが同じタプルを同時に削除すると、 DELETE /DELETE の競合が発生します。

これにより、常にdelete_missing の競合が生成されます。デフォルトでは、オペレーションをスキップすることで解決されます。

両方の DELETE 操作は同じ効果があるため、この競合は無害です。そのうちの 1 つは無視しても問題ありません。

3つ以上のノードとの競合

1つのノードが2番目のノードに再生されてそこで更新される行を挿入する場合、3番目のノードは最初のノードからINSERT を受信する前に2番目のノードからUPDATE を受信できます。このシナリオは INSERT /UPDATE の競合です。

これらの競合は、 UPDATE .これにより、異なるノードに異なるデータが表示される可能性があります。これらは

この競合タイプは、3つ以上のマスターでのみ発生する可能性があり、そのうち少なくとも2つがアクティブに書き込んでいる必要があります。

また、ノード1からノード3へのレプリケーションラグは、次のシーケンスのアクションを許可するのに十分な大きさである必要があります。

1.ノード2はノード1からINSERTを受け取ります

2.ノード2がUPDATEを実行する

3.ノード3はノード2からUPDATEを受信します

4.ノード3はノード1からINSERTを受け取ります

insert_or_error (または場合によっては update_missing 競合タイプのinsert_or_skip 競合リゾルバ)を使用することが、これらの競合の実行可能な軽減戦略です。ただし、このオプションを有効にすると、 INSERT /DELETE の競合の扉が開きます。

1.ノード1がUPDATEを実行する

2.ノード2はDELETEを実行します

3.ノード3はノード2からDELETEを受信します

4.ノード3はノード1からUPDATEを受け取り、それをINSERT

これらが問題になる場合は、テーブルまたはデータベースのフリーズ設定をチューニングして、update_recently_deleted として正しく検出されるようにすることをお勧めします。

別の方法は、 Eager Replication を使用してこれらの競合を防ぐことです。

INSERT /DELETE の競合は、3つ以上のノードでも発生する可能性があります。このような競合は、 UPDATE がDELETE に置き換えられていることを除き、 INSERT /UPDATE と同じです。これにより、 delete_missing の競合が発生する可能性があります。

BDRは、 update_missing の競合で発生するmakeに、各INSERTを最近削除したかどうかを確認することを選択できます。ただし、これを行うと大部分のユーザーがペナルティを受けるため、現時点では単にコストをログに記録します。

後のリリースでは、 delete_missing の競合が発生した場合、

LiveCompareによるデータ検証 を使用した再チェックを介してINSERT /DELETE

の異常を自動的に解決します。これらは、 bdr.conflict_history_summary ビューを確認することにより、アプリケーションで手動で実行できます。

これらの競合は、2つの主な問題のユースケースで発生する可能性があります。

  • キューイングアプリケーションで使用できるように、INSERT の後にすぐにDELETE が続く

・ テーブルのプライマリキー識別子を再利用する場合

これらのケースはいずれも一般的ではありません。これらの問題のユースケースが発生した場合は、影響を受けるテーブルを複製しないことをお勧めします。

BDRは識別子の一意性に依存してレプリケーションを機能さmakeため、後者の場合に問題がBDRます。

同じ一意の識別子を挿入、削除、および後で再利用するアプリケーションは、問題を引き起こす可能性があります。これは

ABA problem と呼ばれます。

BDRには、行が現在の行、最後の行、またはそれよりも古い行であるかを知る方法がありません。

一意の識別子の再利用もビジネスの問題です。アプリケーションは一意の識別子を再利用する必要はありません。

変更がシステムを通過するまでにかかる時間内に識別子が再利用されると、問題が発生しインターバル。通常のオペレーションではその時間は短い場合がありますが、ノードがダウンすると、そのインターバルが数時間または数日に延長される場合があります。

アプリケーションで一意の識別子を再利用しないことをお勧めしますが、使用する場合は、1 年より小さいに再利用しないようにするための措置を講じます。

この問題は、シーケンスまたは UUID を使用するアプリケーションでは発生しません。

外部キー制約の競合

適用されるリモートトランザクションと既存のローカルデータ間の競合は、 FOREIGN KEY (FK)コンストレインでも発生する可能性があります。

BDRはsession_replication_role = 'replica' で変更を適用するため、変更を適用するときに外部キーが再チェックされません。アクティブ/アクティブ環境では、参照元のテーブルへの挿入と同時に被参照のテーブルが削除されると、FK違反が発生する可能性があります。これは、 INSERT /DELETE の競合に似ています。

シングルマスターのPostgresでは、被参照する INSERT /UPDATE は、行レベルのロックを取得する前に DELETE 操作が終了する必要があります。 DELETE が被参照た値を削除する場合、 INSERT /UPDATE はFKチェックに失敗します。

マルチマスターBDR。ノード間の行レベルロックはありません。参照元のテーブルのINSERT は、参照のテーブルのDELETE の背後で待機しないため、両方のアクションを同時に実行できます。したがって、参照元のテーブルのあるノードのINSERT /UPDATE は、別のノードの被参照のテーブルのDELETE と同時に値を使用できます。これにより、参照元テーブルに値がなくなり被参照。

実際には、これは、参照するテーブルでのDELETE 操作とは別のトランザクションで、被参照されるテーブルでDELETE 操作が発生する場合に発生します。これは一般的なオペレーションではありません。

Orders -> OrderItemsなどの親子リレーションでは、これを行うことは一般的ではありません。 OrderItemを完全に削除するよりもキャンセル済みとしてマークする可能性が高くなります。リファレンス/ルックアップデータの場合、新しいファクトデータに同じ値を使用すると同時にエントリを完全に削除することはまれです。

FKがダングリングされる可能性はありますが、一般的にこれのリスクは非常に低いため、 BDRはこの場合をカバーする汎用ソリューションを課しません。これが発生するシチュエーションを理解したら、2つのソリューションが考えられます。

最初の解決策は、FKの使用を、一般に一度に1つのノードからのみ変更される、頻繁に変更される、または変更の同時実行がアプリケーションによって行われる密接に関連するエンティティに制限することです。これにより、アプリケーションレベルでのFK違反が回避されます。

2番目の解決策は、 BDRが提供する関数bdr.ri_fkey_trigger() およびbdr.ri_fkey_on_del_trigger() を使用して、このケースから保護するトリガーを追加することです。 BEFORE トリガーとして呼び出されると、これらの関数は FOREIGN KEY 情報を使用して、 SET NULL制約があるかのように参照列をNULLに設定することにより、FKの異常を回避します。これにより、1つのトリガー内のすべてのFKが再チェックされるため、FK違反を防ぐためにテーブルごとに1つのトリガーのみを追加する必要があります。

例として、FactとRefDataの2つのテーブルがあるとします。ファクトには、RefDataを参照するFKがあります。 Factは参照元のテーブルであり、 RefDataは被参照のテーブルです。各テーブルに1つのトリガーを追加する必要があります。

RefDataで被参照された行が既に削除されている場合、Factに列をNULLに設定するトリガーを追加します。

CREATE TRIGGER bdr_replica_fk_iu_trg
    BEFORE INSERT OR UPDATE ON fact
    FOR EACH ROW
    EXECUTE PROCEDURE bdr.ri_fkey_trigger();

ALTER TABLE fact
    ENABLE REPLICA TRIGGER bdr_replica_fk_iu_trg;

RefDataテーブルでDELETEが発生したときにFactに列をNULLに設定するトリガーを追加します。

CREATE TRIGGER bdr_replica_fk_d_trg
    BEFORE DELETE ON refdata
    FOR EACH ROW
    EXECUTE PROCEDURE bdr.ri_fkey_on_del_trigger();

ALTER TABLE refdata
    ENABLE REPLICA TRIGGER bdr_replica_fk_d_trg;

両方のトリガーを追加すると、外部キーのダングリングが回避されます。

TRUNCATEの競合

TRUNCATE は、すべての行のDELETE と同様に動作しますが、行ごとの削除ではなく、テーブルデータを物理的に削除することによってこのアクションを実行します。その結果、行レベルの競合ハンドリングを利用できないため、明確な競合がある場合でも、 TRUNCATE コマンドは他のDMLアクションとの競合を生成しません。

その結果、TRUNCATE に対して他のノードで別のDMLが同時に実行された場合、リプレイの順序付けによって異なる変更が発生する可能性があります。

次のいずれかのアクションを実行できます。

  • TRUNCATE が他の同時DMLと一緒に実行されないことを確認します。

    LiveCompareによるデータ検証 を利用して、このような矛盾を強調してください。

  • TRUNCATE を、 WHERE 句のないDELETE ステートメントに置き換えます。このアプローチは、大きなテーブルではパフォーマンスが非常に低下する可能性があります。

  • bdr.truncate_locking = 'on' を設定して、 TRUNCATE コマンドのロック動作を設定します。この設定は、 TRUNCATE がbdr.ddl_locking 設定に従うかどうかを決定します。すべてのノードが起動している必要があるため、これはTRUNCATE のデフォルトの動作ではありません。この構成は、すべての場合に可能であるとは限りません。

除外制約の競合

BDRは除外コンストレインをサポートしていないため、作成できません。

既存のスタンドアロンデータベースをBDRデータベースに変換する場合、すべての除外コンストレインを手動で削除します。

分散非同期システムでは、異なるノード上のすべてのトランザクションが完全に分離されるため、制約に違反する行のセットが存在しないことを保証できません。除外コンストレインは、排他制約違反のためにリプレイを任意のノードから他のノードに進めることができないリプレイデッドロックにつながります。

BDRに排他制約を作成させる場合、またはスタンドアロンデータベースをBDRに変換するときに既存の制約を削除しない場合、レプリケーションが中断されることを想定してください。再び進行させるには、着信リモートタプルが競合するローカルタプルを削除または変更して、リモートトランザクションを適用できるようにします。

ロールとテーブルスペースの違いによるデータの競合

競合は、ノードがロールなどの異なるグローバル(Postgresシステム全体の)データを持つ場合にも発生します。これにより、主にDDL の操作が正常に実行されて1つのノードでコミットされますが、他のノードに適用できません。

例、node1にはfred名前付けのユーザがあり、そのユーザはnode2で作成されなかったとします。 node1のfredがテーブルを作成する場合、テーブルは所有者がfredに設定されて複製されます。 DDLコマンドをnode2に適用すると、fred名前付けのユーザがないため、 DDLは失敗します。この失敗により、 Postgresログにエラーが出力されます。

BDRが実行されているデータベースにユーザfredを作成して、この競合を解決するには、管理者の介入が必要です。 bdr.role_replication = on を設定して、将来これを解決できます。

ロックの競合とデッドロックの中止

BDRライタプロセスは通常のユーザセッションとほとんど同じように動作するため、行とテーブルのロックに関する通常のルールにサブジェクトます。これにより、 BDRライタプロセスがユーザトランザクションまたは相互に保持されているロックで待機する場合があります。

関連するロックには次のものが含まれます。

  • ユーザセッションによる明示的なテーブルレベルロック(LOCK TABLE ... )

  • ユーザセッションによる明示的な行レベルロック(SELECT ... FOR UPDATE/FOR SHARE )

  • ローカルアクティビティまたは他のノードからのレプリケーションからの行UPDATE 、INSERT 、またはDELETE 操作による暗黙的なロック

BDRライタプロセスは、ユーザトランザクションでデッドロックする可能性があります。その場合、ユーザトランザクションはライタプロセスが保持するロックを待機しています。 2 つのライタプロセスが互いにデッドロックする場合もあります。 Postgresのデッドロック検出器が介入して、問題のあるトランザクションの1つを終了します。 BDRライタプロセスが終了した場合、再試行しますが、通常は成功します。

これらの問題はすべて一時的なものであり、通常は管理者のアクションは必要ありません。ライタプロセスがアイドルユーザセッションのロックで長時間スタックしている場合、管理者はユーザセッションを終了してレプリケーションを再度実行できますが、これは、別のユーザセッションに影響を与える長いロックを保持しているユーザと違いはありません。

Postgresの log_lock_waits 機能を使用すると、ロック関連のリプレイストールを特定できヘルプ。

発散する競合

異なるノードで同じであるはずのデータが予期せず異なる場合、発散競合が発生します。発散する競合は発生しないはずですが、執筆時点でこのような競合のすべてを確実に防止できるわけではありません。

行の PRIMARY KEY を変更すると、すべてのノードが変更を再生する前に別のノードが同じ行のキーを変更した場合、発散の競合が発生する可能性があります。プライマリキーを変更しないか、指定された1つのノードでのみ変更します。

行データが関係する発散コンフリクトでは、通常、一方のノードのデータを他のノードと一貫性を保つように手動で調整する管理者のアクションが必要です。ドキュメントのとおりにBDRを使用し、安全でないとマークされた設定または機能を回避する限り、このような競合は発生しません。

管理者は、このような競合を手動で解決する必要があります。競合の性質によっては、 bdr.ddl_replication や bdr.ddl_locking などの高度なオプションの使用が必要になる場合があります。ただし、これらのオプションを不注意に使用makeと、状況がさらに悪化し、一般的な指示では対処できない競合が発生する可能性があります。

TOASTサポート詳細

Postgresは、 TOASTサポート詳細 と呼ばれる大きな列にアウトオブラインストレージを使用します。

ロジカルデコーディング( BDRがその上に構築されています)およびロジカルレプリケーションでハンドリングするTOAST値は、テーブルのメイン行のパートとして保存されるインラインデータとは異なります。

TOAST値は、値が変更された場合にのみトランザクションログ(WAL)にログオン。これは、特に UPDATE の競合をハンドリングするときに問題を引き起こす可能性があります。トーストされた列の値を変更しなかった UPDATE ステートメントはその列のない行を生成するためです。

INSERT / UPDATEの競合 で述べたように、 update_missing の競合が

insert_or_error を使用して解決され、 TOAST列が欠落している場合、 BDRはエラーを報告します。

ただし、非同期レプリケーションを使用した同時ワークロードの場合、これよりも微妙な問題があります (Eager トランザクションは影響を受けません)。例、A、B、およびCという3つのノードを持つBDRクラスターでの次のワークロードを想像してください。

1.ノードAで: txn A1は UPDATE SET col1 = ’ トースト data…’を実行し、最初にコミットします。

2.ノードBで: txn B1はUPDATE SET other_column = ‘anything else’; A1の後にコミットします。

3.ノードC:ノードAへの接続が遅れています。

4.ノードC: txn B1が最初に適用され、col1のTOASTされた列を見逃しますが、競合せずに適用されます。

5.ノードC: txn A1が(update_origin_changeで)競合するため、スキップされたされます。

6.ノードCはA1からのトーストされたデータを永遠に逃します。

BDRを使用する場合、このシナリオは通常問題になりません。 (ビルトインロジカルレプリケーションまたはマルチマスターのプレーンロギングを使用する場合です。) BDRは、最近TOAST列の変更をTOASTした行へのローカルUPDATE を検出し、ローカルUPDATE ’ t TOASTを変更する。したがって、 BDRは、異なるノード間でトーストされたデータの不一致を防ぎます。このシチュエーションにより、マルチプルのノードで更新が発生した場合(つまり、タプルのオリジンが変更された場合)、WALロギングが増加します。 BDR AlwaysOnアーキテクチャの場合、通常のように、すべての更新が単一のノードから行われた場合、追加のWALオーバーヘッドはゼロです。

注釈

メインテーブルで同じことをせずにTOASTテーブルだけで`VACUUM FULL` または`CLUSTER` を実行すると、追加のロギングが機能するために必要なメタデータが削除されます。これは、そのようなステートメントの後の短期間、これらの同時実行の問題に対する保護が存在しないことを意味します。

警告

TOASTの追加のWALロギングは、標準のPostgresで BEFORE UPDATE トリガーを使用して実行されます。このトリガーは、テーブル上のすべての`BEFORE UPDATE` トリガーの中で( トリガー名前に基づいて)アルファベット順にソートする必要があります。これを簡単にmakemakeに zzzz_bdr_ プレフィックスが付いていますが、その後にソートされる名前でトリガーを作成しないでください。そうしないと、同時実行の問題に対する保護が得られません。

ただし、 insert_or_error の競合解決には、 REPLICA IDENTITY FULL の使用が必要です。

トーストされた列に関連するこれらの問題は、REPLICA IDENTITY FULL を持つテーブルには影響しません。この設定では、行全体がキーのパートと見なされるため、トーストされた値が常にキーのパートとしてログに記録されキー。 BDRは、キー行から欠落しているデータを埋めて、新しい行を再構築できます。その結果、 REPLICA IDENTITY FULL を使用すると、WALサイズが大幅に増加する可能性があります。

競合の回避または許容

ほとんどの場合、競合を回避または許容するようにアプリケーションをデザインできます。

競合は、マルチプルのノードで同時に発生している場合にのみ発生します。競合を回避する最も簡単な方法は、1つのノードにのみ書き込むか、一度に1つの特定の行から特定の方法でのみ書き込むノードです。

これは、多くのアプリケーションで自然に発生します。例、多くのコンシューマアプリケーションでは、アカウントのデフォルトの請求先住所の変更など、所有ユーザのみがデータを変更できます。このようなデータ変更で更新の競合が発生することはほとんどありません。

ノードがダウンする直前に変更をmake可能性があるため、変更が失われたようです。次に、同じ変更を再度makeと、異なるノードで2つの更新が発生する可能性があります。ダウンしたノードが復旧すると、古い変更を他のノードに送信しようとしますが、データの最後の更新が保持されるため拒否されます。

INSERT /INSERT の競合の場合、

BDRグローバルシーケンス を使用してこのタイプの競合を防ぎます。

部屋予約アプリケーションなど、オブジェクト間の関係を割り当てるアプリケーションの場合、 update_if_newer を適用しても許容できるビジネス結果が得られない場合があります。つまり、2人に同じ部屋を予約したことを個別に確認することは役に立ちません。最も簡単な解決策は、Eager Replicationを使用して、1つの予約のみが成功するように保証ことです。アプリケーションによっては、より複雑な方法も可能です。例、各ノードに100のシートを割り当て、そのノードのライタがそれらを予約できるようにします。ただし、ローカルで利用できるものがない場合は、ほとんどのシートが予約たら、分散ロックスキームまたはEager Replicationを使用します。

特定の種類の更新が1つの特定のノードからのみ行われるようにする別の手法は、さまざまな種類のトランザクションをさまざまなノードにルーティングすることです。例:

  • 1つのノードで小包を受け取りますが、別のノードを使用して小包を配達します

  • 1つのノードで注文が入力され、2番目のノードで顧客に提供されるサービスアプリケーション

多くの場合、最善の方法は、競合の発生を許可し、BDRの競合解決メカニズムと連携して競合に対処するようにアプリケーションをデザインすることです。

競合検出

BDRは、競合検出に次のメカニズムを提供します。

オリジンの競合検出

オリジンの競合検出は、トランザクションの発生元のノードに記録されたコミットタイムスタンプを使用および依存します。これには、クロックが正しく動作するか、2つのノード間の最速メッセージの許容範囲内にある必要があります。そうでない場合、競合の解決はさらに先のノードを優先する傾向があります。パラメーターbdr.maximum_clock_skew およびbdr.maximum_clock_skew_action を使用して、ノード間のクロックスキューを管理できます。

行の起点は、track_commit_timestamp = on の場合にのみ使用できます。

競合は、レプリケーションオリジンが変更されたかどうかに基づいて最初に検出されるため、競合ではないことが判明した状況で競合トリガーが呼び出されます。したがって、このメカニズムは誤検出の競合を生成する可能性があるため、正確ではありません。

起点情報は、行がフリーズするまでのみ利用できます。フリーズされた後に行に到着した更新は競合を発生させないため、すべての場合に適用されます。これは、 bdr_init_physical で新しいノードを追加する場合の通常の場合であるため、その場合に競合が発生すると、多くの誤検出の結果が発生します。

オフラインだったノードが再接続してデータ変更の送信を開始すると、新しく到着した更新が更新するフリーズされた行よりも古い場合、これにより分岐エラーが発生する可能性があります。挿入と削除はこのシチュエーションの影響を受けません。

で説明されているように、拡張停止のためにノードをダウンしたままにしないことをお勧めします。

EDB Postgres Extended ServerおよびEDB Postgres Advanced Serverでは、ノードがダウンしている間の行のフリーズをBDRします。このメカニズムはこのシチュエーションを適切に処理するため、パラメータ設定を変更する必要はありません。

Postgresの他のバリアントでは、このシチュエーションを注意して管理する必要がある場合があります。

フリーズは、通常、バキュームされる行が現在のxidのvacuum_freeze_min_age xidより古い場合に発生します。これは、これらのパラメーターに適切に高い値を構成する必要があることを意味します。

  • vacuum_freeze_min_age

  • vacuum_freeze_table_age

  • autovacuum_freeze_max_age

トランザクションレートに基づいて値を選択し、データベースノードから競合データを削除するまでのダウンタイムを許可します。例、 vacuum_freeze_min_age が5億に設定されている場合、1000 TPSを実行するノードは、競合データが削除されるまでに5.5日以上ダウンする可能性があります。 CommitTSデータ構造は、その設定で5 ギガバイトのディスク上スペースを使用するため、トランザクションレートが低いシステムでは、設定を低くすることでメリットを得られます。

最初に推奨される設定は次のとおりです。

#  1 billion = 10GB
autovacuum_freeze_max_age = 1000000000

vacuum_freeze_min_age = 500000000

#  90% of autovacuum_freeze_max_age
vacuum_freeze_table_age = 900000000

次のことに注意してください。

  • autovacuum_freeze_max_age はノードのスタートにのみ設定できます。

  • vacuum_freeze_min_age を設定できるため、低い値を使用すると行が早期にフリーズされ、競合が無視される場合があります。個々のテーブルにautovacuum_freeze_min_age とtoast.autovacuum_freeze_min_age を設定することもできます。

  • CLUSTERまたはVACUUM FREEZEコマンドを実行すると、行が早期にフリーズされるため、競合が無視される場合があります。

行バージョンの競合検出

または、 BDRは、行のバージョン管理を使用し、ノードのシステムクロックとは無関係に競合検出をmakeオプションを提供します。

行バージョンの競合検出では、3つのことを有効にする必要があります。これらの手順のいずれかが正しく実行されない場合、

オリジンの競合検出 が使用されます。

  1. BDRノードグループでcheck_full_tuple を有効にする必要があります。

2.行バージョンの競合検出を使用するすべてのテーブルでREPLICA IDENTITY FULL を有効にする必要があります。

  1. bdr.alter_table_conflict_detection を使用して、テーブルで行バージョンの追跡を有効にする必要があります。このファンクションは、列(指定した名前)と、新しい列値を管理するUPDATE トリガーを追加します。列はINTEGER タイプとして作成されます。

カウンタはUPDATE でのみインクリメントされますが、この手法により UPDATE とDELETE の両方で競合を検出できます。

このアプローチは Lamport タイムスタンプに似ており、競合検出の ABA 問題を完全に防ぎます。

注釈

行レベルの競合解決は、行のバージョン管理を使用しても 列レベルの競合解決の有効化と無効化 構成に基づいて処理されます。行バージョンが生成される方法は、競合を検出する場合にのみ役立ちます。行のバージョンについての信頼できる情報として信頼しないでください。

特定のテーブルに使用されている現在の競合解決戦略を判断するには、ビューbdr.tables の列conflict_detection を参照してください。

bdr.alter_table_conflict_detection

テーブルの所有者は、特定のテーブルの競合検出の動作を変更できます。

概要

bdr.alter_table_conflict_detection(relation regclass,
                                   method text,
                                   column_name name DEFAULT NULL)

パラメーター

  • relation —新しい競合検出メソッドを設定するリレーションの名前。

  • method —使用する競合検出メソッド。

  • column_name —列検出データの格納に使用する列。これはスキップされたできます。その場合、列名前は競合検出メソッドに基づいて選択されます。 row_origin メソッドは、メタデータストレージ用の追加の列を必要としません。

認識されている競合検出方法は次のとおりです。

  • row_origin —タプルに対して行われた以前の変更の起点(

    オリジンの競合検出

    を参照)。これは、テーブルに追加の列を必要とせずにサポートされている唯一のメソッドです。

  • row_version —行バージョン列( 行バージョンの競合検出 を参照)。

  • column_commit_timestamp —列ごとのコミットタイムスタンプ(

    CLCD で説明)。

  • column_modify_timestamp —列ごとの変更タイムスタンプ(

    CLCD で説明)。

注意事項

column_commit_timestamp とcolumn_modify_timestamp の競合検出方法の違いの詳細については、 現在とコミットのタイムスタンプ を参照してください。

このファンクションは、 DDL ステートメントと同じレプリケーションメカニズムを使用します。これは、レプリケーションが ddl filters 構成の影響を受けることを意味します。

このファンクションは、列レベルの競合解決が有効になっているリレーションでDML グローバルロックを取得します。

このファンクションはトランザクションです。トランザクションの ROLLBACK で効果をロールバックでき、変更は現在のトランザクションで可視されます。

bdr.alter_table_conflict_detection ファンクションは、 bdr.backwards_compatibility が30618以下に設定されていない限り、relation の所有者のみが実行できます。

警告

競合検出メソッドを追加の列を使用してメタデータを保存する方法から変更すると、その列が削除されます。

警告

このファンクションはCAMOを無効にします(ワーニングとともに、これらが`bdr.camo_enable_client_warnings` で無効にされていない限り)。

競合タイプのリスト

BDRは、 conflict_type パラメータとして使用できる次の競合タイプを認識します。

  • insert_exists —着信挿入が、プライマリキーまたは一意のキー/インデックスを介して既存の行と競合します。

  • update_differing —着信更新のキー行がローカル行と異なります。これは、 行バージョンの競合検出 を使用している場合にのみ発生します。

  • update_origin_change —着信更新は、別のノードによって最後に変更された行を変更しています。

  • update_missing —着信更新が存在しない行を変更しようとしています。

  • update_recently_deleted —受信した更新が、最近削除された行を変更しようとしています。

  • update_pkey_exists —着信更新により、 PRIMARY KEY が変更を適用しているノードに既に存在する値に変更されました。

  • multiple_unique_conflicts —着信行は、ターゲットテーブル内のマルチプルのUNIQUEコンストレイン/インデックスと競合しています。

  • delete_recently_updated —現在のノードでの行の最新の更新よりも古いコミットタイムスタンプを持つ着信削除、または行バージョンの競合検出を使用している場合。

  • delete_missing —着信削除が存在しない行を削除しようとしています。

  • target_column_missing —ターゲットテーブルには、受信行にある1つ以上の列がありません。

  • source_column_missing —受信行には、ターゲットテーブルに存在する1つ以上の列がありません。

  • target_table_missing —ターゲットテーブルがありません。

  • apply_error_ddl —レプリケートされたDDLコマンドを適用するときにPostgresによってエラーがスローされました。

競合の解決

ほとんどの競合は自動的に解決できます。 BDRは、デフォルトで last-update-winsメカニズム、より正確には update_if_newer 競合リゾルバです。このメカニズムは、競合検出に使用されたのと同じコミットタイムスタンプに基づいて、競合する2つの行のうちの最後に挿入または変更された行を保持します。特定のコーナーケースのシナリオでの動作は、 bdr.create_node_group、またはbdr.alter_node_groupに使用される設定によって異なります。

BDRでは、次のファンクションを使用して、競合解決のデフォルトの動作をオーバーライドできます。

bdr.alter_node_set_conflict_resolver

このファンクションは、指定されたノードでの競合解決の動作を設定します。

概要

bdr.alter_node_set_conflict_resolver(node_name text,
                                     conflict_type text,
                                     conflict_resolver text)

パラメーター

注意事項

現在、ローカルノードのみを変更できます。ファンクション呼び出しは複製されません。マルチプルのノードの設定を変更する場合は、それぞれでファンクションを実行する必要があります。

このファンクションによる構成の変更は、 bdr.create_node_groupまたはbdr.alter_node_group で指定された競合解決のデフォルトの動作をオーバーライドします。

このファンクションはトランザクションです。変更をロールバックでき、現在のトランザクションで可視されます。

競合リゾルバーのリスト

BDRでは、ハンドルできる競合タイプのカバレッジが異なるいくつかの競合リゾルバーを使用できます。

  • error —エラーをスローし、レプリケーションを停止します。任意の競合タイプに使用できます。

  • skip —リモート変更の処理をスキップし、次の変更でレプリケーションを続行します。 insert_exists 、update_differing 、update_origin_change 、update_missing 、update_recently_deleted 、update_pkey_exists 、delete_recently_updated 、delete_missing 、target_table_missing 、target_column_missing 、およびconflictタイプに使用できます。

  • skip_if_recently_dropped —ダウンストリームで最近(1日以内)に削除されたためにダウンストリームに存在しないテーブルの場合、リモート変更をスキップします。それ以外の場合はエラーをスローします。 target_table_missing 競合タイプに使用できます。 skip_if_recently_dropped の競合リゾルバは、同じ名前のテーブルが削除された直後に再作成されると、問題が発生する可能性があります。その場合、ノードの1つが、テーブルを再作成するためのDDLを認識する前に、再作成されたテーブルのDMLを認識する可能性があります。次に、テーブルが最近削除されたと仮定して、リモートデータを誤ってスキップし、データロスします。したがって、この競合リゾルバとともに削除された直後にオブジェクトnamesqを再利用しないことをお勧めします。

  • skip_transaction —競合を生成したトランザクション全体をスキップします。 apply_error_ddl の競合に使用できます。

  • update_if_newer —リモート行が競合するローカル行よりも後にコミットされた場合(発信元ノードの壁時計によって決定)を更新します。タイムスタンプが同じ場合、ノードIDがタイブレーカーとして使用され、保証のノードで同じ行が選択されます(ノードIDが高い方が勝ちます)。 insert_exists 、update_differing 、update_origin_change 、およびupdate_pkey_exists の競合タイプに使用できます。

  • update —常に複製されたアクションを実行します。 insert_exists (INSERT をUPDATE に変換)、update_differing 、update_origin_change 、update_pkey_exists 、およびdelete_recently_updated (削除を実行)に使用できます。

  • insert_or_skip —オリジンから送信された利用可能な情報から新しい行を作成し、挿入してみてください。完全な行を作成するのに十分な情報がない場合は、変更をスキップします。 update_missing およびupdate_recently_deleted の競合タイプに使用できます。

  • insert_or_error —オリジンから送信された利用可能な情報から新しい行を作成して挿入しようとします。完全な行を構築するのに十分な情報がない場合は、エラーをスローしてレプリケーションを停止します。 update_missing およびupdate_recently_deleted の競合タイプに使用できます。

  • ignore —欠落しているターゲット列を無視して、処理を続行します。 target_column_missing 競合タイプに使用できます。

  • ignore_if_null —リモート行の余分な列に NULL 値が含まれる場合、欠落しているターゲット列を無視します。それ以外の場合は、エラーをスローしてレプリケーションを停止します。 target_column_missing 競合タイプに使用できます。

  • use_default_value —欠落している列値をデフォルト(それが列デフォルトの場合は NULL を含む)で埋めて、処理を続行します。デフォルトの処理中のエラーまたはコンストレインの違反(つまり、NOT NULL列のNULLデフォルト)はレプリケーションを停止します。 source_column_missing 競合タイプに使用できます。

insert_exists 、 update_differing 、 update_origin_change 、 update_missing 、 multiple_unique_conflicts 、 update_recently_deleted 、 update_pkey_exists 、 delete_recently_updated, and delete_missing`の競合タイプは、

を使用してユーザー定義のロジックで解決することもできます。

このマトリクスは、競合リゾルバーがハンドルできる競合の種類を区別するのに役立ちます。

insert_exists

update_differing

update_origin_change

update_missing

update_recently_deleted

update_pkey_exists

delete_recently_updated

delete_missing

target_column_missing

source_column_missing

target_table_missing

multiple_unique_conflicts

error

X

X

X

X

X

X

X

X

X

X

X

X

skip

X

X

X

X

X

X

X

X

X

X

X

X

skip_if_recently_dropped

X

update_if_newer

X

X

X

X

update

X

X

X

X

X

X

insert_or_skip

X

X

insert_or_error

X

X

ignore

X

ignore_if_null

X

use_default_value

X

conflict_trigger

X

X

X

X

X

X

X

X

X

デフォルトの競合リゾルバー

Conflict type

Resolver

insert_exists

update_if_newer

update_differing

update_if_newer

update_origin_change

update_if_newer

update_missing

insert_or_skip

update_recently_deleted

skip

update_pkey_exists

update_if_newer

multiple_unique_conflicts

error

delete_recently_updated

skip

delete_missing

skip

target_column_missing

ignore_if_null

source_column_missing

use_default_value

target_table_missing

skip_if_recently_dropped

apply_error_ddl

error

競合解決のリスト

競合解決は、競合リゾルバによって選択された解決の種類を表し、競合を解決するために実行された特定のアクションに対応します。

conflict_resolution パラメータでは、現在次の競合解決がサポートされています。

  • apply_remote —リモート(着信)行が適用されました。

  • skip —行の処理がスキップされたされました(ローカルで変更はありません)。

  • merge —リモート行とローカル行からの情報をマージして、新しい行が作成されました。

  • user —ユーザーコード(競合トリガー)は、ターゲット表に書き込まれた行を生成しました。

競合ロギング

マルチマスターの競合の診断とハンドリングを容易にするために、 BDRはデフォルトで、すべての競合を bdr.conflict_history テーブルに記録します。次の関数を使用して、この動作をより詳細に変更できます。

bdr.alter_node_set_log_config

ノードの競合ロギング構成を設定します。

概要

bdr.alter_node_set_log_config(node_name text,
                              log_to_file bool DEFAULT true,
                              log_to_table bool DEFAULT true,
                              conflict_type text[] DEFAULT NULL,
                              conflict_resolution text[] DEFAULT NULL)

パラメーター

  • node_name —変更されるノードの名前。

  • log_to_file —ノードログログファイルに記録するかどうか。

  • log_to_table — bdr.conflict_history テーブルにログを記録するかどうか。

  • conflict_type —ログに記録する競合の種類。 NULL(デフォルト)はすべてを意味します。

  • conflict_resolution —ログに記録する競合の解決。 NULL(デフォルト)はすべてを意味します。

注意事項

ローカルノードのみ変更できます。ファンクション呼び出しは複製されません。マルチプルのノードの設定を変更する場合は、それぞれでファンクションを実行する必要があります。

このファンクションはトランザクションです。変更をロールバックでき、現在のトランザクションで可視されます。

競合ロギング構成のリスト

ビューbdr.node_log_config は、すべてのロギング構成を表示します。ロギング構成の名前、ログに記録する場所、および競合の種類と解決がリストされます。

テーブルへの競合のログ

log_to_table がtrueに設定されている場合、競合はテーブルに記録されます。競合ロギングの対象テーブルはbdr.conflict_history です。

この表は、列local_time でレンジパーティション化されています。テーブルはAutopartitionによって管理されています。デフォルトでは、毎日新しいパーティションが作成され、過去1か月の競合が維持されます。その後、古いパーティションは自動的に削除されます。自動パーティションは、7〜14個のパーティションを事前に作成します。 bdr_superuserはこれらのデフォルトを変更できます。

BDRによって管理されるすべてのテーブルで生成された競合はこのテーブルに記録されるため、正当なユーザーのみが競合するデータを読み取れるように保証ことが重要です。 BDRは、 bdr.conflict_history テーブルにROW LEVEL SECURITYポリシーを定義することによりこれを行います。テーブルの所有者のみが、それぞれのテーブルの競合を読み取ることができます。基になるテーブルでRLSポリシーが定義、有効化、および適用されている場合、所有者でさえ競合を読み取ることはできません。 FORCE オプションで作成された RLS ポリシーは、テーブルの所有者にも適用されます。その場合、基になるテーブルの一部またはすべての行が所有者でさえも読み取れない場合があります。そのため、 BDRは競合ログテーブルにより厳しいポリシーも適用します。

デフォルトのロールbdr_read_all_conflicts は、 bdr_superuser ロールを付与せずに、 bdr.conflict_history テーブルに記録されたすべての競合の詳細を表示する必要があるユーザーに付与できます。

デフォルトのロールbdr_read_all_stats は、 bdr.conflict_history_summary というカタログビューにアクセスできます。これにはユーザデータが含まれないため、ログに記録された競合をモニタリングできます。

競合レポート

テーブルに記録された競合は、レポートに要約できます。レポートを使用すると、アプリケーションの所有者は競合を特定、理解、解決し、アプリケーションの変更を導入して競合を防ぐことができます。

SELECT nspname, relname
, date_trunc(day, local_time) :: date AS date
, count(*)
FROM bdr.conflict_history
WHERE local_time > date_trunc(day, current_timestamp)
GROUP BY 1,2,3
ORDER BY 1,2;

 nspname | relname |    date    | count
- --------+---------+------------+-------
 my_app  | test    | 2019-04-05 |     1
(1 row)

LiveCompareによるデータ検証

LiveCompareは、2つのデータベースを比較して同一であることを確認するように設計されたユーティリティプログラムです。

LiveCompareはBDRスタックのパートとして含まれており、 BDRノードの任意のペアを対象としています。デフォルトでは、すべてのレプリケートされたテーブルを比較し、違いを報告します。 LiveCompareは、 Postgresやオラクルなどの非BDRデータソースでも動作します。

LiveCompareを使用して、着信行を継続的にモニタすることもできます。コンテキスト情報を失うことなく停止およびスタートできるため、都合の良いときに実行できます。

LiveCompareを使用すると、マルチプルのテーブルを同時にチェックできます。いくつかのテーブルまたはテーブル内の行のセクションのみをチェックできるように構成できます。チェックは、最初に行ハッシュ全体を比較することによって実行されます。異なる場合、LiveCompareは行全体を比較します。 LiveCompareは、有用なサイズのバッチで行を比較することにより、オーバーヘッドを回避します。

違いが見つかった場合、期間をかけて再確認できるため、最終的な一貫性が遅れます。

詳細については、 LiveCompareによるデータ検証 の文書を参照してください。