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 | مخزن الكائنات غير موجود في قاعدة بيانات أُنشئت مسبقًا. |
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 | حاولت دالة تحديث الحالة تغيير المخزن نفسه أثناء تنفيذها. |
SCOPE_DESTROYED | استُخدم نطاق مُتلَف لإنشاء مخازن. |
SEQUENCE_EXHAUSTED | نفدت المعرّفات من تسلسل داخلي. |
STORAGE_BLOCKED | استدعاء سابق للمحوّل لم ينتهِ بعد انتهاء مهلة أو إلغاء. |
STORAGE_TIMEOUT | استغرقت عملية أطول من operationTimeoutMs. |
STORAGE_UNAVAILABLE | لا يمكن استخدام التخزين هنا؛ يواصل المخزن العمل في الذاكرة. |
STORAGE_VALUE_TOO_LARGE | تتجاوز القيمة المرمَّزة maxStorageCharacters. |
STORAGE_VERSION_MISMATCH | للسجل إصدار مختلف ولا توجد دالة migrate تعالجه. |
STORE_DESTROYED | تم الاشتراك في مخزن مُتلَف أو طُلب منه استخدام التخزين. |