restate / v1.0.0

可靠性设计

当存储缓慢、出错或缺失时 restate 保证什么,以及如何解读它报告的错误。

本页内容保证按顺序保存动手试试加载失败时保存并关闭没有可用的存储校验和迁移已保存的数据自带存储限制与超时错误码

保证

这些行为是 1.0 契约的一部分,并由包的测试覆盖。

情况restate 的做法
连续多次保存按 store 依次执行;在队列中等待的保存会写入最新的值。
某条记录加载失败autoSave 和批量保存以 LOAD_FAILED 跳过它;只有显式的 save() 能替换它。
保存过程中组件被卸载已请求的保存会完成;之后对已销毁 store 的写入会被忽略。
存储不可用store 在内存中工作,status().available 为 false,保存会被拒绝。
适配器卡住操作在 operationTimeoutMs 后超时,后续工作会等待适配器结束。
待处理的工作过多新操作以 QUEUE_FULL 被拒绝,而不是无限增长。

按顺序保存

同一个 store 的操作永远不会重叠。在另一次保存等待时发起的保存会取用更新的值,所以即使每次变化都保存,也能写入最新状态而不会塞满队列。

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 被拒绝,并列出每个失败的 store。在那条记录成功加载之前,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,
  },
);

限制与超时

调整单个操作可运行多久、可以有多少个操作排队,以及值最大可以多大。

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

错误码

restate 创建的错误名称为 RestateError,带有 code,持久化 store 的错误还带有 key。存储适配器和你的回调可能会以它们自己的错误拒绝。

错误码含义
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_LIMIT某个 updater 不断地启动又停止。
LOAD_FAILED记录无法加载,因此自动保存或批量保存没有替换它。
MMKV_UNAVAILABLE无法加载 react-native-mmkv。
MULTIPLE_ERRORS一次操作中有多个回调失败;请查看 error.errors。
NOTIFICATION_LIMIT观察者在循环中不断安排新的变化。
OBSERVER_ERROR某个监听器或生命周期回调抛出了异常。
OPERATION_ABORTED某个存储操作被取消。
QUEUE_FULL等待中的操作超过了 maxPendingOperations 允许的数量。
REENTRANT_SETTER某个状态 updater 在运行时试图修改同一个 store。
SCOPE_DESTROYED使用已销毁的 scope 创建 store。
SEQUENCE_EXHAUSTED内部序列的标识符已用完。
STORAGE_BLOCKED超时或中止后,先前的适配器调用仍未结束。
STORAGE_TIMEOUT操作耗时超过了 operationTimeoutMs。
STORAGE_UNAVAILABLE此处无法使用存储;store 会继续在内存中工作。
STORAGE_VALUE_TOO_LARGE编码后的值超过了 maxStorageCharacters。
STORAGE_VERSION_MISMATCH记录的版本不同,且没有处理它的 migrate 函数。
STORE_DESTROYED订阅了已销毁的 store,或要求它使用存储。

搜索文档

搜索所有指南的标题、描述和正文。