Using CRDTs#

テーブルでCRDTを使用する#

CRDTを使用するには、 integer などの標準のビルトインデータ型ではなく、 CREATE/ALTER TABLEで特定のデータ型を使用する必要があります。たとえば、1つの通常の整数カウンターと単一の行を含む次のテーブルを検討します。

非CRDTの例#

CREATE TABLE non_crdt_example (
    id      integer             PRIMARY KEY,
    counter integer             NOT NULL DEFAULT 0
);

INSERT INTO non_crdt_example (id) VALUES (1);

2つの異なるノードで次のSQLを同時に発行するとします。

UPDATE non_crdt_example
   SET counter = counter + 1    -- "reflexive" update
 WHERE id = 1;

両方の更新を適用した後、次のクエリを使用して結果の値を確認できます。

SELECT * FROM non_crdt_example WHERE id = 1;
   id |   counter
 -----+-----------
    1 |         1
(1 row)

このコードは、 update_if_newer 競合リゾルバーが原因でインクリメントの1つが失われたことを示しています。

CRDTの例#

代わりにCRDTカウンターデータ型を使用するには、次の手順を実行します。

テーブルを作成しますが、カウンターデータ型としてCRDT bdr.crdt_gcounter を使用します。

CREATE TABLE crdt_example (
    id      integer             PRIMARY KEY,
    counter bdr.crdt_gcounter   NOT NULL DEFAULT 0
);

列レベルの競合解決のためにテーブルを構成します。

ALTER TABLE crdt_example REPLICA IDENTITY FULL;

SELECT bdr.alter_table_conflict_detection(crdt_example,
            column_modify_timestamp, cts);

次に、この例の値を含む行を挿入します。

INSERT INTO crdt_example (id) VALUES (1);

ここで、以前と同様に、2つのノードで同じSQLを同時に発行するとします。

UPDATE crdt_example
   SET counter = counter + 1    -- "reflexive" update
 WHERE id = 1;

変更が適用されると、カウンターが更新を同時に実行できることがわかります。

SELECT id, counter FROM crdt_example WHERE id = 1;
   id |   counter
 -----+-----------
    1 |         2
(1 row)

この例は、CRDTが他の方法で競合する非同期同時更新に直面しても、アキュムレータ列が動作できることを示しています。

構成オプション#

bdr.crdt_raw_value 構成オプションは、クエリが現在の値またはCRDTタイプの完全な内部状態を返すかどうかを決定します。デフォルトでは、現在の数値のみが返されます。 true に設定されている場合、クエリーは完全な状態の表現を結果ます。特殊なハッシュ演算子 # を使用して、特殊な演算子デフォルト動作を使用せずに現在の数値のみを要求できます。 bdr.crdt_raw_value = on を使用して完全な状態がダンプされた場合、値はbdr.crdt_raw_value = on でのみリロードできます。

注釈

bdr.crdt_raw_value は、クライアントに返されたデータ、つまり選択リストの単純な列参照のみのフォーマットを適用します。クエリーの他の部分の列参照たとえば`WHERE` 句またはselectリストの式などは、 # 演算子を使用する必要がある場合があります。

さまざまな種類のCRDT#

crdt_gcounter タイプは、例が示すように、 x = x + 1 などの再帰的UPDATE SQLでのみ動作する状態ベースのCRDTタイプの例です。

CRDTの別のクラスは、 delta CRDT タイプです。これらは オペレーションベースのCRDTタイプCmCRDT の特別なサブクラスです。

デルタCRDTでは、値の更新は、同じノード上の以前の値と比較されます。次に、変更は他のすべてのノードにデルタとして適用されます。

CREATE TABLE crdt_delta_example (
    id       integer            PRIMARY KEY,
    counter  bdr.crdt_delta_counter NOT NULL DEFAULT 0
);

ALTER TABLE crdt_delta_example REPLICA IDENTITY FULL;

SELECT bdr.alter_table_conflict_detection(crdt_delta_example,
            column_modify_timestamp, cts);

INSERT INTO crdt_delta_example (id) VALUES (1);

2つのノードで次のSQLを同時に発行するとします。

UPDATE crdt_delta_example
   SET counter = 2          -- notice NOT counter = counter + 2
 WHERE id = 1;

両方の更新を適用した後、次のクエリを使用して結果の値を確認できます。

SELECT id, counter FROM crdt_delta_example WHERE id = 1;
   id | counter
 -----+---------
    1 |       4
(1 row)

通常のinteger 列の場合、結果は2 になります。ただし、デルタCRDTカウンターで行を更新する場合、古い行バージョンで開始し、新しい行バージョンを作成し、両方をリモートノードに送信します。そこで、そこにあるバージョンたとえばローカルバージョンなどと比較します。標準CRDTはNEWとLOCALバージョンをマージしますが、デルタCRDTはOLDとNEWバージョンを比較し、デルタをローカルバージョンに適用します。

クエリのプランニングと最適化#

重要な問題は、クエリのプランニングと最適化がこれらの新しいデータ型でどのように機能するかです。 CRDTタイプは透過的に処理されます。 ANALYZE とオプティマイザーの両方が動作するため、他に何もせずに推定とクエリプランニングは正常に動作します。