WebRTC DataChannel と OPFS と sqlite-wasm を組み合わせた P2P SNS 試作 その3
前回までの記事


OPFS と sqlite-wasm でローカルのデータを永続化
OPFS と sqlite-wasm に関しては正直殆どをAIに書かせた。とはいえ、AIにやらせると一発では実装できなかったため何度か試行錯誤した。
sqlite-wasm には公式の README.md にコードサンプルがある。
const promiser = await new Promise((resolve) => { const _promiser = sqlite3Worker1Promiser({ onready: () => resolve(_promiser), }); }); log('Done initializing. Running demo...'); const configResponse = await promiser('config-get', {}); log('Running SQLite3 version', configResponse.result.version.libVersion); const openResponse = await promiser('open', { filename: 'file:mydb.sqlite3?vfs=opfs', });
await promiser() の引数のオブジェクトに filename: 'file:mydb.sqlite3?vfs=opfs' と指定しているが、これで自動的に OPFS を使うようになるらしい。とても簡単で便利だ。
これを実際にAIに組み込ませたら、以下のように動的インポートするコードを書いてきた。
const base = (import.meta.env.BASE_URL || "/").replace(/\/?$/, "/"); const promiserModule = (await import( /* @vite-ignore */ `${base}sqlite3-worker1-promiser-bundler-friendly.mjs` )) as { default: (config?: Record<string, unknown>) => Promise<WorkerExec>; }; const createPromiser = promiserModule.default; const worker = await createPromiser({ locateFile: (file: string) => `${base}${file}`, proxyUri: `${base}sqlite3-opfs-async-proxy.js`, }); await worker("open", { filename: "file:local-feed.db?vfs=opfs", flags: "c", });
この部分は何回もAIに修正させたので、この呼び出し方が本当に正しいのかはいまいち判然としない。 しかしとりあえずは動くようだ。
WASM を静的ファイルとして Web 上に置いて、HTMLからそれを読み込む構成だから動的 import をしているのだと思われる。公式と異なる方法なのでちょっと不安ではあるが動いている。
上記の worker オブジェクトが作られたら、以下のように SQL を流せる
await worker("exec", { sql: ` PRAGMA journal_mode = WAL; CREATE TABLE IF NOT EXISTS users ( id TEXT PRIMARY KEY, nickname TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS posts ( id TEXT PRIMARY KEY, user_id TEXT NOT NULL, content TEXT NOT NULL, created_at INTEGER NOT NULL, nickname_snapshot TEXT, FOREIGN KEY(user_id) REFERENCES users(id) ); `, });
とか
const byIdRes = (await worker("exec", { sql: "SELECT id, nickname FROM users WHERE id = ? LIMIT 1", bind: [storedId], rowMode: "array", returnValue: "resultRows", })) as ExecResult | undefined;
のような感じだ。
OPFS を使う際の注意点として、公式ドキュメントにもあるが、
[!Warning]
For this to work, you need to set the following headers on your server:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
のように sqlite-wasm を呼ぶ側のファイルのレスポンスヘッダーを適切に設定する必要がある。
この形式だと ORM とか migration とかはすんなりとは使えないかも知れない。サーバーサイドのテーブル定義を更新するのとは違って、クライアントの状態に応じて良い感じに migration できる必要があり、スマホアプリやデスクトップアプリで SQLite を使った経験が無いとややこしく感じるかも知れない。
とはいうものの migration に関しては結構簡単に作れるので AI を使って自作したほうが早い気がする。
感想
P2Pのサービスは接続している人がいなければ成り立たない。そのため、いかに繋がりっぱなしにしてもらうか、という課題が発生する。
既存のP2Pサービスは、アップロードした人が優先的にダウンロードを受け取れる仕組みだとか、IPFSみたいにファイルをホストし続ければ報酬がもらえるとかの仕組みでインセンティブを設計している。
しかしそういう仕組がなければ、接続しっぱなしにするための動機が生まれず、データを非同期的にネットワーク上に維持することが難しくなる。
P2Pを使ったら何か面白いSNSが生まれるのではと思ったが、完全に浅はかだった。自分の頭の悪さを再確認するだけで終わった。