2 ポイント 投稿者 GN⁺ 2024-10-16 | 1件のコメント | WhatsAppで共有
  • 最新の WASM 対応ブラウザで sqlite3 を直接使いたい開発者向けに、WebAssembly・JavaScript API ドキュメントを一か所に集約したハブ
  • すぐに試すなら 3 ステップ HOWTO から始められ、ダウンロード・npm モジュール・ビルド・Emscripten の詳細ドキュメントでインストールと配布の流れを確認できる
  • API ドキュメントは、読み込み方式、クライアントコードのレシピ、永続ストレージ、JS API における C 構造体、仮想テーブルやテーブル値関数の作成までを扱う
  • SQLime、Evolu、SQLiteNext、sqlite-wasm-esm、sqlite-wasm-http といった実際の利用例と、sql.js、wa-sqlite、absurd-js などの 先行プロジェクトもあわせて整理
  • ブラウザ SQLite、OPFS、IndexedDB、WASI 関連資料へリンクし、Web 環境で SQLite を使うための実装選択肢を素早く比較できる

sqlite3 WASM ドキュメントの範囲

利用例と関連エコシステム

  • このプロジェクトを使うサードパーティプロジェクト

    • SQLime: データベースブラウザのインターフェースを提供
    • Evolu: プライバシー、使いやすさ、ベンダーロックインなしを重視したローカルファースト(local-first)プラットフォーム
    • SQLiteNext: このプロジェクトを next.js と統合するデモ
    • sqlite-wasm-esm: Vite ツールチェーンでこのプロジェクトを使う方法を実演
    • sqlite-wasm-http: HTTP 経由で直接提供されるデータベースへの読み取り専用アクセスを提供する SQLite VFS
  • 先行研究と隣接プロジェクト

    • Alon Zakai の sql.js: WebSQL を除けば、ブラウザで sqlite3 を直接利用した最初期の事例として知られ、2012年のコミットまでさかのぼる
    • Roy Hashimoto の wa-sqlite: sqlite3 データベース向け OPFS storage の初期実装として知られ、JS 側の SQLite VFS 関連作業も含む
    • James Long の absurd-js: sqlite3 データベースを IndexedDB データベース内に保存する方法を実演
    • postgres-wasm: ブラウザで Postgres database server を実行
    • Jaccwabyt: WASM 上でホストされた C 構造体を JS コードで操作する小さな JS ライブラリで、このプロジェクトの OPFS sqlite3_vfs 実装を支援するために作られた
    • CoWasm: “Collaborative WebAssembly for Servers and Browsers” であり、demo app に sqlite3 shell アプリケーションの WASM ビルドを含む
    • absurder-js: Nicholas G. Piesco が absurd-js を再構想したプロジェクトで、SQLite forum announcement もある
    • sqlite-wasm-rs: wasm32-unknown-unknown ビルド向けの Rust 製 SQLite バインディングを提供
  • 外部ドキュメントと記事

1件のコメント

 
GN⁺ 2024-10-16
Hacker News のコメント
  • ブラウザ上でインメモリ SQLiteを動かしつつ、Litestream や Cloudflare Durable Objects が使っている方式のように、WAL ログのコピーをサーバーへストリーミングすると面白そう
    WebSocket を使ってもいいし、断続的な fetch() POST でも可能に見える。その後の訪問時には、サーバー側のデータを使ってクライアントの SQLite を再び埋める方式になる
    ただし SQLite フォーラムの投稿 を見ると、標準の SQLite WASM ビルドでは WAL モードが外されているので、カスタムビルドが必要になりそう

    • ここには「そうは動かない」という層がいくつもある
      インメモリ SQLiteは WAL を使わないし、ブラウザ Wasm は SQLite の WAL モードが想定する共有メモリ API のようなものをサポートしていない
      Litestream には非常に厳密な WAL 構成が必要で、ネイティブ SQLite の標準構成ではたまたまうまく合っているが、Wasm で再現するのは難しい
      Cloudflare Durable Objects は Litestream に着想を得ている可能性はあるが、実際の動作はかなり異なるし、LiteFS や Turso も同様
      SQLite の変更分をストリーミングするという大きなアイデア自体は可能だが作業量が多く、ブラウザ Wasm の並行性モデルのため実装は厄介
      そのフォーラム投稿は以前に自分が書いたもので、サーバー側の Wasm SQLite ビルドでは WAL を動かしたが、ブラウザで使える、あるいは実現可能な選択肢ではなかった
    • 2022年8月にこれと似た概念実証を作り、Twitter の DM を送ったが返事はなかった。ハンドルは justjs14
      当時のコードは探す必要があるが、ブラウザで VFS 付きの SQLite を Wasm にコンパイルして DB を開き、変更後に WAL をサーバーへプッシュ/プルできるようにしていた。手動でも WebRTC でもブラウザ間同期が可能で、ブラウザクライアントに GitHub トークンを渡せば GitHub Pages でも動作する
      興味があれば、また最初から実行できるようにしてみてもよい。当時このアプローチでかなり実験していて、少なくとも一部のアプリケーションには有用になり得ると考えている
      プル機能だけのデモもここにある: https://just.billywhizz.io/sqlite/demo/#https://just.billywhizz.io/sqlite/demo/db/chinook.db
    • ここにある他の考えとは違って、WAL コピーのストリーミングは技術的に可能だと思う。ただし VFS の中で実装する必要がある
      「共有メモリ」は SharedArrayBuffer にできるし、DB 接続を一度に1つだけ開くなら通常のバッファでも可能。たとえば、すでに一般的な SharedWorker の中でそのようにできる
      実装は単純ではないだろうが、明らかに可能。実際には、より大きな問題は行単位ではなくブロック単位の変更をキャプチャする点にある
      そのため DB 全体の複製には使えるが、一部の行を他のユーザーと共有するような部分同期は難しい
      部分同期のために行単位の変更が必要なら、トリガーや SQLite セッション拡張のようなものを使う必要がある: https://sqlite.org/sessionintro.html
      PowerSync ではクライアント側トリガーを使っており、トリガーの保守作業以外には大きな欠点は見つからなかった
    • https://electric-sql.com/ のようなものを考えているのかな?
    • この「ハイドレーション」というアイデアを完全に理解したわけではないが、最近 Wasm を活用してバックエンドロジックの一部をブラウザで実行することに関心が出てきた
      たとえば一般的なテンプレートレンダリングや一部の Htmx エンドポイントを Wasm で動かせば、普通のサーバーサイド Web フレームワークでも、OP が言っているものに近い SQLite Wasm ストアでオフライン機能を提供できる
      Django の完全な例: https://github.com/m-butterfield/django_webassembly
      マクロでクライアント/サーバーを分離した最小限の Rust 例: https://github.com/richardanaya/wasm-service
      Wasm なしで同じようなことを、より多くのマクロで行う Clojure Electric: https://github.com/hyperfiddle/electric
      最近の Microsoft 系の Web ツールには B で始まるものもある
      ここ数年で Python エコシステムの意外に多くの部分が Wasm 対応を得て、このアプローチに活用できるようになった。明らかな機械学習関連ライブラリだけでなく、Pygame や Pyxel のようなゲームライブラリもあり、SDL2 が対応を追加したので、マルチタッチ UI ライブラリである Kivy の対応も遠くなさそう
      どんなコンピュータでもコードを実行し、必要なら Web ブラウザのサンドボックス内でプラットフォームの官僚主義や手数料を避けられる、新しい可能性が多くありそう
  • ドキュメントには、これらのコンポーネントは 3.40 で公開ベータとして初めて登場し、コミュニティからのフィードバックに応じて 3.41 で API を安定化する予定だと書かれている
    ただし最新の SQLite リリースは 2024-08-13 の 3.46.1 なので、おそらく API の安定化はすでに済んでいて、ドキュメントが更新されていないだけのようだ
    SQLite チームが WASM 版をまとめた公式 npm パッケージを出してくれれば、よい配布手段になりそうだ。あとで見ると、返信によればすでに存在するらしい
    個人的にいちばん気に入っている SQLite-in-WASM は今でも Pyodide 版だ。公式 SQLite 実装よりずっと前から存在していて、Pyodide を使うと Python 標準ライブラリの一部として WASM SQLite が付いてくる
    https://lite.datasette.io/ ではその形で使っており、https://pyodide.org/en/stable/console.html でも試せる
    sqlite3.connect(':memory:')select sqlite_version() を実行すると 2022-06-25 の 3.39.0 が返るので、Pyodide はバージョンアップが必要そうだ。このバージョンは emscripten から引き継いでいるようだ: https://github.com/emscripten-core/emscripten/blob/main/tools/ports/sqlite3.py

    • API の安定化はその通り。次にコンピューターの前に座ったら、その文言を更新してみる
      ただし SQLite チームが公式 npm パッケージを出すことはないだろう。私たちは純粋な JS だけを配布しており、言語外ツールのサポートという泥沼に入ることは断固として拒んでいる。プロジェクトメンバーの誰もそうしたツールを使っていない
      Thomas Steiner がメンテナンスしている「公式承認済み」の npm ビルドはサポートしているが、どの JS フレームワークも積極的な開発対象にはしていない
      特定のフレームワーク、npm を含む、を直接サポートすると、そのフレームワークを推奨しているという印象を与えかねず、私たちはサードパーティープロジェクトの推奨を積極的に避けている
    • SQLite チームはすでにしばらく前からそれをやっているようだ。JS モジュールで @sqlite.org/sqlite-wasmjsdelivr から取り込み、sqlite3-bundler-friendly.mjssqlite3.wasm をロードすればよい
      そうすれば SQLite の C API と oo1 オブジェクト指向 API を使え、通常の type=module スクリプトタグや Deno でも動作する
      サンプル HTML はここにある: https://github.com/Ciantic/experimenting-sqlite-wasm/blob/main/standalone.html
    • https://github.com/sqlite/sqlite-wasm
      sqlite-wasm は Pyodide よりはるかに速くロードされるので、Python が不要なら前者のほうがよい選択だ
    • DuckDB WASM は Pyodide とは独立して使え、SQLite 拡張も可能だ
      ただし、かなり制限があるようだ。sqlite_version() がないのでバージョン確認すらできず、リポジトリのバージョンは 3.38.1 とかなり古い
      現在の DuckDB Web シェルは、昨日出た 1.1.2 向けの SQLite 拡張がまだリリースされていないため、SQLite 拡張をロードできない。以前のバージョンは最近更新された WASM エディションで動作する
      空間拡張、GDAL、ベクトル検索なども組み込める: https://github.com/duckdb/duckdb-wasm/releases/tag/v1.29.0
      自前で「SQL Web シェル」を作るのもそれほど難しくはなかったが、ドキュメントは十分に完全とは言えなかった
      https://github.com/duckdb/sqlite_scanner/blob/main/src/sqlite/sqlite3.h
    • そのページの FAQ を読んでみると役に立つかもしれない
  • Go で使う場合は、ncruces の Wasm SQLite パッケージ https://github.com/ncruces/go-sqlite3 が本当に気に入っている
    cznic の Go パッケージも素晴らしいが、この Wasm 版は OpenBSD のような環境でもうまく動作する

  • WebSQL は単に SQLite であるべきだったし、そうであればオフラインファーストやアプリ全般のストレージエコシステムはずっと良くなっていただろう
    今からでも可能性はあるだろうか。SQLite を抽象化して過剰に仕様化するより、ブラウザーがサポートすべき SQLite API バージョンを仕様化し、定期的にバージョンを上げていく形にはできないだろうか?

    • ブラウザーで「定期的にバージョンを上げる」方式は、かなり問題になり得る
      Web の核心のひとつは、可能な限り既存のものが動き続けるべきだという点で、そうした破壊的変更は難しい
      仕様がいくつかのバージョンを列挙し、Web ページが選べるようにしたとしても、ブラウザーはもはやサポートされていないかもしれない古い SQLite バージョンを維持するという厄介な負担を抱えることになる。各バージョンはさらに 1MB ほどを占める
      それなら、今のように各 Web サイトが自分の選択の代償を負うようにしておくほうがよいのではないかと思う
      共通リソースを繰り返しダウンロードすることが心配なら、それはすでに他の CDN でも受け入れられている問題だ。共通依存関係の共有キャッシュ解決策は、SQLite だけに限定されるものよりはるかに価値が大きいだろう
      現在の方式のほうが、ブラウザー提供版より良さそうに見える
    • SQLite は好きだが、すべてのブラウザーに入れるにはいくつかの特異な挙動があることは認めざるを得ない。なので、SQLite 風の API くらいのほうがよさそうだ
    • WebSQL が定着しなくて、むしろ幸いだったと思う
      今では必要なときに 410KB の圧縮 WASM blob として最新の SQLite を取得できる。ブラウザーが強制する SQLite バージョンに縛られていたら、今ごろ 10 年前のバージョンを使っていたかもしれない
  • ページをざっと見ただけでは、ユースケースがよく分からない
    もっと具体的には、これは IndexedDB の代替になり得るのか? データは永続化されるのか、それとも SQLite ファイルを File System API や IndexedDB/localStorage に直接保存する必要があるのか?

    • 紹介ページによると、このプロジェクトの目標の一つは、可能な範囲で利用できる JS API を使って クライアント側の永続ストレージをサポートすること
      現時点では Origin-Private FileSystem(OPFS)と、非常に限定的ながら window.localStoragewindow.sessionStorage バックエンド経由の保存を含む
  • 最近 ローカルファースト領域にかなり関心があるが、ブラウザ内蔵のストレージ API、特に IndexedDB を使う場合と比べると、SQLite を埋め込むのはかなり重く感じる
    主要なオープンソースライブラリも、おおむね内蔵 API を使っているように見える
    WebAssembly でさらに別の実行ファイルを入れようとするより、ブラウザネイティブのストレージ上に SQLite 風の API と同期を提供するオープンソースの解法を見てみたい

    • SQL は IndexedDB API より使いやすいとも言えるし、たとえば if() フィルタの代わりに WHERE 句を使えば メモリと CPU の使用を抑えられるかもしれない
    • TinyBase に興味があるかもしれない。私が作ったライブラリです
  • 公式ダウンロードページから取得して mjs ファイルと「bundler-friendly」ファイルをすべて取り除いても、最小の SQLite Wasm 依存関係は約 1.3MB になる
    ブラウザアプリでは少し大きめに見えるが、最近の Wasm は他の場所でも実行されるので、そうした環境ではより筋が通るかもしれない

    • それでも圧縮はよく効く。sqlite3.js と Wasm は元のサイズで 1.3MB だが、JS を最小化し、2つのファイルを Brotli で圧縮すると 410KB まで下がる
    • 現代の Web アプリで 1.3MB は十分に妥当なサイズだと思う。特にサイト初回訪問後はキャッシュされるはず
      ユーザー設定を保存する程度なら当然 SQLite をダウンロードする必要はないが、完全なデータベースが利点になる作業なら、1MB を気にしすぎて無駄に車輪の再発明をする必要はない
      他のコメントが正しければ、ネットワーク上ではそもそも 1.3MB ですらないはず
    • ブラウザにはすでに IndexedDB が組み込まれている、という点と合わせて考慮すべき要素
      それでも Figma や Photoshop のようなアプリ、機械学習アプリのように、アプリケーションコードとデータがもともと大きいブラウザアプリでは、1.3MB は大きな追加負担ではないかもしれない
      Wasm のパースは JS よりかなり速いという点も考慮に値する。この主張の出典はいま見つけられないが、関連する良い記事が少なくとも一つはあった
      https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API
    • WebAssembly のサンドボックスモデルを考える必要がある。理論上、プログラム、ここでは SQLite は、実行に適したどこででも動かせる
      ローカルかもしれないし、中央サーバーかもしれないし、近くの エッジかもしれない
    • サーバー側では、同時実行性とファイルロックを別の形で扱う、別の Wasm SQLite ビルドが必要になる可能性が高い
      また WASI はまだ答えには程遠い。SQLite amalgamation は WASI で問題なくビルドできるが、同時実行性は未解決の問題
      Wasm ベースの SQLite ドライバを使える状態にするために、VFS を一から作る必要があった
      https://github.com/ncruces/go-sqlite3/blob/main/vfs/README.md
  • しばらく Rust エコシステムでこれを動かそうとしていたが、blessed.rs の SQL ラッパーである rusqlitesqlx はまだこれを活用できないようだった
    自分でラップするのも難しく、emscripten Wasm コードを wasm32-unknown-unknown と噛み合わせて動作させる方法を見つけられなかった
    結局、何らかの形で JS ラッパーが必要になり、そうなるとそれらのクレートが期待するインターフェースを実装して JS から公開しなければならない。Rust でこれが解決されれば本当に良くなるはず

    • 関連する作業を進めている。興味がある、または貢献したいならここに参加すればよい: https://github.com/rhashimoto/wa-sqlite/discussions/154
      本質的には、wasm-bindgenextern C ヘッダーとして SQLite emscripten ビルドを取り込み、共有配列バッファへのアクセスを可能にするためマルチスレッドモードでコンパイルしつつ、VFS を Rust で再実装する必要がある
      その後は SQLite の行を生の Wasm バイトとしてアクセスできるようになり、Rust-SQLite スタイルのラッパーや統合を作れる
      接続プーリングのような便利機能は依然としてないだろうが、Wasm では DB を 排他モードで使うほうがよい可能性が高い
    • この SQLite ビルドを Rust の Diesel SQLite と連携して動作させたものがここにある: https://github.com/xmtp/diesel-wasm-sqlite
      今週 0.2 リリースを準備中で、いくつかの問題を調整する予定。それ以外は Diesel ORM をネイティブ同様に使える
    • emscripten Wasm コードと wasm32-unknown-unknown をうまく噛み合わせる部分については良いニュースがある
      Emscripten と Rust が Wasm 上でうまく連携できるようにする計画がある: https://github.com/rustwasm/wasm-bindgen/pull/4014#issuecomment-2356908451
    • 私もこれをかなり長く待っていた。1年ほど保留されている PR がいくつかあり、概念実証は見たが、実際に使えるものには程遠かった
  • SQLite の Wasm ビルドと Chicory ランタイムを使って、純粋な JVM 上で動作する SQLite ライブラリを作った: https://github.com/dylibso/sqlite-zero
    本番投入を目指したものというよりは実験に近いが、依存関係のない SQLite ツールを JVM エコシステムに持ち込むうえでは有用かもしれないと思う

    • ファイルシステムへのアクセスはどういう形なのか? WASI なのか?
  • 一般的な質問だが、どんな状況なら DB 全体を送ってブラウザ側でクエリを処理させるほうが有利なのか?
    言い換えると、従来のサーバーホスティング DB モデルよりユーザー体験を改善するために、これはいつ使うことになるのか?

    • 個人的には 静的ホスティングの Web サイトで使っているので、サーバーホスティングのデータベースはそもそも選択肢ではなかった
      適切なドライバを使えば、データベース全体を送る代わりに必要なデータベースのチャンクをストリーミングできる: https://github.com/mmomtchev/sqlite-wasm-http
      FTS データベース全体をダウンロードしなくても SQLite の全文検索ができる。検索語が十分短いと大部分は受け取るが、全部ではない
    • この記事タイトルを見たとき直感的に思い浮かんだのは、オフラインファーストアプリで PouchDB と CouchDB が相互作用する方式のようなものだ
      https://medium.com/offline-camp/couchdb-pouchdb-and-hoodie-as-a-stack-for-progressive-web-apps-a6078a985f18
    • オフライン利用では、大量のデータを扱うときに良いかもしれない
      オーディオライブラリから 3D モデリングソフトウェアまで考えられる。変更をローカルで作成して永続化し、その後、定期的に、または再びオンラインになったときにサーバー側と同期すればよい
    • 複数のサービスと直接通信するダッシュボードを作っている。自分のサーバーにはアクセス制御とトークン管理用のごく少量のデータだけがあり、実データは自分のサーバーには来ない
      こうしたアプリは クライアント側の組み込み DB によく合う候補だ