restate / v1.0.0

Niezawodność by design

Co gwarantuje restate, gdy pamięć jest wolna, zawodzi lub jej brak, i jak czytać zgłaszane błędy.

Na tej stronieGwarancjeUporządkowane zapisyWypróbujGdy wczytanie się nie udaZapisz i zamknijBrak dostępnej pamięciWaliduj i migruj zapisane daneUżyj własnej pamięciLimity i limity czasuKody błędów

Gwarancje

Te zachowania są częścią kontraktu 1.0 i są objęte testami pakietu.

SytuacjaCo robi restate
Kilka zapisów z rzęduWykonuje je po kolei dla każdego magazynu; zapis w kolejce zapisuje najnowszą wartość.
Rekord się nie wczytałautoSave i zapisy zbiorcze pomijają go z LOAD_FAILED; zastępuje go tylko jawne save().
Komponent odmontowuje się w trakcie zapisuZlecony zapis się kończy; późniejsze zapisy do zniszczonego magazynu są ignorowane.
Pamięć jest niedostępnaMagazyn działa w pamięci operacyjnej, status().available jest false, zapisy są odrzucane.
Adapter się zawieszaOperacja kończy się po operationTimeoutMs, a kolejna praca czeka, aż adapter skończy.
Za dużo oczekującej pracyNowe operacje są odrzucane z QUEUE_FULL zamiast rosnąć bez końca.

Uporządkowane zapisy

Operacje jednego magazynu nigdy się nie nakładają. Zapis rozpoczęty, gdy inny czeka, bierze najnowszą wartość, więc zapis przy każdej zmianie zapisuje ostatni stan bez zapychania kolejki.

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

Wypróbuj

Spowolnij pamięć, spraw, by zapisy się nie udawały, albo całkiem usuń pamięć.

Przygotowywanie przykładu…

Gdy wczytanie się nie uda

restateManager.ready jest odrzucane z BULK_OPERATION_FAILED i wymienia każdy magazyn z błędem. Dopóki ten rekord się nie wczyta, autoSave i saveAll go nie zastąpią i zgłoszą LOAD_FAILED.

Spróbuj ponownie przez initialize(), by zachować edycje i je zapisać, wywołaj restore(), by je odrzucić, albo save(), by celowo nadpisać rekord.

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);
  }
}

Zapisz i zamknij

Zapis zlecony przed odmontowaniem i tak się kończy, więc obsługa „Zapisz i zamknij” nie musi czekać.

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

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

Brak dostępnej pamięci

Renderowanie na serwerze i pamięć zablokowana przez przeglądarkę nie są błędami przy starcie. Sprawdzaj available, by powiedzieć użytkownikom, że edycje zostają tylko w pamięci.

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.');
}

Waliduj i migruj zapisane dane

version, migrate i validate chronią przed starymi lub uszkodzonymi rekordami. validate uruchamia się przed zapisem oraz po przywróceniu lub migracji.

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),
  },
);

Użyj własnej pamięci

Nada się każdy obiekt z getItem, setItem i removeItem. Metody mogą zwracać wartości lub Promise; isAvailable jest opcjonalne.

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,
  },
);

Limity i limity czasu

Dostosuj, jak długo może trwać operacja, ile operacji może czekać i jak duża może być wartość.

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

Kody błędów

Błędy tworzone przez restate mają name RestateError, code oraz, w zapisywanych magazynach, key. Adaptery pamięci i twoje callbacki mogą odrzucać własnymi błędami.

KodZnaczenie
BULK_OPERATION_FAILEDWywołanie zbiorcze, takie jak restateManager.ready lub saveAll, miało błędy; zobacz error.failures.
INDEXEDDB_ABORTEDTransakcja IndexedDB została przerwana.
INDEXEDDB_BLOCKEDOtwarcie bazy danych zablokowało połączenie w innej karcie.
INDEXEDDB_CONNECTION_CLOSINGPołączenie z bazą danych było zamykane, gdy zaczęło się żądanie.
INDEXEDDB_OPEN_FAILEDNie udało się otworzyć bazy danych.
INDEXEDDB_REQUEST_FAILEDŻądanie IndexedDB nie powiodło się.
INDEXEDDB_STORE_MISSINGW już utworzonej bazie danych brakuje object store.
INVALID_STORAGE_RECORDZapisany rekord nie jest prawidłowym rekordem restate.
INVALID_STORED_VALUEWartość nie przeszła validate.
LIFECYCLE_LIMITUpdater w kółko startował i się zatrzymywał.
LOAD_FAILEDRekordu nie udało się wczytać, więc zapis automatyczny lub zbiorczy go nie zastąpił.
MMKV_UNAVAILABLENie udało się wczytać react-native-mmkv.
MULTIPLE_ERRORSW jednej operacji zawiodło kilka callbacków; zobacz error.errors.
NOTIFICATION_LIMITObserwatorzy w kółko planowali zmiany w pętli.
OBSERVER_ERRORSłuchacz lub callback cyklu życia rzucił błąd.
OPERATION_ABORTEDOperacja na pamięci została anulowana.
QUEUE_FULLCzekało więcej operacji, niż pozwala maxPendingOperations.
REENTRANT_SETTERUpdater stanu próbował zmienić ten sam magazyn w trakcie swojego działania.
SCOPE_DESTROYEDDo tworzenia magazynów użyto zniszczonego zakresu.
SEQUENCE_EXHAUSTEDWewnętrznej sekwencji skończyły się identyfikatory.
STORAGE_BLOCKEDPoprzednie wywołanie adaptera nie zakończyło się po przekroczeniu czasu lub anulowaniu.
STORAGE_TIMEOUTOperacja trwała dłużej niż operationTimeoutMs.
STORAGE_UNAVAILABLETutaj nie można użyć pamięci; magazyn działa dalej w pamięci operacyjnej.
STORAGE_VALUE_TOO_LARGEZakodowana wartość przekracza maxStorageCharacters.
STORAGE_VERSION_MISMATCHRekord ma inną wersję i żadna funkcja migrate jej nie obsługuje.
STORE_DESTROYEDZasubskrybowano zniszczony magazyn lub poproszono go o użycie pamięci.

Szukaj w dokumentacji

Przeszukuj tytuły, opisy i treść wszystkich przewodników.