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

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

ここで役立つのが Storage Foundation API です。Storage Foundation API は、高速かつ独自性のない新しいストレージ API です。高性能なデータベースの実装や大きな一時ファイルの正常な管理など、ご要望の多かったウェブのユースケースを開拓できます。この新しいインターフェースでは、デベロッパーはウェブに「独自のストレージを使用する」ことができるため、ウェブとプラットフォーム固有のコード間の機能ギャップを減らすことができます。

Storage Foundation API は、非常に基本的なファイル システムに似せて設計されているため、開発者は上位レベルのコンポーネントを構築できる汎用的かつシンプルでパフォーマンスの高いプリミティブを提供することで、柔軟性を提供します。アプリケーションは、ユーザビリティ、パフォーマンス、信頼性の適切なバランスを求めて、ニーズに最適なツールを利用できます。

ウェブに別のストレージ API が必要な理由

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

  • これらのオプションの中には、Cookie や、sessionStoragelocalStorage のメカニズムで構成される Web Storage API など、保存できるデータが非常に少量であるため、この提案と明らかに重複しないものもあります。
  • その他のオプションは、File and Directory Entries APIWebSQL など、さまざまな理由ですでに非推奨となっています。
  • File System Access 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 を返します。新しい長さが現在の長さより小さい場合、ファイルの末尾からバイトが削除されます。そうでない場合、ファイルは値 0 のバイトで拡張されます。
  • 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 へのアクセスはオリジン バインドされます。つまり、オリジンは自己作成のデータにのみアクセスできます。また、これは安全なコンテキストに限定されます。

ユーザー コントロール

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

関連情報

謝辞

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

UnsplashMarkus Spiske によるヒーロー画像。