- ローカルファーストアプリは高速な応答性と基本的なプライバシー保護を約束する一方で、実際にはオフライン対応の実装が非常に難しいという限界がある
- 最大の理由は同期の複雑さにあり、複数のデバイスで同時にデータが変更された場合、最終的に完全に同一の状態へ収束しなければならないという問題が生じる
- 大きな技術的課題は、時間順序の不確実性と競合の2つである
- この問題を解決するには、Hybrid Logical Clocks(HLCs) や CRDTs のような分散システム設計を適用する必要がある
- SQLite ベースの拡張機能を活用することで、信頼性が高くシンプルな同期アーキテクチャを提供でき、これはあらゆるプラットフォームで利用可能である
オフラインファーストアプリの約束と現実
- オフラインファーストアプリは、即時の応答、基本的なプライバシー保護、不安定なネットワーク環境でも読み込み待ちなしで利用できることを掲げている
- 実際には、ほとんどのアプリがオフライン対応を十分に実装できておらず、多くは変更をローカルに一時保存し、後でネットワーク接続時に送信する方式にとどまっている
- このような実装は信頼性に欠け、最終的には「変更が保存されない可能性があります」といった警告メッセージにつながる
同期の根本的な難しさ
- ローカルファーストアプリを作ると、必然的に分散システムを構築することになる
- 複数のデバイスがオフライン環境でそれぞれ独立してデータを変更でき、後で再接続したときに同一の状態へ正確に収束しなければならない
- そのためには2つの大きな課題が存在する
1. イベント順序の不確実性
- 複数のデバイスでイベントが異なる時点に発生し、順序によって状態が変わりうる
- 例: Aデバイスは x=3 に設定、Bデバイスは x=5 に設定 → オフラインでそれぞれ変更した後に同期すると、異なる結果が生じる可能性がある
- 従来の集中型データベースは強い一貫性によってこれを解決するが、これはグローバル同期を必要とするため、ローカルファーストシステムには適していない
- 最終的には、イベントごとに適切な順序を動的かつ分散環境でも確定する必要があり、中央の時計なしで順序を決める方法が求められる
Hybrid Logical Clocks(HLCs)の導入
- Hybrid Logical Clocks(HLCs) は、個々のデバイス間でイベント順序について実質的に合意できるようにする、シンプルで効果的なアルゴリズムである
- HLCは物理時間情報と論理カウンタを組み合わせて利用する
- 例として:
- Aデバイスが 10:00:00.100 の時刻にイベントを記録した場合、HLCは (10:00:00.100, 0)
- メッセージを受け取ったBデバイスは、自身の時計が遅れていても、HLCを (10:00:00.100, 1) に進める
- これにより、2つのデバイスの物理時計の差に関係なく、正確なイベント順序を確定できる
2. 競合の問題
- 正しい順序の適用だけでは十分ではなく、異なるデバイスで同じデータが独立して修正された場合、競合は必然的に発生する
- ほとんどのシステムは、開発者に競合解決コードを手動で書かせるが、これはエラー発生のリスクと運用負担を招く
CRDTsの活用
- Conflict-Free Replicated Data Types(CRDTs) を適用するのが最良の方法である
- CRDTsは、どのような順序で同期しても、あるいは重複適用しても、各デバイスの状態が最終的に常に同一になることを保証する
- 最も単純なCRDT戦略はLast-Write-Wins(LWW) である
- 各更新にタイムスタンプを付与する
- 同期時には、より新しいタイムスタンプの値が選ばれる
SQLiteの利点
- ローカルファーストアプリを構築するには、信頼性が高く軽量なローカルDBが不可欠であり、SQLite が最適な選択肢である
- SQLiteベースのフレームワーク拡張で同期機能を実装すると、次のような利点がある
- メッセージ適用は単純: 現在値を参照 → 新しいタイムスタンプのほうが新しければ上書き → そうでなければ無視
- この方式は同期順序に関係なく、全デバイスでの状態収束性を保証する
アーキテクチャの意義
- この構造はシンプルで信頼性の高い同期を実現する
- 数週間オフライン状態でもデータ消失がない信頼性
- 常に最終状態へ収束する決定論的性質
- 重い依存関係のない軽量なSQLite拡張だけで実現可能
- iOS, Android, macOS, Windows, Linux, WASM など主要な全プラットフォームをサポート
開発者への提言
- 単純なリクエストキューでオフラインモードを“それらしく”見せる方式は避けるべきである
- 結果整合性の概念を受け入れ、HLCやCRDTのような実証済みの分散システム技術を活用する必要がある
- 大きく複雑なフレームワークではなく、小さく依存関係の少ない構造を志向するのが望ましい
- その結果、アプリは即時起動、オフライン利用可能、基本的なプライバシー保護といった利点を享受できる
オープンソース SQLite-Sync 参考案内
- プロダクションで今すぐ使えるクロスプラットフォームのオフラインファーストエンジンに関心があるなら、オープンソースの SQLite-Sync 拡張を参照できる
1件のコメント
Hacker Newsの意見
Cache-Controlを正しく使い、ネットワーク層でそれを順守することで多くの問題を解決できる。こうすればサーバー側でキャッシュ寿命を変えても、アプリ更新なしですぐ適用できる