2 ポイント 投稿者 GN⁺ 2024-06-27 | 1件のコメント | WhatsAppで共有
  • Triplitは、サーバーとブラウザ間でデータをリアルタイム同期するオープンソースのデータベースで、アプリにTypescriptパッケージとして組み込んで使うフルスタックデータベースを掲げている
  • サーバー保存とクライアントクエリの同期をまとめて処理し、増分アップデート、属性単位の競合解決、ローカルキャッシュ、オフラインモード、自動再接続をサポートする
  • SQLite、IndexedDB、LevelDB、Memoryなどのプラガブルストレージをサポートし、サーバー側の永続ストレージと管理ダッシュボードを提供する
  • Reactとvanilla Javascriptでクエリ・変更APIを利用でき、React・Svelteバインディング、CLI、Console、Serverなどで構成されるモノレポとして提供される
  • 高速なインタラクションのための楽観的更新、失敗した更新のロールバック・リトライ、サーバーで強制される読み取り・書き込み権限、CRDTベースのコラボレーション機能も提供する

Triplitが提供するもの

  • Triplitは、サーバーとブラウザ間でデータをリアルタイム同期するオープンソースのデータベース
  • アプリにTypescriptパッケージとして追加できる同期データストアを提供する
  • サーバーにデータを保存し、クライアントのクエリを賢く同期する
  • Triplitはこの形態をフルスタックデータベースと呼んでいる
    • Local Firstコミュニティでの発表動画も提供されている: presentation

主な機能

  • リアルタイム同期

    • 増分アップデートをサポートする
    • 属性単位の競合解決を提供する
  • ローカルファーストの使い勝手

    • 完全なクライアント側データベースに基づくローカルキャッシュを提供する
    • 楽観的更新により、すべてのインタラクションを高速に感じられるようにする
    • オフラインモード、自動再接続、一貫性保証をサポートする
  • サーバー保存と運用

    • サーバー側の永続ストレージを提供する
    • 管理ダッシュボードを含む
    • SQLite、IndexedDB、LevelDB、Memoryなどのプラガブルストレージプロバイダーをサポートする
  • データモデルとAPI

    • 複雑なデータモデル向けのリレーショナルクエリをサポートする
    • スキーマを通じてデータ安全性とTypescriptの自動補完を提供する
    • vanilla JavascriptとReactで、クエリ・変更のためのシンプルなAPIを提供する
  • コラボレーションとセキュリティ

    • サーバーで読み取りと書き込みの両方について権限を強制する
    • CRDTsベースのコラボレーション・マルチプレイヤー機能を提供する
    • デルタパッチを使ってネットワークトラフィックを減らし、低レイテンシを目指す
    • 失敗した更新に対してロールバックとリトライを管理する

モノレポ構成

  • TriplitDB: ブラウザ、Node、Deno、React NativeなどのJS環境で動作するよう設計されたDBで、ネットワーク上の複数ライターと一貫性を保ちながら、高速でライブ更新されるクエリを提供する
  • Client: ローカル・リモートのTriplitDBとやり取りするブラウザライブラリ
  • CLI: プロジェクトのスキャフォールディング、フルスタック開発環境の実行、サーバーマイグレーションなどのためのコマンドラインツール
  • React: @triplit/client用Reactバインディング
  • Svelte: @triplit/client用Svelteバインディング
  • Console: Triplitプロジェクトのデータを表示・変更し、スキーマを管理するアプリ
  • Server: Triplitクライアント間でデータを同期するNodeサーバー
  • Server-core: Triplitサーバーを作るためのプロトコル非依存ライブラリ
  • Docs: Nextraで作られたTriplitドキュメント
  • Types: Triplitプロジェクト群が共有する型
  • UI: shadcnベースの共有UIコンポーネント

クイックスタートの流れ

  • 新規プロジェクトはnpm create triplit-app@latest my-appで開始する
  • 既存プロジェクトには@triplit/cliを開発依存関係としてインストールしたうえで、npm run triplit initを実行する
  • my-app/triplit/schema.tsスキーマを定義する
    • 例ではtodosコレクションにidtextcompletedフィールドを定義する
    • completedはデフォルト値がfalseのBooleanフィールドとして設定される
  • npm run triplit devで開発用の同期サーバーを起動する
  • 開発サーバーは、アプリがサーバーと同期するために必要な環境変数を出力する
    • Viteの例ではVITE_TRIPLIT_SERVER_URL=http://localhost:6543
    • VITE_TRIPLIT_TOKEN=copied-in-from-triplit-dev

Reactの使用例と同期確認

  • Reactの例ではTriplitClientuseQueryを使用する
  • クライアントはスキーマ、サーバーURL、トークンを受け取って作成される
  • useQuery(client.query('todos'))todosクエリの結果を購読する
  • チェックボックス変更時にclient.updatecompleted値を反転する
  • アプリを起動したあと別のブラウザタブを開くと、データがリアルタイム同期されることを確認できる

ドキュメントと連絡チャネル

1件のコメント

 
GN⁺ 2024-06-27
Hacker News のコメント
  • Triplitをプロジェクト https://github.com/thanhnguyen2187/cryptaa で使ってみたが、期待どおりに動作した
    データモデルは、単一の中央DBを真実の源泉とするよりも、より分散型/P2Pに近い構想によく合っている。ただし、セルフホスティングクエリ言語には不満がある
    サーバー認証トークンの生成方法がドキュメントでは明確でなかったため、CLIの dev コマンドでトークンを作成した。システムサービスでトークンが平文ログに残るのはセキュリティ上望ましくないが、より大きなアクセス権限の問題が前提になるとは思う
    カスタムのクエリDSLはSQLのように UNIQUECOUNT といった表現力が足りず、一部の集計を自前で行う必要がある
    最近 Evolu https://www.evolu.dev/docs を見たが、スコープと機能が似ているように見える。Triplitには .subscribe() があり、Evoluにはない。EvoluはKyselyベースの型付きSQLなのでクエリがより馴染みやすく高度で、ブラウザではEvoluがOPFS上のSQLiteを使う一方、TriplitはIndexedDBを使っているようだ
    Redditに投稿した記事: https://www.reddit.com/r/sveltejs/comments/1dndpj8/cryptaa_a...

    • セルフホスティングのドキュメントは、設定がより明確になるよう整理中で、指摘してくれた点は助かる
      クエリについてはまだ集計がないがロードマップにはあり、インクリメンタルクエリエンジンを活用すれば面白い方向に進めると考えている
      たとえば1時間ごとに更新されるデータダッシュボードは、既存のシステム(Postgres、MongoDBなど)では毎回クエリを最初から再実行する必要があるが、Materializeに近い方法で新しいデータだけを処理すれば、はるかに効率よく更新し続けられる
      Evoluはまだ直接使ってみていないが、Discordで比較した人がいるかもしれない: https://triplit.dev/discord
    • Evoluを教えてくれてありがとう。TriplitとEvolu はどちらも興味深そうなので、両者の比較を見てみたい
    • Evoluも useQuery や分離された方式で subscribe をサポートしている
  • こうした優れたオフライン同期プロトコルを持つDBを使うとき、異なるクライアントバージョンを同時にアップグレードできない状況で、スキーマの進化をどう扱うのか気になる
    以前、モバイルのヘルスアプリでこの問題に苦労した背景がある

    • 新しいテーブルだけを作り、既存テーブルには互換性を壊す変更を加えないのがよい
      必要であれば、2つのバージョンに同時に書き込む二重書き込みを行うことになる
      SQL DBの互換性を壊す変更を無停止でライブマイグレーションするのと似ているが、切り替え時点が顧客次第なので、そのロジックをより長く維持する必要がある
      最新バージョンを調整するテーブルも重要で、破壊的変更がある場合は、遅れているクライアントにユーザーへアップグレードを促させるべきだ
      全クライアントでサポートする最小バージョンと連動させて、二重読み取り/書き込みをどれだけ維持するかも決められる
    • 短く言えば、スキーマの後方互換性を維持する方法が、互換性を保証する最も簡単なやり方だ
      Triplitは後方互換でない変更を作ると警告を出し、ドキュメントで確認できる: https://www.triplit.dev/docs/schemas/updating#pushing-the-sc...
      ただし時間がたつと、紛らわしい名前の多い雑然としたスキーマ定義が自然に生まれがちだ
      これを修正するソリューションはまだリリースしていないが、苦痛を減らすためのいくつかの取り組みを進めている。さまざまなアプローチの背景としてはCambriaの文書が素晴らしい: https://www.inkandswitch.com/cambria/
    • 一部のクライアント、たとえばサーバーは、非常に古いスキーマとも同期できる必要があると思う
      ユーザーがスマートフォンを2年間引き出しに入れたままにしていた可能性もあるので、各クライアントができるだけ早く自分自身をマイグレーションすればよい
    • 過去のマイグレーションを定義してサポートする組み込みの仕組みがあれば、キラー機能になりそうだ
      誰もがそれぞれ独自のマイグレーション管理方法を新たに作る必要がなくなる
  • クライアントがDBに直接書き込めることがどんなアプリで許容されるのか、そしてバックエンドロジックなしでどう耐えられるのか、よく理解できない
    SupabaseとFirestoreについても同じ疑問があり、何かを見落としている気がする

    • 現実の世界で作られるものの大半はビジネスロジックがほとんどなく、単なるCRUDに近い
      企業環境では当然その逆だが、それを無視した議論を見るともどかしい
      特にテック系Twitterで特定のスタックや作業方法を擁護する内容を見ると、ビジネスシステムを作ったことがなくCRUDだけを作ってきたのがあまりに明らかで、経験豊富な開発者がなぜ同意しないのか理解していないことが多い
    • Firebaseでコラボレーションアプリを作ったことがあるが、各ユーザーが自分のコメントやカードに対してできることを強く制限し、特定の操作権限だけを与えるような形なら、それなりに動作する
      バックエンドロジックが多いものにはあまり向いていないと思う
    • どちらもバックエンドで強制されるアクセス制御があるので、バックエンドロジックがまったくないわけではない
      Supabaseには、たとえば行レベルセキュリティという機能がある
      クライアントがSupabaseへリクエストを送ることはできるが、Supabaseがバックエンドで追加クエリを実行し、入ってきたリクエストが許可されるかを判断する
      簡単な例として、UserID カラムの値がリクエストした認証済みユーザーと同じ場合に限り、その行を読み書き・更新できるようにできる
  • ユーザー設定を Triplit に保存してきており、この設定は管理者が管理できる必要があった
    ユーザーにはアプリが常にローカルで動いているように感じられるべきで、インターネット品質もしばしば良くない一方、複数のデバイスを行き来して使い、管理者が他ユーザーの設定を見て管理する必要があったため、同期が必要だった
    全体として Triplit はフロントエンド開発者体験もサポートも非常に優れており、Issue や機能リクエストを見つけるとチームが非常に素早く対応してくれる
    高可用性デプロイについての答えが出れば、Postgres の代わりにより重要なデータも移す予定

  • なぜ AGPL ライセンスを選んだのか気になる

    • AGPL ライセンスにすることで Triplit を簡単にセルフホストできるようにしつつ、変更した人がその変更をコミュニティへ還元することを保証したかった
    • 使っている DB のせいで製品まで AGPL にしなければならないなら避けたい
  • Local First Discord サーバー https://localfirstweb.dev/ で YouTube 発表 https://www.youtube.com/playlist?list=PLTbD2QA-VMnXFsLbuPGz1... を見た気がするので、Show HN で見かけてうれしい
    TypeScript を使っていないので主な対象ではないかもしれず、Web と違って接続が不安定なモバイルアプリでローカルファーストを主に使っており、Flutter と Rust バックエンドを使用している
    ElectricSQL や PowerSync のような他のローカルファーストソリューションは、クライアントとサーバー DB を直接同期するため、クライアント/サーバーに対してより独立している
    CRDT ベースのソリューションも FFI でクライアントとサーバーから使える。例えば automerge は Rust なので、Flutter 側では flutter_rust_bridge 経由の FFI、Web では WASM、バックエンドでは Rust として使える
    Triplit は、異なるクライアント間の競合なしの解決というより、サーバーを真実の源泉とする、より古典的なクライアント・サーバー同期に見える
    なぜクライアントとサーバーにより独立した DB レイヤー方式ではなく、言語レベルのソリューションを選んだのか気になるし、今後 JS ベース以外の言語やフレームワークをサポートするのは難しそうに見える
    また Supabase と競争しようとしているようだが、Supabase も Postgres の DB レベル同期と CRDT を実験中なので https://news.ycombinator.com/item?id=33931971 追いつく可能性もありそう

    • Flutter や他のネイティブ対応についてはかなり考えてきており、特に Flutter の話はよく出る
      ただし出発点としては純粋な TypeScriptに集中することにした。市場が十分に大きく、PWA の未来を信じており、そこに集中してこそ最高の体験を作れると見ているため
      いつかはもっとプラットフォーム非依存なものを作ることになると思うが、時期ははっきりしない
      ElectricSQL と Supabase のチームはいずれも優秀で思慮深く、SQL 領域で成長し続けると思われるが、これがアプローチの最も根本的な違い
      Triplit は SQL を避けることで開発者に最高の体験を提供できると考えており、2つの哲学が共存する余地は十分にある
  • LWW なら、クライアントの情報量が演算数に対して線形に増えるのか気になる
    つまり、ユーザーが DB を多く変更するほど演算ログが増え続けるのか、それともチェックポイントを置くのか、ユーザーが1日に数百万回の演算を行う場合に容量面でどうスケールするのか気になる

    • Triplit は特定属性の変更履歴を保存するが、最新値インデックスのおかげでクエリは高速に保たれる
      ただし LWW レジスター自体が履歴保存を要求するわけではなく、長期間オフラインだったクライアントも効率的に同期できるようにするための現在の実装にすぎない
      1日100万演算までは、まだ完全に到達したとは言いにくいが、サーバーが権限を持つ利点がある
      将来的には Triplit サーバーが各クライアントの最終同期タイムスタンプを追跡し、Postgres が dead tuple を VACUUM 処理する方式に似た形で、履歴を段階的に枝刈りできる
  • Tauri で使えるように Rust バインディングがあるとよさそう
    Tauri の成長、近く登場するモバイルデバイス対応、最近の SQLite 人気まで合わせると、オフラインファーストアプリの隙間を埋め、多くの開発チームの標準的な選択肢になり得る

    • 似た同期ソリューションである ElectricSQL に Rust バインディングを追加しようとしている
      ElectricSQL は DB レイヤーで動作するので言語非依存で、サーバーで Rust を使っているため、Rust バインディングがクライアントとサーバーの両方で動作できる
      一緒に開発したければ知らせてほしい
    • Tauri はネイティブ Web レンダラーを使うものではないかと思う
      それなら Triplit はそのまま動作するはず
  • React Native アプリでしばらく Triplit を使ってきており、とてもよく動く
    強くおすすめするし、自分に必要な条件をすべて満たした唯一のローカルファースト DBだった
    適切で sane なクエリ言語(SQL ではない)、優れた TypeScript サポート、オフライン対応、React Native 対応があり、オープンソースでセルフホスト可能な点もよい

  • 既存の PostgreSQL DB と一緒に使うことはできないのか気になる

    • 現時点ではできないが、Postgres のレプリケーションプロトコルと WAL2JSON を使って双方向同期を行う内部ツールがある
      まだ公開できる準備は整っていないが、近いうちに人々が試せるようにしたい
    • 既存の Postgres と連携する ElectricSQL を見てみるとよい
      自分もそちらを使う方向に傾いている