restate / v1.0.0

Надёжность by design

Что гарантирует restate, когда хранилище медленное, сбоит или отсутствует, и как читать ошибки, о которых он сообщает.

На этой страницеГарантииУпорядоченные сохраненияПопробуйтеЕсли загрузка не удаласьСохранить и закрытьХранилище недоступноВалидация и миграция сохранённых данныхСвоё хранилищеЛимиты и тайм-аутыКоды ошибок

Гарантии

Это поведение — часть контракта 1.0, и оно покрыто тестами пакета.

СитуацияЧто делает restate
Несколько сохранений подрядВыполняет их по порядку для каждого хранилища; сохранение в очереди записывает самое свежее значение.
Запись не загрузиласьautoSave и массовые сохранения пропускают её с LOAD_FAILED; заменяет её только явный save().
Компонент размонтируется во время сохраненияЗапрошенное сохранение завершается; последующие записи в уничтоженное хранилище игнорируются.
Хранилище недоступноХранилище работает в памяти, status().available равно false, сохранения отклоняются.
Адаптер зависаетОперация завершается по тайм-ауту через operationTimeoutMs, а следующая работа ждёт, пока адаптер закончит.
Слишком много ожидающей работыНовые операции отклоняются с QUEUE_FULL вместо бесконечного роста.

Упорядоченные сохранения

Операции одного хранилища никогда не пересекаются. Сохранение, начатое во время ожидания другого, берёт самое новое значение, поэтому сохранение при каждом изменении записывает последнее состояние, не переполняя очередь.

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 и перечисляет каждое хранилище с ошибкой. Пока эта запись не загрузится, 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, имеют name RestateError, code и, для сохраняемых хранилищ, key. Адаптеры хранилища и ваши колбэки могут отклоняться собственными ошибками.

КодЗначение
BULK_OPERATION_FAILEDВ массовом вызове, таком как restateManager.ready или saveAll, были ошибки; см. error.failures.
INDEXEDDB_ABORTEDТранзакция IndexedDB прервана.
INDEXEDDB_BLOCKEDОткрытие базы данных заблокировано подключением в другой вкладке.
INDEXEDDB_CONNECTION_CLOSINGПодключение к базе данных закрывалось, когда начался запрос.
INDEXEDDB_OPEN_FAILEDНе удалось открыть базу данных.
INDEXEDDB_REQUEST_FAILEDЗапрос IndexedDB не удался.
INDEXEDDB_STORE_MISSINGВ уже созданной базе данных нет object store.
INVALID_STORAGE_RECORDСохранённая запись не является корректной записью restate.
INVALID_STORED_VALUEЗначение не прошло validate.
LIFECYCLE_LIMITUpdater снова и снова запускался и останавливался.
LOAD_FAILEDЗапись не удалось загрузить, поэтому автоматическое или массовое сохранение её не заменило.
MMKV_UNAVAILABLEНе удалось загрузить react-native-mmkv.
MULTIPLE_ERRORSВ одной операции упало несколько колбэков; см. error.errors.
NOTIFICATION_LIMITНаблюдатели раз за разом планировали изменения в цикле.
OBSERVER_ERRORСлушатель или колбэк жизненного цикла выбросил ошибку.
OPERATION_ABORTEDОперация с хранилищем отменена.
QUEUE_FULLОжидающих операций больше, чем позволяет maxPendingOperations.
REENTRANT_SETTERUpdater состояния попытался изменить то же хранилище во время своего выполнения.
SCOPE_DESTROYEDДля создания хранилищ использовалась уничтоженная область.
SEQUENCE_EXHAUSTEDВо внутренней последовательности закончились идентификаторы.
STORAGE_BLOCKEDПредыдущий вызов адаптера не завершился после тайм-аута или отмены.
STORAGE_TIMEOUTОперация длилась дольше operationTimeoutMs.
STORAGE_UNAVAILABLEЗдесь хранилище использовать нельзя; хранилище продолжает работать в памяти.
STORAGE_VALUE_TOO_LARGEЗакодированное значение превышает maxStorageCharacters.
STORAGE_VERSION_MISMATCHУ записи другая версия, и ни одна функция migrate её не обрабатывает.
STORE_DESTROYEDНа уничтоженное хранилище подписались или попросили его использовать хранилище.

Поиск по документации

Поиск по заголовкам, описаниям и тексту всех руководств.