Conflicts

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

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

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

この章では、標準データ型と行レベルの競合について詳しく説明します。

この章で後述するように、競合のハンドリングは構成可能です。競合は、Stream Triggersの章で説明されている競合トリガーを使用して、テーブルごとに異なる方法で検出および処理できます。

列レベルの競合検出および解決は、CLCDの章で説明されているBDRで利用できます。

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

  • 競合のないデータ型(CRDT)-CRDTの章で説明されています。

  • イーガーレプリケーション-Eager Replicationの章で説明されています。

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

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

分散ロックは基本的に悲観的なアプローチであり、BDRは楽観的なアプローチを支持します。可能な場合は競合を回避しますが、競合が発生した場合はそれらを発生させて解決します。

!!! Warning “Upgrade Notes” * すべてのSQL可視インターフェイスはbdrスキーマにあります。 bdr_conflictsまたはbdr_crdtスキーマの以前に非推奨になったすべてのインターフェースは削除され、3.7 +ノードまたは少なくとも1つの3.7+ノードを含むグループでは動作しません**。すべてのBDRバージョンにすでに存在するbdrスキーマのものを使用してください。

競合の発生方法

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

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

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

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

競合の種類

主キーまたは一意の競合

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

行の競合は次のとおりです。

  • INSERT vs INSERT

  • UPDATE vs UPDATE

  • UPDATE vs DELETE

  • INSERT vs UPDATE

  • INSERT vs DELETE

  • DELETE vs DELETE

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

INSERT / INSERTの競合

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

BDRは、ユーザー定義の競合ハンドラによってオーバーライドされない限り、発信元ホストのタイムスタンプに従って、最近挿入された2つのタプルを保持することでこれを処理します。

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

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

このタイプの競合は、Global Sequencesを使用することで効果的に解消できます。

複数の一意のConstraintsに違反する挿入

INSERT / INSERTの競合は、複数のUNIQUE制約(そのうちの1つはPRIMARY KEYかもしれません)に違反する可能性があります。新しい行が複数のUNIQUE制約に違反しており、その結果、複数の別の行と競合する場合、レプリケーションの変更を適用するとamultiple_unique_conflictsの競合が発生します。

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

!!! Warning * マルチプルの行がこのように競合する場合、競合解決の結果が挿入オペレーションを続行することである場合、データの一部は常に削除されます!

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

更新/更新の競合

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

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

これらの競合は両方とも、前述のようにinsert_existsと同じ方法で解決されます。

プライマリキーの更新の競合

現在、 BDRはPRIMARY KEY操作がUPDATEオペレーションによって変更されている場合、競合解決を実行できません。 primarykeyを更新することはできますが、既存の値と競合しない保証にする必要があります。

プライマリキーの更新の競合は[Divergent Conflicts]であり、マニュアルの演算子介入が必要です。

PKの更新はPostgreSQLで可能ですが、 PostgreSQLと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 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

そのため、PKを更新するPostgreSQLアプリケーションの場合、 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)

このシチュエーションは、LiveCompareを使用して特定および解決できます。

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

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

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

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

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

その結果、特にBDRを使用して、アプリケーションでPK UPDATEを許可しないことを強くお勧めします。主キーを変更するアプリケーションの部分がある場合、同時変更を回避するには、Eagerレプリケーションを使用しmakeそれらの変更を行います。

!!! Warning * update_pkey_existsの競合の解決により更新が発生した場合、行の1つが常に削除されます!

複数の一意のConstraintsに違反する更新

[複数のUNIQUEConstraintsに違反するINSERT]のように、incomingUPDATEが複数のUNIQUEインデックス(および/またはPRIMARY KEY)に違反する場合、BDRはmultiple_unique_conflictsの競合を発生させます。

BDRは、遅延一意コンストレインをサポートしています。トランザクションがソースでコミットできる場合、競合が発生しない限り、ターゲットにクリーンに適用されますが、遅延プライマリキーはコンストレインワーニングとして使用できません。 。

更新/削除の競合

1つのノードが、別のノードが同時にb_tran_4した行をUPDATEすることができます。この場合、再生時にUPDATE / リプレイの競合が発生する可能性があります。

DELETEd行がまだ検出可能な場合(削除され行がVACUUMによって削除されていない場合)、update_recently_deletedの競合が生成されます。デフォルトでは、UPDATEは単にスキップされたされますが、これに対する解決策は構成できます。詳細については[競合解決]を参照してください。

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

競合する別のタイプのDELETEとUPDATEは、行がローカルでUPDATEdになった後に実行されるDELETE操作です。このシチュエーションでは、結果は使用される競合検出のタイプに依存します。デフォルトの[Origin Conflict Detection]を使用すると、競合はまったく検出されず、DELETEが適用され、行が削除されます。 行バージョンの競合検出を有効にすると、delete_recently_updatedの競合が生成されます。この競合タイプのデフォルトの解決策は、DELETEを適用して行を削除することですが、これは競合トリガーを介して構成または処理できます。

INSERT / UPDATEの競合

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

これが発生すると、update_missingの競合が生成されます。デフォルトの競合リゾルバはinsert_or_skipですが、代わりにinsert_or_errorまたはskipを使用できます。 insert-or-actionを実行するリゾルバーは、可能であれば(行全体が受信されたとき)UPDATEからのデータに基づいて新しい行をINSERTに最初に試行します。行の再構築を可能にするには、テーブルにhaveREPLICA IDENTITY FULLがニーズであるか、行にTOASTされたデータが含まれていない必要があります。

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

挿入/削除の競合

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

現在、 BDRはこの競合タイプを検出できません。INSERT操作は競合タイプを生成せず、INSERTが適用されます。

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

削除/削除の競合

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

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

両方のDELETEが同じ効果を持つため、この競合は無害です。したがって、どちらか一方を安全に無視できます。

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

1つのノードINSERTsが行を2番目のノードとUPDATEdthereで再生される場合、3番目のノードは2番目のノードからUPDATEを受信してから、1番目のノードからINSERTを受信できます。これはINSERT / UPDATEの競合です。

これらの競合は、UPDATEを破棄することで処理されます。これにより、異なるノードで異なるデータが発生する可能性があります。つまり、これらは[Divergent Conflicts]です。

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

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

1.ノード2はノード12からINSERTを受信します。ノード2はUPDATE3を実行します。ノード3はノード24からUPDATEを受け取ります。ノード3はノード1からINSERTを受け取ります

insert_or_error(または、場合によってはupdate_missing競合タイプの場合はinsert_or_skip競合リゾルバー)を使用することは、競合に対する実行可能な緩和戦略です。ただし、このオプションを有効にすると、INSERT / DELETEの競合が発生することに注意してください。以下を参照してください。

1.ノード1がUPDATE2を実行します。ノード2はDELETE3を実行します。ノード3はノード24からDELETEを受け取ります。ノード3はノード1からUPDATEを受け取り、INSERTに変換します

これらが問題である場合、update_recently_deletedとして正しく検出されるように、tableorデータベースの凍結設定を調整することをお勧めします。

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

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

BDRは、update_missingの競合で発生するmakeに、各INSERTを最近削除されたチェックに入れることを選択できます。ただし、これを行うと、大部分のユーザーにペナルティが科せられるため、現時点ではdelete_missingをログに記録するだけです。

以降のリリースでは、 ログの競合が発生した場合、LiveCompareを使用した再チェックにより、INSERT / DELETEの異常が自動的に解決されます。後で参照してください。

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

  • INSERT、それに続くDELETE-キューイングアプリケーションで使用できるように

  • テーブルのPK識別子が再利用される場合

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

BDRは、レプリケーションが正しく機能するmakeに識別子のBDR性に依存しているため、後者の場合に問題があります。

同じ一意の識別子を挿入、削除し、後で再利用するアプリケーションは、問題を引き起こす可能性があります。これは、ABA問題として知られています。 BDRには、行が現在の行、最後の行、またははるかに古い行であるかどうかを知る方法がありません。< https://en.wikipedia.org/ wiki/ ABA_problem >

一意の識別子の再利用もまた、ビジネス上の問題です。これは、時間の経過とともに一意の識別ができないため、監査、トレーサビリティ、および適切なデータ品質が妨げられるためです。アプリケーションは一意の識別子を再利用する必要はありません。

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

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

シーケンスまたはUUIDを使用するアプリケーションは、この問題の影響を受けません。

外部キー制約の競合

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

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

最初に問題を説明してから、解決ソリューションを提供します。

シングルマスターPostgreSQLでは、参照されるテーブルの値を参照するINSERT / UPDATEは、行レベルのロックを取得する前にDELETEが完了するまで待機する必要があります。 DELETEが被参照た値を削除すると、INSERT / UPDATEはFKチェックに失敗します。

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

実際には、これは、参照被参照でDELETEが発生した場合にのみ発生します。これは一般的なオペレーションではありません。

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

FKがぶら下がる可能性はありシチュエーションが、このリスクは一般的に非常に低いため、 BDRはこのケースをカバーする一般的なソリューションを課しません。

最初の解決策は、FKの使用を、通常一度に1つのノードのみから変更されるか、まれにしか変更されない、または変更の並行性がアプリケーションを介する密接に関連するエンティティに制限することです。これにより、アプリケーションレベルでのFK違反が回避されます。

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

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

RefDataの被参照されたrowがすでに削除されている場合、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は、すべての行の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に変換するときに既存の制約を削除しない場合は、レプリケーションが中断することを予期する必要があります。再度進行させるには、リモートトランザクションを適用できるように、着信リモートタプルが競合するローカルタプルを削除または変更します。

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

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

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

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

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

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

関連するロックには以下が含まれます。

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

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

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

BDRライタプロセスは、ユーザートランザクションでデッドロックすることもできます。この場合、ユーザトランザクションは、ライタプロセスが保持するロックを待機しています。 2つのライタプロセスも互いにデッドロックする可能性があります。 PostgreSQLのデッドロックディテクタは、問題のあるトランザクションの1つに介入して終了します。 BDRライタプロセスが終了した場合、単にリトライし、通常は成功します。

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

PostgreSQLでlog_lock_waitsfacilityを使用すると、ロック関連のリプレイストールを識別できヘルプ。

発散する対立

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

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

行データに関連するさまざまな競合では、通常、管理者がノードの1つのデータを手動で調整して、他のノードと整合性を保つ必要があります。文書化されているとおりにBDRが使用され、安全でないとマークされた設定または機能が回避される限り、このような競合は発生しないはずです。

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

TOASTサポートの詳細

PostgreSQLは、TOASTと呼ばれる大きな列にライン外ストレージを使用します。

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

TOAST値は、値が変更された場合にのみトランザクションログ(WAL)にログオン。これは、特に、更新された列の値を変更しなかったUPDATEステートメントがその列のない行を生成するため、UPDATEの競合をハンドリングするときに問題を引き起こす可能性があります。 [INSERT / UPDATE Conflicts]で述べたように、update_missingconflictがinsert_or_errorを使用して解決され、 TOAST列が欠落している場合、 BDRはエラーを生成します。

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

1.ノードA:txn A1はUPDATE SET col1 = ’ トースト data …‘を実行し、first2をコミットします。ノードB:txn B1はUPDATE SET other_column = ’anything else’;そしてA13以降にコミットします。ノードC:ノードAへの接続が遅れる4。ノードC:txn B1が最初に適用され、col1のTOASTされた列が欠落しますが、競合せずに適用されます5。ノードC:txn A1は(update_origin_changeで)競合し、スキップされます6。ノードCはA1からのトーストされたデータを永遠に失います

BDRが最近複製された行にローカルのUPDATEを検出すると、 BDRがTOAST列の独自のロギングを追加するため、 BDRを使用する場合(ビルトインロジカルレプリケーションまたはマルチマスターのプレーンpglogicalを使用する場合)、通常は上記の問題はありませんTOAST列の変更。localUPDATEはTOASTを変更していません。したがって、 BDRは、マルチプルのノードで更新が発生した場合(つまり、タプルのオリジンが変更された場合)にWALロギングが増加する代わりに、TOASTされたデータの不整合を防ぎます。すべての更新が単一のノードから行われた場合、追加のWALオーバーヘッドはゼロになりますBDR AlwaysOnアーキテクチャでは通常そうです。

!!! Note * メインテーブルでも同じことを行わずに、 TOASTテーブルでのみVACUUM FULLまたはCLUSTERを実行すると、余分なロギングが機能するために必要なメタデータが削除されます。存在しません。

!!! Warning * TOASTの追加のWALロギングは、BEFORE UPDATEトリガーを使用して行われます。このトリガーは、テーブルのすべてのBEFORE UPDATEトリガーの中で、トリガー名前に基づいてアルファベット順に最後にソートする必要があります。これを簡単にmakemakeにzzzz_bdr_の接頭辞が付いていますが、その後にソートされる名前のトリガーを作成しないでください。そうしないと、並行性の問題に対する保護が存在しません。 EDB Postgres ExtendedでBDRを使用する場合、このトリガーは作成または使用されません。

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

TOASTed列に関連するこれらの問題は、行全体がキーのパートであると見なされるため、この設定は常にキーの一部としてTOASTされた値をログするため、edREPLICA IDENTITY FULLのあるテーブルに影響しません。 BDRとpglogicalの両方は、新しい行を再構築するのに十分スマートであり、キー行の欠落データを埋めます。その結果、REPLICA IDENTITY FULLを使用するとWALサイズが大幅に増加する可能性があることに注意してください。

競合の回避または許容

ほとんどの場合、競合を回避するか、競合を許容するようにアプリケーションを設計できます。

競合は複数のノードで同時に発生している場合にのみ発生する可能性があるため、競合を回避する最も簡単な方法は、1つのノードにのみ書き込みを行うか、特定の行から一度に特定のノードから特定の行にのみ書き込みを行うことです。

これは多くのアプリケーションで自然に起こります。例、多くの消費者アプリケーションでは、所有するユーザのみがデータを変更できます。たとえば、アカウントのデフォルトの請求先住所を変更すると、データの変更はめったに発生しません。

ノードがダウンする直前に変更をmakeと、変更が失われたように見える場合があります。その後、同じ変更を再度行って、異なるノードを介して2つの更新をmakeことができます。ダウンしたノードが復旧すると、古い変更を他のノードに送信しようとしますが、データの最後の更新が保持されるため拒否されます。

INSERT / INSERTの競合の場合、Global Sequencesを使用すると、このタイプの競合を完全に防ぐことができます。

ルーム予約アプリケーションなど、オブジェクト間に関係を割り当てるアプリケーションの場合、update_if_newerを適用しても、許容できるビジネス結果が得られない場合があります。最も簡単な解決策は、 保証を使用して、1つの予約のみが成功するようにすることです。アプリケーションに応じて、より複雑な方法が可能になる場合があります。たとえば、各ノードに100シートを割り当て、そのノードのライタがそれらを予約できるようにします。

特定のタイプの更新が1つの特定のノードからのみ発生するようにする別の手法は、異なるノードを介して異なるタイプのトランザクションをルーティングすることです。例:

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

  • 注文が1つのノードに入力されるサービスアプリケーション、作業は 2番目のノードで準備してから、別のノードで顧客に提供しました。

最善のアクションコースは、頻繁に競合を発生させ、BDRの競合解決メカニズムと連携して競合に対処するようにアプリケーションを設計することです。

競合検出

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

オリジン競合検出

(以前はTimestamp Conflict Detectionとして知られていましたが、これは混乱を招きました。)

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

行の起点は、track_commit_timestamps = onの場合のみ利用可能です。

競合は、複製元が変更されたかどうかに基づいて最初に検出されるため、競合トリガーは、実際の競合ではないことが判明する可能性のある状況と呼ばれます。したがって、このメカニズムは、偽陽性の競合を生成する可能性があるため、正確ではありません。

オリジン情報は、行が凍結されるまでしか使用できません。凍結された行に到着する更新は競合を引き起こさないため、すべての場合に適用されます。これは、bdr_init_physicalで新しいノードを追加する通常のケースです。そのため、競合を発生させると、多くの誤検知ケースが発生します。

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

ユーザーは、Node Restart and Down Node Recoveryで説明されているように、拡張の停止のためにノードを放置しないことをお勧めします。

EDB Postgres Extendedでは、ノードがダウンしている間、パラメータ設定を変更することなくこのシチュエーションを適切にハンドルするために、 BDRは行の凍結を自動的に抑制します。

Postgresの他の亜種では、ユーザーはこのシチュエーションを慎重に管理する必要がある場合があります。

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

  • vacuum_freeze_min_age

  • vacuum_freeze_table_age

  • autovacuum_freeze_max_age

トランザクションレートに基づいて値を選択する必要があります。競合データがデータベースサーバから削除されるまでの猶予期間はダウンタイムです。例、 ディスク上が5億に設定されている場合、競合データが削除される前に、1000 TPSを実行しているノードがわずか5.5日ギガバイトする可能性がありスペース。 fromlower設定。

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

#  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およびトースト.autovacuum_freeze_min_age 個々のテーブルに設定することもできます。

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

行バージョンの競合検出

または、 BDRには、ノードのシステムクロックに関係なく、行のバージョン管理と競合検出を使用するオプションがあります。

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

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

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

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

カウンタはUPDATEでのみ増加しますが、この手法により、UPDATEとDELETEの両方の競合検出が可能になります。

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

!!! Note * 行レベルの競合解決は、行のバージョン管理を使用しても、[競合解決]構成に基づいて処理されます。行バージョンの生成方法は、競合の検出にのみ有用であり、どのバージョンの行がより新しいかに関する信頼できる情報として信頼すべきではありません。

特定のテーブルに使用されている現在の競合解決戦略を決定するには、ビュー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-列ごとのコミットタイムスタンプ( 章)。

  • column_modify_timestamp-列ごとの変更タイムスタンプ( 章)。

注

column_commit_timestampとcolumn_modify_timestampの競合検出方法の違いの詳細については、CLCDの章のCurrent vs Commit Timestampセクションを参照してください。

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

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

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

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

!!! Warning * メタデータを保存するために余分な列を使用するものから競合検出メソッドを変更すると、その列は削除されることにノートしてください。

!!! Warning * このファンクションは、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-着信行がマルチプルと競合します ターゲット表の一意のコンストレイン/インデックス。

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

  • delete_missing-着信削除は、そうでない行を削除しようとしています 存在します。

  • target_column_missing-ターゲット表に1つ以上の列がありません 入ってくる行に存在する。

  • source_column_missing-着信行に1つ以上の列がありません ターゲット表に存在するもの。

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

  • apply_error_ddl-適用時にPostgreSQLによりエラーがスローされました 複製されたDDLコマンド。

競合の解決

ほとんどの競合は自動的に解決できます。 BDRのデフォルトはalast-update-winsメカニズム、より正確にはupdate_if_newerconflictリゾルバです。このメカニズムは、競合検出に使用される同じコミットタイムスタンプに基づいて、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およびsource_column_missingの競合タイプ。

  • skip_if_recently_dropped-テーブルの場合、リモート変更をスキップします ダウンストリームに存在しない(最近(現在は 1日)ダウンストリームでドロップ;それ以外の場合はエラーをスローします。に使用できます target_table_missing競合タイプ。 skip_if_recently_droppedの競合 リゾルバは、同じ名前のテーブルがすぐに再作成される場合に課題を引き起こす可能性があります 落とした後。その場合、ノードの1つでDMLが表示される場合があります DDLがテーブルを再作成する前に、テーブルを再作成しました。それから テーブルが最近削除されたと仮定して、リモートデータを誤ってスキップします そして、データロスを引き起こします。したがって、オブジェクト名を再利用しないことをお勧めします これらがこの衝突リゾルバと共に落とされた直後。

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

  • update_if_newer-リモート行が後でコミットされた場合に更新する(as 発信元サーバーの壁時計によって決定されます) ローカル行。タイムスタンプが同じ場合、ノードIDはタイブレーカーとして使用されます 保証のノードで同じ行が選択されるようにします(より高いnodeidが優先されます)。 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-によって送信された利用可能な情報から新しい行を構築しよう オリジンとINSERT;構築するのに十分な情報がない場合 行全体、変更をスキップします。 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)、処理を続行します。いずれかのエラー デフォルトまたはコンストレイン違反を処理します(つまり、NULLデフォルトで NOT 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、およびdelete_missingの競合タイプは、Conflict Triggersを使用するユーザー定義ロジックでも解決できます。

これは、コンフリクトリゾルバがハンドルできるコンフリクトタイプを個別化するのにヘルプマトリクスです。

i n s e r t _ e x i s t s

u p d a t e _ d i f f e r i n g

u p d a t e _ o r i g i n _ c h a n g e

u p d a t e _ m i s s i n g

u p d a t e _ r e c e n t l y _ d e l e t e d

u p d a t e _ p k e y _ e x i s t s

d e l e t e _ r e c e n t l y _ u p d a t e d

d e l e t e _ m i s s i n g

t a r g e t _ c o l u m n _ m i s s i n g

s o u r c e _ c o l u m n _ m i s s i n g

t a r g e t _ t a b l e _ m i s s i n g

m u l t i p l e _ u n i q u e _ c o n f l i c t s

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はデフォルトですべての競合をPostgreSQLログログファイルに記録します。この動作は、次の機能を使用してより細かく変更できます。

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列でパーティション分割されたレンジです。テーブルは自動パーティションによって管理されます。デフォルトでは、毎日新しいパーティションが作成され、過去1か月間の競合が維持されます。その後、古いパーティションは自動的に削除されます。自動パーティションは、7〜14個のパーティションを事前に作成します。 bdr_superuserはこれらのデフォルトを変更する場合があります。

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

デフォルトのロール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は、Postgresand オラクルなどの非BDRデータソースでも機能します。

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

LiveCompareは、マルチプルのテーブルの同時チェックを許可し、いくつかのテーブルまたはテーブル内の行のセクションのチェックを許可するように設定できます。チェックは、最初に全行ハッシュを比較してから実行されます。有用なサイズのバッチの行。

差異が見つかった場合は、一定期間にわたって再チェックすることができ、結果整合性の遅延を許容します。

詳細については、LiveCompareのドキュメントを参照してください。