Origin Private File System を基盤とするブラウザ内の SQLite Wasm

SQLite を使用して、ウェブですべてのストレージニーズを高パフォーマンスで処理します。

SQLite は、人気のあるオープンソースの軽量な埋め込みリレーショナル データベース管理システムです。多くのデベロッパーは、構造化された使いやすい方法でデータを保存するためにこれを使用しています。SQLite はサイズが小さくメモリ要件が低いため、モバイル デバイス、デスクトップ アプリケーション、ウェブブラウザでデータベース エンジンとして使用されることがよくあります。

SQLite の主な機能の 1 つは、サーバーレス データベースであるということです。つまり、動作に個別のサーバー プロセスは必要ありません。代わりに、データベースはユーザーのデバイス上の単一のファイルに保存されるため、アプリケーションに簡単に統合できます。

SQLite のロゴ。

Web Assembly ベースの SQLite

Web Assembly(Wasm)に基づく非公式の SQLite バージョンがいくつかあり、sql.js など、ウェブブラウザで使用できます。sqlite3 WASM/JS サブプロジェクトは、SQLite プロジェクトに正式に関連付けられた最初の取り組みであり、ライブラリの Wasm ビルドをサポートされている SQLite 配信ファミリーの確立されたメンバーにします。このプロジェクトの具体的な目標は次のとおりです。

  • 使用の観点から可能な限り C に近い低レベルの sqlite3 API をバインディングします。
  • 低レベルの API に直接対応する、sql.jsNode.js スタイルの実装に似た、より高レベルのオブジェクト指向 API。この API は、低レベル API と同じスレッドから使用する必要があります。
  • Worker ベースの API で、Worker メッセージを介して以前の API と通信します。これは、ローレベルの API をワーカー スレッドにインストールし、ワーカー メッセージ経由で通信するメインスレッドでの使用を目的としています。
  • Worker API の Promise ベースのバリエーション。スレッド間の通信をユーザーから完全に隠します。
  • 利用可能な JavaScript API(オリジン プライベート ファイル システム(OPFS)など)を使用した永続的なクライアントサイド ストレージのサポート。

SQLite Wasm と送信元のプライベート ファイル システム永続バックエンドの使用

npm からライブラリをインストールする

次のコマンドを使用して、npm から @sqlite.org/sqlite-wasm パッケージをインストールします。

npm install @sqlite.org/sqlite-wasm

Origin のプライベート ファイル システム

オリジンのプライベート ファイル システム(OPFS、File System Access API の一部)は、データに非常に高いパフォーマンスでアクセスできる特別なサーフェスで拡張されています。この新しいサーフェスは、ファイルのコンテンツへのインプレース書き込み専用アクセスを提供するという点で、既存のサーフェスと異なります。この変更により、フラッシュされていない変更を一貫して読み取る機能と、専用ワーカーで同期バリアントを使用できるようになり、パフォーマンスが大幅に向上し、新しいユースケースのブロックが解除されます。

ご想像のとおり、プロジェクトの目標の最後のポイントである、利用可能な JavaScript API を使用した永続的なクライアントサイド ストレージのサポートには、データベース ファイルへのデータの永続化に関する厳格なパフォーマンス要件が伴います。ここで、オリジンのプライベート ファイル システム、具体的には FileSystemFileHandle オブジェクトの createSyncAccessHandle() メソッドが使用されます。このメソッドは、ファイルの同期読み取りと書き込みに使用できる FileSystemSyncAccessHandle オブジェクトに解決される Promise を返します。このメソッドは同期的であるため、パフォーマンス上の利点がありますが、そのため、オリジンのプライベート ファイル システム内のファイルに対して専用の ウェブワーカー内でのみ使用でき、メインスレッドをブロックすることはできません。

必要なヘッダーを設定する

ダウンロードした SQLite Wasm アーカイブには、sqlite3 WASM/JS ビルドを構成する sqlite3.js ファイルと sqlite3.wasm ファイルが含まれています。jswasm ディレクトリにはコア sqlite3 の成果物が含まれ、最上位ディレクトリにはデモアプリとテストアプリが含まれます。ブラウザは file:// URL から Wasm ファイルを提供しないため、これを使用してビルドするアプリにはウェブサーバーが必要です。そのサーバーは、ファイルを提供するときにレスポンスに次のヘッダーを含める必要があります。

これらのヘッダーが必要な理由は、SQLite Wasm が SharedArrayBuffer に依存しており、これらのヘッダーの設定がセキュリティ要件の一部であるためです。

DevTools でトラフィックを調べると、次の情報が表示されます。

上記の 2 つのヘッダー(Cross-Origin-Embedder-Policy と Cross-Origin-Opener-Policy)が Chrome DevTools でハイライト表示されています。

Speedtest

SQLite チームは、非推奨の Web SQL と比較して、WebAssembly 実装に関するベンチマークを実施しました。これらのベンチマークは、SQLite Wasm が一般的に Web SQL と同程度の速度であることを示しています。少し遅くなる場合もあれば、少し速くなる場合もあります。すべての詳細は結果ページで確認できます。

スタートガイドのコードサンプル

前述のように、Origin プライベート ファイル システム永続性バックエンドを使用する SQLite Wasm は、Worker コンテキストから実行する必要があります。ライブラリは、これらの処理をすべて自動的に処理し、メインスレッドから直接使用できます。

import { sqlite3Worker1Promiser } from '@sqlite.org/sqlite-wasm';

(async () => {
  try {
    console.log('Loading and initializing SQLite3 module...');

    const promiser = await new Promise((resolve) => {
      const _promiser = sqlite3Worker1Promiser({
        onready: () => {
          resolve(_promiser);
        },
      });
    });

    console.log('Done initializing. Running demo...');

    let response;

    response = await promiser('config-get', {});
    console.log('Running SQLite3 version', response.result.version.libVersion);

    response = await promiser('open', {
      filename: 'file:worker-promiser.sqlite3?vfs=opfs',
    });
    const { dbId } = response;
    console.log(
      'OPFS is available, created persisted database at',
      response.result.filename.replace(/^file:(.*?)\?vfs=opfs$/, '$1'),
    );

    await promiser('exec', { dbId, sql: 'CREATE TABLE IF NOT EXISTS t(a,b)' });
    console.log('Creating a table...');

    console.log('Insert some data using exec()...');
    for (let i = 20; i <= 25; ++i) {
      await promiser('exec', {
        dbId,
        sql: 'INSERT INTO t(a,b) VALUES (?,?)',
        bind: [i, i * 2],
      });
    }

    console.log('Query data with exec()');
    await promiser('exec', {
      dbId,
      sql: 'SELECT a FROM t ORDER BY a LIMIT 3',
      callback: (result) => {
        if (!result.row) {
          return;
        }
        console.log(result.row);
      },
    });

    await promiser('close', { dbId });
  } catch (err) {
    if (!(err instanceof Error)) {
      err = new Error(err.result.message);
    }
    console.error(err.name, err.message);
  }
})();

デモ

上記のコードの動作については、デモをご覧ください。Glitch のソースコードもぜひご覧ください。以下の埋め込みバージョンでは OPFS バックエンドが使用されていませんが、別のタブでデモを開くと使用されます。

オリジンの非公開ファイル システムをデバッグする

SQLite Wasm のオリジン プライベート ファイル システムの出力をデバッグするには、OPFS Explorer Chrome 拡張機能を使用してください。

Chrome ウェブストアの OPFS Explorer。

拡張機能をインストールしたら、Chrome DevTools を開き、[OPFS Explorer] タブを選択します。これで、SQLite Wasm がオリジンの非公開ファイル システムに書き込む内容を検査できるようになります。

デモアプリの Origin Private File System の構造を示す OPFS Explorer Chrome 拡張機能。

DevTools の OPFS Explorer ウィンドウでファイルを選択すると、ローカル ディスクに保存できます。その後、SQLite Viewer などのアプリを使用してデータベースを検査し、SQLite Wasm が実際に期待どおりに動作することを確認できます。

SQLite Wasm デモからデータベース ファイルを開くために使用される SQLite Viewer アプリ。

ヘルプとフィードバック

SQLite Wasm は SQLite コミュニティによって開発、保守されています。サポート フォーラムで検索したり、投稿したりして、ヘルプやフィードバックを入手してください。詳細なドキュメントは SQLite のサイトで確認できます。