アプリ向けの高パフォーマンス ストレージ: Storage Foundation API

ウェブ プラットフォームは、ウェブ用に微調整された高性能アプリケーションを構築するために必要なツールをデベロッパーに提供するようになってきています。特に注目すべきは、WebAssembly(Wasm)により高速で強力なウェブ アプリケーションへの扉が開かれ、Emscripten などのテクノロジーにより、デベロッパーは試行済みのコードをウェブ上で再利用できるようになりました。この可能性を真に活用するには、デベロッパーはストレージに関して同じ能力と柔軟性を持つ必要があります。

そこで役立つのが Storage Foundation API です。Storage Foundation API は、高速かつ独自性のない新しいストレージ API です。パフォーマンスの高いデータベースの実装や大規模な一時ファイルの適切な管理など、ご要望の多かったウェブの新しいユースケースを実現します。この新しいインターフェースにより、デベロッパーはウェブに「独自のストレージを持ち込む」ことができるため、ウェブとプラットフォーム固有のコードの間の機能のギャップが小さくなります。

Storage Foundation API は、ごく基本的なファイル システムに似た設計になっているため、上位レベルのコンポーネントを構築できる汎用性、シンプルさ、パフォーマンスに優れたプリミティブが提供され、デベロッパーは柔軟に利用できます。アプリケーションは、ユーザビリティ、パフォーマンス、信頼性の適切なバランスを見つけて、ニーズに最適なツールを利用できます。

ウェブに別の Storage API が必要な理由

ウェブ プラットフォームには、デベロッパー向けにさまざまなストレージ オプションが用意されており、それぞれが特定のユースケースを念頭に置いて構築されています。

  • これらのオプションの一部は、Cookie や、sessionStoragelocalStorage のメカニズムで構成される Web Storage API など、非常に少量のデータしか保存できないことから、この提案とは明らかに重複していません。
  • その他のオプションは、File and Directory Entries APIWebSQL など、さまざまな理由で非推奨になっています。
  • File System Access API にも同様の API サーフェスがありますが、この API は、クライアントのファイル システムとやり取りし、オリジンやブラウザの所有権の外部にあるデータにアクセスできるようにするために使用されます。この焦点の違いにより、セキュリティに関する考慮事項が厳しくなり、パフォーマンス コストが高くなります。
  • IndexedDB API は、一部の Storage Foundation API ユースケースのバックエンドとして使用できます。たとえば、Emscripten には IndexedDB ベースの永続ファイル システムである IDBFS が含まれています。ただし、IndexedDB は基本的に Key-Value ストアであるため、パフォーマンスに関する大きな制限があります。さらに、IndexedDB ではファイルのサブセクションに直接アクセスすることはさらに難しく、速度も遅くなります。
  • 最後に、CacheStorage インターフェースは幅広くサポートされており、ウェブ アプリケーション リソースなどのサイズの大きなデータを保存するように調整されていますが、値は変更できません。

Storage Foundation API は、アプリケーションのオリジン内で定義された変更可能な大きなファイルを効率良く保存できるようにすることで、以前のストレージ オプションとすべてのギャップを埋めることを目的としています。

Storage Foundation API に推奨されるユースケース

この API を使用するサイトには、たとえば次のようなものがあります。

  • 大量の動画、音声、画像データを処理する生産性向上アプリまたはクリエイティブ アプリ。このようなアプリでは、セグメントをメモリに保持するのではなくディスクにオフロードできます。
  • Wasm からアクセス可能な永続ファイル システムに依存しており、IDBFS が保証できるパフォーマンスよりも高いパフォーマンスを必要とするアプリ。

Storage Foundation API とは

API は主に 2 つの部分で構成されています。

  • ファイル システム呼び出し。ファイルやファイルパスを操作する基本的な機能を提供します。
  • ファイル ハンドル: 既存のファイルに対する読み取りと書き込みのアクセス権を付与します。

ファイル システム呼び出し

Storage Foundation API には、window オブジェクト上に存在し、いくつかの関数を含む新しいオブジェクト storageFoundation が導入されています。

  • storageFoundation.open(name): 指定した名前のファイルが存在する場合は開き、それ以外の場合は新しいファイルを作成します。開かれたファイルで解決される Promise を返します。
  • storageFoundation.delete(name): 指定した名前のファイルを削除します。ファイルが削除されたときに解決される Promise を返します。
  • storageFoundation.rename(oldName, newName): ファイルの名前を古い名前から新しい名前にアトミックに変更します。ファイルの名前が変更されたときに解決される Promise を返します。
  • storageFoundation.getAll(): 既存のすべてのファイル名の配列で解決される Promise を返します。
  • storageFoundation.requestCapacity(requestedCapacity): 現在の実行コンテキストで使用する新しい容量(バイト単位)をリクエストします。使用可能な残りの容量で解決された Promise を返します。
  • storageFoundation.releaseCapacity(toBeReleasedCapacity): 現在の実行コンテキストから指定されたバイト数を解放し、残りの容量で解決される Promise を返します。
  • storageFoundation.getRemainingCapacity(): 現在の実行コンテキストで使用可能な容量で解決される Promise を返します。

ファイル ハンドル

ファイルの操作には、次の関数が使用されます。

  • NativeIOFile.close(): ファイルを閉じ、オペレーションの完了時に解決される Promise を返します。
  • NativeIOFile.flush(): ファイルのメモリ内の状態をストレージ デバイスと同期(つまりフラッシュ)し、オペレーションが完了したときに解決される Promise を返します。
  • NativeIOFile.getLength(): ファイルの長さ(バイト単位)で解決される Promise を返します。
  • NativeIOFile.setLength(length): ファイルの長さをバイト単位で設定し、オペレーションの完了時に解決される Promise を返します。新しい長さが現在の長さより小さい場合、ファイルの末尾からバイトが削除されます。それ以外の場合、ファイルはゼロ値のバイトで拡張されます。
  • NativeIOFile.read(buffer, offset): 指定されたバッファの転送結果であるバッファを使用して、指定されたオフセットにあるファイルのコンテンツを読み取ります。このバッファはデタッチされたままになります。転送されたバッファと正常に読み取られたバイト数を含む NativeIOReadResult を返します。

    NativeIOReadResult は、次の 2 つのエントリで構成されるオブジェクトです。

    • buffer: ArrayBufferViewread() に渡されたバッファを転送した結果です。ソースバッファと同じ型と長さです。
    • readBytes: buffer に正常に読み込まれたバイト数。エラーが発生した場合や、読み取り範囲がファイルの末尾を超えて範囲にある場合、この値はバッファサイズよりも小さくなることがあります。読み取り範囲がファイルの末尾を超えると、ゼロに設定されます。
  • NativeIOFile.write(buffer, offset): 指定されたバッファの内容を、指定されたオフセットでファイルに書き込みます。バッファはデータが書き込まれる前に転送されるため、切断されたままになります。転送されたバッファと正常に書き込まれたバイト数を含む NativeIOWriteResult を返します。書き込み範囲がその長さを超えると、ファイルが拡張されます。

    NativeIOWriteResult は、次の 2 つのエントリで構成されるオブジェクトです。

    • buffer: write() に渡されたバッファを転送した結果である ArrayBufferView。ソースバッファと同じタイプと長さです。
    • writtenBytes: buffer に正常に書き込まれたバイト数。エラーが発生した場合、これはバッファサイズより小さくなる場合があります。

完全な例

上記で紹介したコンセプトをより明確にするために、Storage Foundation ファイルのライフサイクルのさまざまな段階について、2 つの例で詳しく説明します。

オープニング、ライティング、リーディング、結び

// Open a file (creating it if needed).
const file = await storageFoundation.open('test_file');
try {
  // Request 100 bytes of capacity for this context.
  await storageFoundation.requestCapacity(100);

  const writeBuffer = new Uint8Array([64, 65, 66]);
  // Write the buffer at offset 0. After this operation, `result.buffer`
  // contains the transferred buffer and `result.writtenBytes` is 3,
  // the number of bytes written. `writeBuffer` is left detached.
  let result = await file.write(writeBuffer, 0);

  const readBuffer = new Uint8Array(3);
  // Read at offset 1. `result.buffer` contains the transferred buffer,
  // `result.readBytes` is 2, the number of bytes read. `readBuffer` is left
  // detached.
  result = await file.read(readBuffer, 1);
  // `Uint8Array(3) [65, 66, 0]`
  console.log(result.buffer);
} finally {
  file.close();
}

開く、一覧表示、削除する

// Open three different files (creating them if needed).
await storageFoundation.open('sunrise');
await storageFoundation.open('noon');
await storageFoundation.open('sunset');
// List all existing files.
// `["sunset", "sunrise", "noon"]`
await storageFoundation.getAll();
// Delete one of the three files.
await storageFoundation.delete('noon');
// List all remaining existing files.
// `["sunrise", "noon"]`
await storageFoundation.getAll();

デモ

以下の埋め込みから Storage Foundation API のデモを試すことができます。ファイルの作成、名前変更、書き込み、読み取りを行い、変更時に更新を要求した使用可能な容量を確認します。デモのソースコードは Glitch にあります。

セキュリティと権限

Chromium チームは、強力なウェブ プラットフォーム機能へのアクセスの制御で定義されているユーザー コントロール、透明性、エルゴノミクスなどの基本原則に従って、Storage Foundation API を設計し、実装しました。

ウェブ上の他の最新のストレージ API と同じパターンに従って、Storage Foundation API へのアクセスはオリジンにバインドされています。つまり、オリジンは自己作成データにのみアクセスできます。また、これは安全なコンテキストに限定されます。

ユーザー コントロール

保存容量は、ディスク容量へのアクセスを分散し、不正使用を防ぐために使用されます。占有するメモリを最初にリクエストする必要があります。他のストレージ API と同様に、ユーザーは Storage Foundation API が占有する領域をブラウザ上で消去できます。

関連情報

謝辞

Storage Foundation API は、Emanuel KrivoyRichard Stotz によって指定され、実装されました。この記事は、Pete LePageJoe Medley によってレビューされました。

UnsplashMarkus Spiske によるヒーロー画像