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

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

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

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

SQLite のロゴ。

Web アセンブリに基づく SQLite

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

  • 使用方法の点で C 言語の API にできるだけ近い低レベルの sqlite3 API をバインドします。
  • sql.jsNode.js スタイルの実装に似た、低レベル API に直接アクセスする高レベルのオブジェクト指向 API。この API は、低レベル API と同じスレッドから使用する必要があります。
  • Worker メッセージを介して以前の API と通信する Worker ベースの API。これはメインスレッドでの使用を想定しており、下位レベルの API はワーカー スレッドにインストールされ、ワーカー メッセージを介して通信します。
  • ユーザーからスレッド間の通信の側面を完全に隠す、Worker API の Promise ベースのバリアント。
  • Origin Private File System(OPFS)など、利用可能な JavaScript API を使用した永続的なクライアントサイド ストレージのサポート。

Origin Private File System 永続バックエンドでの SQLite Wasm の使用

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

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

npm install @sqlite.org/sqlite-wasm

オリジン プライベート ファイル システム

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

プロジェクトの目標の最後のポイントである「利用可能な JavaScript API を使用した永続的なクライアントサイド ストレージのサポート」には、データベース ファイルへのデータの永続化に関する厳しいパフォーマンス要件が伴います。ここで、オリジン プライベート ファイル システム、より具体的には FileSystemFileHandle オブジェクトの createSyncAccessHandle() メソッドが役に立ちます。このメソッドは、ファイルを同期的に読み書きするために使用できる FileSystemSyncAccessHandle オブジェクトに解決される Promise を返します。このメソッドは同期処理であるため、パフォーマンス上のメリットがありますが、メインスレッドがブロックされないように、オリジン プライベート ファイル システム内のファイル専用の Web Worker 内でのみ使用できます。

必要なヘッダーの設定

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

  • Cross-Origin-Opener-Policy は、ブラウジング コンテキストを同一オリジンのドキュメントのみに分離するsame-origin ディレクティブに設定されます。クロスオリジン ドキュメントは同じブラウジング コンテキストに読み込まれません。
  • Cross-Origin-Embedder-Policyrequire-corp ディレクティブに設定されているため、ドキュメントは同じオリジンからのリソース、または別のオリジンから読み込み可能として明示的にマークされたリソースのみを読み込むことができます。

これらのヘッダーが必要なのは、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 Private File System 永続性バックエンドを使用する 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);
  }
})();

デモ

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

Origin Private File System をデバッグする

SQLite Wasm の Origin Private File System の出力をデバッグするには、OPFS Explorer Chrome 拡張機能を使用します。

Chrome ウェブストアの OPFS Explorer。

拡張機能をインストールしたら、Chrome DevTools を開き、[OPFS Explorer] タブを選択します。これで、SQLite Wasm がオリジン プライベート ファイル システムに書き込む内容を検査する準備が整いました。

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

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

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

ヘルプを利用する、フィードバックを送信する

SQLite Wasm は、SQLite コミュニティによって開発および保守されています。サポート フォーラムで検索したり投稿したりして、ヘルプを利用したりフィードバックを送信したりできます。完全なドキュメントは、SQLite サイトで入手できます。