لماذا لا يشبه التراجع عن قاعدة البيانات التراجع عن التطبيق

عندما يتعطل إصدار جديد من تطبيقك، يكون الإصلاح عادةً بسيطًا. تضغط على زر التراجع في خط أنابيب النشر لديك، ويعود الإصدار القديم. يتوقف الخادم عن تشغيل الكود المعطل ويبدأ في تشغيل الإصدار السابق. ما لم تكن قد غيرت تبعيات النظام أو الإعدادات، يعود التطبيق إلى طبيعته في غضون دقائق.

يعمل هذا لأن التطبيقات مصممة لتكون عديمة الحالة (stateless) بطبيعتها. يمكنها التوقف وإعادة التشغيل وتبديل الإصدارات دون فقدان أي شيء دائم. البيانات التي تعالجها تأتي من المستخدمين أو من قاعدة البيانات. إذا توقف التطبيق لبضع ثوانٍ، قد يلاحظ المستخدمون ذلك، لكن لا يتم فقدان أي بيانات. بمجرد تشغيل الإصدار القديم مرة أخرى، يستأنف كل شيء كما لو لم يحدث شيء.

قواعد البيانات لا تعمل بهذه الطريقة.

واقع الحالة (Stateful) لقواعد البيانات

قاعدة البيانات هي مكون ذو حالة (stateful). إنها تحتوي على بيانات يجب أن تبقى عبر إصدارات التطبيق. ملفات تعريف المستخدمين، المعاملات، سجلات الطلبات، إعدادات التهيئة—كلها تعيش داخل قاعدة البيانات ويجب أن تظل سليمة. عندما تغير مخطط قاعدة البيانات (schema)—إضافة عمود، تغيير نوع بيانات، أو حذف جدول—تقوم قاعدة البيانات بتسجيل هذا التغيير بشكل دائم. البيانات المخزنة بالفعل لا تعود تلقائيًا إلى شكلها السابق لمجرد أنك أعدت التطبيق إلى إصدار أقدم.

هذا هو الفرق الأساسي الذي يفاجئ العديد من الفرق. يفترضون أن التراجع عن قاعدة البيانات يعمل بنفس طريقة التراجع عن التطبيق: تشغيل أمر عكسي، ويعود كل شيء إلى ما كان عليه قبل الترحيل. في الواقع، التراجع عن تغييرات المخطط أكثر تعقيدًا وخطورة.

مثال ملموس على المشكلة

تخيل أن فريقك يضيف عمود phone_number إلى جدول users من خلال ترحيل قاعدة بيانات. يتم تشغيل الترحيل بنجاح، وعلى مدار الساعات القليلة التالية، يبدأ المستخدمون في ملء أرقام هواتفهم. ثم يكتشف شخص ما خطأ في كود التطبيق الذي يقرأ هذا العمود. يقرر الفريق التراجع عن التطبيق إلى الإصدار السابق. يتم استعادة التطبيق، ويختفي الخطأ.

لكن عمود phone_number لا يزال في قاعدة البيانات. البيانات التي أدخلها المستخدمون لا تزال موجودة. إذا قمت الآن بتشغيل ترحيل تراجعي لإزالة هذا العمود، فستفقد جميع أرقام الهواتف هذه. بشكل دائم. وقد تكون هذه البيانات مستخدمة بالفعل من قبل ميزات أخرى أو تم عرضها من قبل مستخدمين آخرين. لا يمكنك حذفها دون عواقب.

هذا الخطر من فقدان البيانات هو السبب في أنه لا يمكن الاستخفاف بالتراجع عن قاعدة البيانات. في كل مرة يغير فيها الترحيل المخطط، قد يتم تحويل البيانات أو نقلها أو حذفها. استعادة المخطط القديم تعني استعادة شكل البيانات القديم. بدون آلية تضمن إمكانية إعادة البيانات إلى حالتها السابقة بالضبط، يمكن أن يؤدي التراجع عن المخطط إلى بيانات غير متسقة أو مفقودة.

مشكلة عدم توافق الكود مع المخطط (Code-Schema Mismatch)

إلى جانب فقدان البيانات، هناك مشكلة ثانية: عدم التوافق بين كود التطبيق ومخطط قاعدة البيانات.

عندما تقوم بالتراجع عن التطبيق إلى إصدار أقدم، قد لا يتعرف هذا الكود القديم على الأعمدة أو الجداول الجديدة الموجودة في قاعدة البيانات. قد يتعطل أثناء محاولة قراءة عمود لا يتوقعه، أو يفشل في إدراج البيانات بسبب قيد لا يعرفه.

على العكس من ذلك، إذا قمت بتشغيل ترحيل تراجعي يزيل عمودًا لا يزال مستخدمًا بواسطة كود التطبيق الحالي، فسينهار التطبيق فورًا. نظرًا لأن نشر التطبيق وترحيل قاعدة البيانات نادرًا ما يتم تنفيذهما في نفس اللحظة بالضبط، لا يمكنك ضمان أن كلا الجانبين متزامنان أثناء التراجع.

هذا عدم التوافق خطير بشكل خاص في بيئة الإنتاج. حتى بضع ثوانٍ من عدم الاتساق يمكن أن تسبب أخطاء، أو معاملات فاشلة، أو بيانات تالفة يصعب استعادتها.

لماذا لا تنطبق نفس استراتيجية التراجع

التراجع عن التطبيق يعمل لأن الإصدار القديم هو حالة جيدة معروفة. الكود هو نفسه كما كان قبل النشر. البيئة هي نفسها. الشيء الوحيد الذي يتغير هو أي ثنائي (binary) قيد التشغيل.

التراجع عن قاعدة البيانات ليس لديه "حالة جيدة معروفة" بنفس الطريقة. المخطط والبيانات تقدما للأمام. تشغيل ترحيل عكسي لا يعطيك نفس قاعدة البيانات التي كانت لديك من قبل. إنه يعطيك مخططًا يشبه القديم، ولكن مع بيانات قد تكون قد تحولت أو أضيفت أو أزيلت بطرق لا رجعة فيها دون تخطيط دقيق.

تحاول بعض الفرق الالتفاف حول هذا عن طريق أخذ نسخة احتياطية كاملة من قاعدة البيانات قبل كل ترحيل. هذا النهج له مشاكله الخاصة:

  • استعادة النسخة الاحتياطية تستغرق وقتًا—غالبًا دقائق أو ساعات، وليس ثوانٍ.
  • أي بيانات تمت كتابتها بعد أخذ النسخة الاحتياطية تُفقد.
  • الخدمات الأخرى التي تعتمد على نفس قاعدة البيانات قد تتعطل أثناء الاستعادة.
  • الاستعادة نفسها قد تفشل، مما يتركك في حالة أسوأ.

الطريق الأكثر أمانًا: الترحيل للأمام (Roll Forward)

بسبب هذه المخاطر، تعامل العديد من الفرق ذات الخبرة التراجع عن قاعدة البيانات كخيار أخير. بدلاً من محاولة التراجع عن ترحيل فاشل، يفضلون الترحيل للأمام: كتابة ترحيل جديد يصلح المشكلة دون عكس المخطط.

على سبيل المثال، إذا قام ترحيل بحذف عمود لا يزال مطلوبًا، فإن نهج الترحيل للأمام سيكون إعادة إضافة العمود في ترحيل جديد، بدلاً من محاولة التراجع عن الحذف. هذا يبقي المخطط يتحرك في اتجاه واحد ويتجنب تعقيد عكس تحويلات البيانات.

الترحيل للأمام ليس ممكنًا دائمًا. أحيانًا يكون الضرر شديدًا جدًا، أو يتطلب الإصلاح تغييرًا في المخطط لا يمكن التعبير عنه كترحيل أمامي. لكن في معظم الحالات، يكون أكثر أمانًا من محاولة التراجع الذي قد يؤدي إلى فقدان البيانات أو تعطيل التطبيق.

قائمة التحقق العملية لسلامة تغيير قاعدة البيانات

قبل تشغيل أي ترحيل لقاعدة البيانات في الإنتاج، راجع قائمة التحقق هذه:

  • هل يمكن عكس الترحيل دون فقدان البيانات؟ إذا لم يكن الأمر كذلك، خطط لاستراتيجية الترحيل للأمام بدلاً من ذلك.
  • هل هناك نسخة احتياطية من قاعدة البيانات تم أخذها مباشرة قبل الترحيل؟ اختبر أنه يمكن استعادة النسخة الاحتياطية فعليًا.
  • هل الترحيل متوافق مع الإصدارات السابقة مع كود التطبيق الحالي؟ يجب أن يظل الكود القديم يعمل أثناء تشغيل الترحيل.
  • هل هناك خدمات أو قواعد بيانات أخرى تعتمد على المخطط الذي تقوم بتغييره؟ قم بالتنسيق مع فرقهم.
  • هل لديك طريقة لاكتشاف أن الترحيل تسبب في مشكلة قبل أن يبلغ عنها المستخدمون؟ يجب أن تكون المراقبة والتنبيهات في مكانها.

الخلاصة

التراجع عن قاعدة البيانات ليس زر تراجع بسيط. إنها عملية عالية المخاطر يمكن أن تسبب فقدان البيانات، وأخطاء في التطبيق، وتوقفًا طويلًا. تعامل معها بنفس الحذر الذي تتعامل به مع الجراحة: خطط مسبقًا، واحصل على نسخة احتياطية، وفضل الإصلاح الأمامي كلما أمكن. التراجع الأكثر أمانًا هو الذي لا تضطر أبدًا إلى تشغيله.