2025/10/09 14:01 CRDT and SQLite: Local-First Value Synchronization

やっほー、ロボ子!ローカルファーストアプリケーションのデータ同期について、面白い記事を見つけたのじゃ!

博士、こんにちは。ローカルファーストアプリケーションですか?最近よく耳にする気がします。

そうじゃ!この記事によると、CRDT(Conflict-free Replicated Data Types)を使って、SQLiteを基盤とするアプリでデータのINSERT、UPDATE、DELETEをうまく同期させるらしいのじゃ。

CRDTですか。データの競合を避けるためのものですよね。具体的にはどうやるんですか?

まず、SQLiteのトリガーを設定して、データの変更をインターセプトするのじゃ。そして、変更をカラム単位のイベントに分割するらしいぞ。各カラムには、原因に関するメタデータが付与されるみたいじゃ。

カラム単位でイベントを分割するんですね。それによって、何が嬉しいんですか?

カラムレベルのクロックを使うことで、例えば、Bobが`status`を変更し、Aliceが`title`を変更した場合、競合なしにマージできるのじゃ!

なるほど!もし両方が同じカラムを変更した場合はどうなるんですか?

その場合は、最も高いクロック(または他のCRDTルール)が優先されるのじゃ!

ふむふむ。同期の仕組みも気になります。

同期は、P2Pモードとサーバーオーケストレーションモードがあるみたいじゃ。P2Pモードでは、AliceがBobに「どの`db_version`を見たか」を尋ねて、BobはAliceの`site_id`の最後の`db_version`で応答するのじゃ。AliceはBobの応答より大きい`db_version`を持つ操作のみを送信するらしいぞ。

バージョン管理をして差分だけを送るんですね。効率的です。

サーバーオーケストレーションモードでは、Aliceは同期サービスに連絡するのじゃ。サーバーはすべてのクライアントの`site_id`ごとの最後の`db_version`を保持していて、Bobが同期すると、サーバーはBobにどの`db_version`が欠落しているかを伝え、それらの操作のみをストリーミングするのじゃ。

サーバーが差分を管理してくれるんですね。P2Pと比べて、構成がシンプルになりそうですね。

UPDATEの場合、エンジンはイベントをキャプチャし、`status`の新しい`column_version`を生成するのじゃ。そして、`op_type = UPDATE`で新しい操作レコードを`metadata_table`に挿入するらしいぞ。

`metadata_table`に操作履歴が記録されるんですね。

DELETEは、物理的な行の削除ではなく、墓石として実装されることが多いみたいじゃ。エンジンは`op_type = DELETE`(墓石)で操作を書き込むのじゃ。

墓石ですか。削除されたことを示すフラグのようなものですね。

そうじゃ!そして、AliceとBobはオフラインでも作業できて、ネットワークが再度利用可能になると、同期レイヤーが「各サイトからまだ見ていない操作は何か」を尋ねて、欠落しているすべての操作を送信およびマージするのじゃ!

オフラインでも作業できるのは便利ですね!

この記事によると、SQLiteとAIを使用してローカルファースト同期を容易にすることを使命とする「SQLite AI」というプロジェクトがあるらしいのじゃ!

へー、面白そうですね!ローカルファーストのアプリがもっと簡単に作れるようになるかもしれませんね。

そうじゃな!ところでロボ子、ローカルファーストって、まるで私がいつもおやつを最初に食べるみたいじゃな!

博士、それはちょっと違いますよ!
⚠️この記事は生成AIによるコンテンツを含み、ハルシネーションの可能性があります。
