Node.js / المتوسط: حدود التطبيق / الدرس 11
التخزين والمعاملات ومنع تكرار الأثر
افصل سلوك مستودع البيانات عن النقل وافهم التحديثات المتزامنة والتغييرات الذرية والأوامر المتكررة.
في هذه الصفحة
أهداف الدرس
- اشرح المفاهيم والمفاضلات المتعلقة بموضوع «التخزين والمعاملات ومنع تكرار الأثر».
- توقع المثالين وتحقق من مخرجاتهما وسلوك تنظيف الموارد فيهما.
- شخّص الإخفاقات الثلاثة الشائعة وطبق الحلول المشروحة على مدخلات مشابهة.
مستودع البيانات يحدد السلوك
يخفي مستودع البيانات تفاصيل التخزين خلف عمليات تعبر عن التطبيق. قد تعبّر عملية تحويل أو حجز عن قيد أقوى من فصل القراءة والكتابة. أبعد الاتصالات والاعتمادات عن تحليل الطلب ومرر المستودع إلى شيفرة المجال. يحد المجمع عدد الاتصالات لكنه لا يمنع فقد التحديث وحده. حدد معنى النجاح: بيانات مثبتة أم كتابة في طابور أم تغيير ذاكرة فقط. يؤثر ذلك في الاستجابة والمحاولة والاستعادة بعد تعطل العملية.
لا تنشئ await معاملة
قد يقرأ طلبان الرصيد نفسه ثم ينتظران عملا ويكتبان نتيجتين متعارضتين. لا تجعل أحادية تنفيذ الاستدعاءات التسلسل غير المتزامن كله ذريّا. يوضح المثال الأول فحص الإصدار: يزيد التحديث الأول المراجعة ويُرفض الثاني القديم. هذا نموذج ذاكرة للتزامن التفاؤلي وليس تخزينا دائما. تستطيع قاعدة البيانات تنفيذ شرط مماثل في UPDATE ذرية وفحص الصفوف المتأثرة. عند التعارض أعد الحساب من الحالة الحالية بدلا من إعادة إرسال قيمة قديمة عمياء.
المعاملات تحمي قيدا
تجمع المعاملة التغييرات لتثبت معا أو تتراجع معا وفق عزل القاعدة. يجب ألا يثبت خصم التحويل دون إضافته. افحص القيود والملكية ضمن القرار المعاملي مع الكتابة عندما يهم التزامن. قد تحتفظ معاملة تنتظر واجهة بعيدة بالأقفال طويلا؛ فأبقها مركزة وفكر في outbox للمتابعة الموثوقة. حرر الاتصال المستعار داخل finally، ولا تعد تراجع المعاملة دليلا على التراجع عن أثر خارجي.
let account = { balance: 10, revision: 0 };
function update(expectedRevision, balance) {
if (account.revision !== expectedRevision) return false;
account = { balance, revision: expectedRevision + 1 };
return true;
}
const first = { ...account };
const second = { ...account };
console.log(update(first.revision, first.balance - 3));
console.log(update(second.revision, second.balance - 4));
console.log(account.balance, account.revision);
true
false
7 1
لا تستطيع مراجعة قديمة استبدال النتيجة الأولى. يجب حل التعارض باستخدام الحالة الحديثة.
الأوامر المتكررة تحتاج إلى عقد
قد يعيد العميل المحاولة بعد فقد الاستجابة رغم تثبيت العملية. يربط المثال الثاني مفتاحا اصطناعيا بالمدخل والنتيجة؛ فيحصل التكرار المطابق على النتيجة الأصلية، ويُرفض استخدام المفتاح لمدخل مختلف. الخريطة نموذج تعليمي متسلسل يفقد حالته عند إعادة التشغيل. تحتاج المفاتيح الفعلية إلى نطاق مستدع وسياسة احتفاظ وتخزين ذري مع التغيير. ويظل الفحص ثم الإدراج عبر await معرضا للسباق دون معاملة أو قيد فريد.
const receipts = new Map();
let nextId = 1;
function create(key, name) {
if (receipts.has(key)) {
const saved = receipts.get(key);
if (saved.name !== name) throw new Error('Key reused with different input');
return saved.id;
}
const id = nextId++;
receipts.set(key, { name, id });
return id;
}
console.log(create('request-a', 'Ada'), create('request-a', 'Ada'));
try { create('request-a', 'Grace'); } catch (error) { console.log(error.message); }
1 1
Key reused with different input
تعيد المحاولات المتسلسلة المتطابقة معرفا واحدا. ولا يستعير مدخل مختلف نتيجة الأمر السابق خفية.
تأمين حدود التخزين وتشغيلها
استخدم عمليات قاعدة ذات معاملات منفصلة للقيم، وقيد المعرفات الديناميكية بقائمة سماح، وامنح التطبيق الصلاحيات اللازمة فقط. ضع مهلا لطلب الاتصال والاستعلام، وراقب انتظار المجمع، ولا تنشئ مجمعا لكل طلب. خطط توافق الترحيلات والرجوع قبل نشر مخطط جديد. اختبر الكتابات المتنافسة والتكرار وتعطل التخزين وفشل التثبيت بقاعدة معزولة عند الحاجة. الذاكرة وJSON نموذجان مفيدان، لكنهما لا يضمنان تنسيق العمليات أو ديمومة التعطل بلا تنفيذ صريح.
الأخطاء الشائعة وإصلاحها
اختفاء تحديث رصيد
- لماذا يحدث؟
- كتب طلبان فوق قيمة قُرئت قبل await.
- كيف تصلحه؟
- استخدم تحديثا شرطيا ذريّا أو معاملة مناسبة.
- كيف تتجنب تكراره؟
- اختبر الطلبات المتزامنة على المستودع الحقيقي.
نفاد اتصالات المجمع
- لماذا يحدث؟
- لم تُحرر الاتصالات في كل مسارات الفشل.
- كيف تصلحه؟
- حررها داخل finally وحدد مهلة للحصول عليها.
- كيف تتجنب تكراره؟
- راقب الاتصالات النشطة وزمن انتظار المجمع.
تنشئ المحاولة سجلات مكررة
- لماذا يحدث؟
- فُقدت الاستجابة بعد تثبيت العملية الأصلية.
- كيف تصلحه؟
- خزّن مفتاح منع التكرار والنتيجة ذريّا.
- كيف تتجنب تكراره؟
- اختبر المحاولة بعد التثبيت وقبل تسليم الاستجابة.
اختبر فهمك
1. يبدأ خصمان 3 و4 من رصيد 10. ماذا يجب أن يحفظ المستودع الصحيح؟
اعرض الحل المشروح
إذا قُبلا فالرصيد النهائي 3. قد يرفض تعارض الإصدار محاولة مؤقتا، لكن يجب إعادة حسابها من الرصيد المحدث قبل التكرار. يدل رصيد 6 أو7 مع إعلان نجاحهما على فقد تحديث.
2. لماذا يجب تخزين مفتاح منع التكرار مع الكتابة لا بعدها؟
اعرض الحل المشروح
قد يترك التعطل بين تثبيت الكتابة وتخزين المفتاح دون دليل على التنفيذ السابق. خزّنهما في معاملة واحدة بمفتاح فريد ذي نطاق. عندئذ تقرأ المحاولة النتيجة الأصلية بدلا من تكرار الأثر.
أهم ما تعلمته
- يخفي مستودع البيانات تفاصيل التخزين خلف عمليات تعبر عن التطبيق.
- قد يقرأ طلبان الرصيد نفسه ثم ينتظران عملا ويكتبان نتيجتين متعارضتين.
- استخدم عمليات قاعدة ذات معاملات منفصلة للقيم، وقيد المعرفات الديناميكية بقائمة سماح، وامنح التطبيق الصلاحيات اللازمة فقط.