restate / v1.0.0
안정성 설계
저장소가 느리거나 실패하거나 없을 때 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가 만든 오류는 이름이 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 | 파괴된 scope로 스토어를 만들려 했습니다. |
SEQUENCE_EXHAUSTED | 내부 순번이 더 이상 식별자를 만들 수 없습니다. |
STORAGE_BLOCKED | 시간 초과나 중단 뒤에도 이전 어댑터 호출이 끝나지 않았습니다. |
STORAGE_TIMEOUT | 작업이 operationTimeoutMs보다 오래 걸렸습니다. |
STORAGE_UNAVAILABLE | 여기서는 저장소를 쓸 수 없습니다. 스토어는 메모리로 계속 동작합니다. |
STORAGE_VALUE_TOO_LARGE | 인코딩한 값이 maxStorageCharacters보다 깁니다. |
STORAGE_VERSION_MISMATCH | 기록의 버전이 다르고 이를 처리할 migrate 함수가 없습니다. |
STORE_DESTROYED | 파괴된 스토어를 구독하거나 저장소 작업에 사용했습니다. |