restate / v1.0.0

信頼性の設計

ストレージが遅い、失敗する、存在しないとき restate が何を保証するか、そして報告されるエラーの読み方。

このページの内容保証順番どおりの保存試してみる読み込みに失敗したとき保存して閉じるストレージがない場合保存データの検証とマイグレーション独自のストレージを使う制限とタイムアウトエラーコード

保証

これらの動作は 1.0 の契約の一部で、パッケージのテストでカバーされています。

状況restate の動作
保存を立て続けに呼ぶストアごとに順番に実行し、キューで待っている保存は最新の値を書き込みます。
レコードの読み込みに失敗したautoSave と一括保存は LOAD_FAILED を返してスキップし、置き換えられるのは明示的な save() だけです。
保存中にコンポーネントがアンマウントされる要求済みの保存は完了し、破棄されたストアへのその後の書き込みは無視されます。
ストレージが使えないストアはメモリ上で動き、status().available は false、保存は reject されます。
アダプターが応答しない操作は operationTimeoutMs でタイムアウトし、後続の処理はアダプターが落ち着くまで待ちます。
保留中の処理が多すぎる際限なく増え続けるのではなく、新しい操作を QUEUE_FULL で reject します。

順番どおりの保存

1 つのストアの操作が重なることはありません。別の保存が待っている間に出した保存は新しい値を引き継ぐので、変更のたびに保存してもキューを埋めずに最新の状態を書き込めます。

JavaScript / JSX
useDraft.set('A');
const first = useDraft.save(); // writes "A"
useDraft.set('B');
const second = useDraft.save(); // waits in the queue
useDraft.set('C');
const third = useDraft.save(); // joins the waiting save

await Promise.all([first, second, third]); // ['A', 'C', 'C'] — two writes

試してみる

ストレージを遅くしたり、書き込みを失敗させたり、ストレージをなくしたりしてみてください。

サンプルを準備中…

読み込みに失敗したとき

restateManager.ready は BULK_OPERATION_FAILED で reject され、失敗したストアを一覧にします。そのレコードが読み込めるまで、autoSave と saveAll はそれを置き換えず LOAD_FAILED を報告します。

編集を残して保存するには initialize() で再試行し、編集を捨てるには restore() を呼び、意図的にレコードを上書きするには save() を呼びます。

JavaScript / JSX
try {
  await restateManager.ready;
} catch (error) {
  // BULK_OPERATION_FAILED: one entry per store that could not load.
  for (const { store, error: cause } of error.failures) {
    console.error(store.key, cause.code);
  }
}

保存して閉じる

アンマウント前に要求した保存は完了するので、「保存して閉じる」のハンドラーで待つ必要はありません。

JavaScript / JSX
function Document({ onClose }) {
  const useDraft = useRestateScope((scope) => persistedRestate('', { scope, key: 'draft' }));

  const saveAndClose = () => {
    useDraft.save(); // finishes even after this component unmounts
    onClose();
  };
  // …
}

ストレージがない場合

サーバーレンダリングやブラウザーのストレージのブロックは、起動時のエラーにはなりません。available を確認して、変更がメモリにしか保持されないことをユーザーに伝えましょう。

JavaScript / JSX
const { available } = useDraft.status();

if (!available) {
  // Server rendering or blocked browser storage: the store works in memory
  // and save() rejects with STORAGE_UNAVAILABLE.
  showNotice('Changes are kept until you close this tab.');
}

保存データの検証とマイグレーション

version、migrate、validate が古いレコードや壊れたレコードから守ります。validate は保存の前と、復元またはマイグレーションの後に実行されます。

JavaScript / JSX
import { persistedRestate } from '@kkwaenggwari/restate';

const useSettings = persistedRestate(
  { theme: 'system' },
  {
    key: 'settings',
    version: 2,
    migrate(value, from, to) {
      if (from === 1 && to === 2 && typeof value?.darkMode === 'boolean') {
        return { theme: value.darkMode ? 'dark' : 'light' };
      }
      throw new Error('Unsupported stored version');
    },
    validate: (value) => ['system', 'light', 'dark'].includes(value?.theme),
  },
);

独自のストレージを使う

getItem、setItem、removeItem を持つオブジェクトなら何でも使えます。メソッドは値を返しても Promise を返してもかまいません。isAvailable は省略できます。

JavaScript / JSX
import { persistedRestate } from '@kkwaenggwari/restate';

const sessionStorageAdapter = {
  isAvailable: () => typeof sessionStorage !== 'undefined',
  getItem: (key) => sessionStorage.getItem(key),
  setItem: (key, value) => sessionStorage.setItem(key, value),
  removeItem: (key) => sessionStorage.removeItem(key),
};

export const useFilters = persistedRestate(
  { query: '' },
  {
    key: 'filters',
    storage: sessionStorageAdapter,
  },
);

制限とタイムアウト

1 つの操作を実行できる時間、待機できる操作の数、値の最大サイズを調整できます。

JavaScript / JSX
persistedRestate(value, {
  key: 'draft',
  operationTimeoutMs: 10_000, // default 30000
  maxPendingOperations: 64, // default 256
  maxStorageCharacters: 1_000_000,
});

エラーコード

restate が作るエラーの name は RestateError で、code を持ち、保存ストアなら key も持ちます。ストレージアダプターやあなたのコールバックは、独自のエラーで reject することがあります。

コード意味
BULK_OPERATION_FAILEDrestateManager.ready や saveAll などの一括呼び出しで失敗がありました。error.failures を確認してください。
INDEXEDDB_ABORTEDIndexedDB のトランザクションが中断されました。
INDEXEDDB_BLOCKED別のタブの接続によって、データベースのオープンがブロックされました。
INDEXEDDB_CONNECTION_CLOSINGリクエストの開始時に、データベース接続が閉じかけていました。
INDEXEDDB_OPEN_FAILEDデータベースを開けませんでした。
INDEXEDDB_REQUEST_FAILEDIndexedDB のリクエストが失敗しました。
INDEXEDDB_STORE_MISSING作成済みのデータベースにオブジェクトストアがありません。
INVALID_STORAGE_RECORD保存されたレコードが有効な restate のレコードではありません。
INVALID_STORED_VALUE値が validate を通りませんでした。
LIFECYCLE_LIMITupdater が開始と停止を繰り返し続けました。
LOAD_FAILEDレコードを読み込めなかったため、自動保存や一括保存で置き換えませんでした。
MMKV_UNAVAILABLEreact-native-mmkv を読み込めませんでした。
MULTIPLE_ERRORS1 つの操作で複数のコールバックが失敗しました。error.errors を確認してください。
NOTIFICATION_LIMITオブザーバーがループの中で変更を予約し続けました。
OBSERVER_ERRORリスナーまたはライフサイクルのコールバックが例外を投げました。
OPERATION_ABORTEDストレージ操作がキャンセルされました。
QUEUE_FULLmaxPendingOperations で許される数より多くの操作が待機していました。
REENTRANT_SETTERstate の updater が、実行中に同じストアを変更しようとしました。
SCOPE_DESTROYED破棄されたスコープでストアを作ろうとしました。
SEQUENCE_EXHAUSTED内部のシーケンスで使える ID がなくなりました。
STORAGE_BLOCKEDタイムアウトまたは中断の後、以前のアダプター呼び出しがまだ完了していません。
STORAGE_TIMEOUT操作に operationTimeoutMs より長くかかりました。
STORAGE_UNAVAILABLEこの環境ではストレージを使えません。ストアはメモリ上で動き続けます。
STORAGE_VALUE_TOO_LARGEエンコードした値が maxStorageCharacters より長くなっています。
STORAGE_VERSION_MISMATCHレコードのバージョンが異なり、それを扱う migrate 関数がありません。
STORE_DESTROYED破棄されたストアを購読しようとしたか、ストレージの使用を要求しました。

ドキュメントを検索

すべてのガイドのタイトル、説明、本文から検索します。