restate / v1.0.0
Надёжность by design
Что гарантирует restate, когда хранилище медленное, сбоит или отсутствует, и как читать ошибки, о которых он сообщает.
На этой странице
ГарантииУпорядоченные сохраненияПопробуйтеЕсли загрузка не удаласьСохранить и закрытьХранилище недоступноВалидация и миграция сохранённых данныхСвоё хранилищеЛимиты и тайм-аутыКоды ошибокГарантии
Это поведение — часть контракта 1.0, и оно покрыто тестами пакета.
| Ситуация | Что делает restate |
|---|---|
Несколько сохранений подряд | Выполняет их по порядку для каждого хранилища; сохранение в очереди записывает самое свежее значение. |
Запись не загрузилась | autoSave и массовые сохранения пропускают её с LOAD_FAILED; заменяет её только явный save(). |
Компонент размонтируется во время сохранения | Запрошенное сохранение завершается; последующие записи в уничтоженное хранилище игнорируются. |
Хранилище недоступно | Хранилище работает в памяти, status().available равно false, сохранения отклоняются. |
Адаптер зависает | Операция завершается по тайм-ауту через operationTimeoutMs, а следующая работа ждёт, пока адаптер закончит. |
Слишком много ожидающей работы | Новые операции отклоняются с QUEUE_FULL вместо бесконечного роста. |
Упорядоченные сохранения
Операции одного хранилища никогда не пересекаются. Сохранение, начатое во время ожидания другого, берёт самое новое значение, поэтому сохранение при каждом изменении записывает последнее состояние, не переполняя очередь.
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(), чтобы намеренно перезаписать запись.
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);
}
}Сохранить и закрыть
Сохранение, запрошенное до размонтирования, всё равно завершится, поэтому обработчику «Сохранить и закрыть» не нужно ждать.
function Document({ onClose }) {
const useDraft = useRestateScope((scope) => persistedRestate('', { scope, key: 'draft' }));
const saveAndClose = () => {
useDraft.save(); // finishes even after this component unmounts
onClose();
};
// …
}Валидация и миграция сохранённых данных
version, migrate и validate защищают от старых или повреждённых записей. validate выполняется перед сохранением и после восстановления или миграции.
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 необязателен.
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,
},
);Лимиты и тайм-ауты
Настройте, сколько может длиться операция, сколько их может ждать и насколько большим может быть значение.
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_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 состояния попытался изменить то же хранилище во время своего выполнения. |
SCOPE_DESTROYED | Для создания хранилищ использовалась уничтоженная область. |
SEQUENCE_EXHAUSTED | Во внутренней последовательности закончились идентификаторы. |
STORAGE_BLOCKED | Предыдущий вызов адаптера не завершился после тайм-аута или отмены. |
STORAGE_TIMEOUT | Операция длилась дольше operationTimeoutMs. |
STORAGE_UNAVAILABLE | Здесь хранилище использовать нельзя; хранилище продолжает работать в памяти. |
STORAGE_VALUE_TOO_LARGE | Закодированное значение превышает maxStorageCharacters. |
STORAGE_VERSION_MISMATCH | У записи другая версия, и ни одна функция migrate её не обрабатывает. |
STORE_DESTROYED | На уничтоженное хранилище подписались или попросили его использовать хранилище. |