تغذيتك التراكمية تفوّت الالتزامات المتأخرة: ابنِ مؤشراً جديراً بالثقة
صفٌّ وصل متأخراً لأن العلامة المائية لم تكن حدّاً للالتزام. تتبّع الخلل ثم عرّف التغطية والحدود والاحتفاظ والاستئناف الآمن.
هذا خلل شاهدته يحدث مرتين، في محرّكين مختلفين. يعمل التصدير عند 10:00 ويسجّل أعلى طابع زمني لآخر تعديل رآه. معاملة بدأت عند 09:58 ما زالت مفتوحة، ثم تُلتزم عند 10:03، وتحمل طابعاً زمنياً من وقت بدايتها. يطلب التصدير التالي الصفوف الأحدث من 10:00 فقط، فلا يراها أبداً. كان حجم النقل ممتازاً والبيانات خاطئة.
تتبّع التصدير الذي أضاع صفاً
تتبّع ترتيب الأحداث لا جملة SQL. يكتب التطبيق وقت تعديل الصف عند بداية المعاملة، لا عند الالتزام. يقرأ المصدّر علامة مائية بساعة الحائط بينما المعاملة ما زالت مفتوحة. ثم تُلتزم المعاملة بطابع تعديل أقدم من العلامة المحفوظة. تبدأ دفعة المستهلك التالية بعد العلامة، فيبقى الصف خلف المؤشر ولن يُختار مرة أخرى أبداً.
لا شيء يسجّل خطأً، لأن شيئاً لم يفشل. هذا ما يجعل هذا الصنف من الأخطاء مكلفاً: التغذية التراكمية سريعة، وأعداد الصفوف تبدو معقولة، والصف المفقود يظهر بعد أسابيع كتذكرة مطابقة لا يستطيع أحد إعادة إنتاجها.
أرقام التسلسل تحمل الشكل نفسه من الخلل لسبب مختلف. المعرّف الأحادي يخبرك بترتيب تخصيص القيم، لا بترتيب التزام المعاملات. معاملة أخذت رقماً منخفضاً قد تُلتزم بعد معاملة أخذت رقماً عالياً، لذا فمؤشر مبني على ترتيب التخصيص قد يترك ثقباً أيضاً.
خمسة وعود على التغذية أن تفي بها
قبل اختيار وسيلة النقل، اطلب من النظام المصدر عقداً من خمسة بنود. إن غاب أي بند فما لديك استدلال لا تغذية تغييرات.
- التغطية: الإدارجات والتحديثات والحذف، ولكلٍّ منها المفتاح الثابت اللازم لتطبيقه.
- الحدود: ماذا تعني قيمة المؤشر وما تحتويه دفعة متسقة.
- الاتساق: كيف تُقرأ الجداول المترابطة دون خلط حالات مصدر غير متوافقة.
- الاحتفاظ: إلى أي مدة يستطيع مستهلك منقطع أن يستأنف من موضعه المحفوظ.
- إعادة التهيئة: لقطة كاملة مدعومة عندما لا يعود ذلك الموضع صالحاً.
يمكن لآلية مدعومة أن تفي بالخمس كلها. توثيق تتبّع التغييرات في SQL Server مثال منشور وملموس: يعرّف نسخة صغرى صالحة، ويسجّل الحذف إلى جانب التغييرات، ويصف معاملة لقطة تشمل التحقق والتقاط النسخة واستعلامات التغيير المرتبطة. وهو مثال لما ينبغي أن تطالب به من مصدر، لا صياغة يمكنك لصقها في محرّك آخر.
صمّم نقطة التحقق كجزء من خط المعالجة
موضع المستهلك المحفوظ هو آلة الحالة الحقيقية، فاكتبه على هذا الأساس. لا يجوز للمؤشر أن يتقدم إلا عبر سير العمل الذي طبّق الدفعة بالفعل وألتزم معاملة الهدف.
cursor = durable_checkpoint() # last fully applied position
batch = extract(cursor, upper_bound) # one consistent batch
apply(batch) # idempotent at the target
commit()
checkpoint(batch.upper_bound) # only now
إذا فشل التسليم في منتصفه، طابق الدفعة نفسها بدلاً من اختراع موضع مصدر جديد. إعادة الاستخراج بحد أعلى أحدث هي الطريق الذي يجعل تطبيقاً جزئياً فجوة دائمة. وتنطبق القاعدة المرآوية على الجانب الآخر أيضاً: التطبيق المتكرر الآمن عند الهدف يمنع التكرارات ولا يفعل شيئاً لتغيير لم يره الاستخراج قط.
الحذف يحتاج سجلات لا استنتاجات
مقارنة الصفوف التي ما زالت موجودة لا تخبر المستهلك أي المفاتيح اختفت. والمصدر الذي لا يسجّل الحذف أبداً يتركك بخيارين ناقصين: الاحتفاظ بتاريخ للمفاتيح طويلاً بما يكفي للمقارنة، أو تشغيل مطابقة كاملة صريحة وفق جدول. كلاهما قرار تصميم له كلفة، ولا يوجد أي منهما ما لم يملكه أحد.
الاحتفاظ هو القرار نفسه. لكل تغذية تغييرات مدعومة نافذة؛ اذكرها في العقد ونبّه عندما تقترب نقطة تحقق المستهلك منها. والمستهلك الذي يستيقظ بعد انقطاع دام أسبوعين ينبغي أن يقرأ خطأً واضحاً "يلزم إعادة التهيئة" بدلاً من أن يتلقى بصمت دفعة جزئية.
ثم قِس ما إذا كانت التغذية التراكمية تستحق العناء
الرجوع إلى مقارنة كل مفتاح وتجزئته صحيح أحياناً، ويستحق أن تعرف كلفته. فأنت ما زلت تنقل معلومات عن كل صف، والتجزئات لا تمنحك لقطة متسقة عبر الجداول، ومخطط التجزئة يجلب أسئلته الخاصة عن التمثيل والتصادمات. الهدف بايتات أقل مع حد قابل للتحقق؛ أما بايتات أقل تُسقط بصمت تغييراً ملتزماً فليست تغذية تراكمية أصلاً.
عمود آخر تعديل تلميح. أما المؤشر الذي ينجو من التزام متأخر وحذف وانقطاع طويل فهو عقد.