اختبار ترحيلات قاعدة البيانات قبل الوصول إلى الإنتاج

لقد كتبت سكريبت ترحيل (migration script). يبدو صحيحًا. الصياغة سليمة. المنطق يبدو مناسبًا. قمت بتشغيله على قاعدة البيانات المحلية لديك، وعمل بشكل جيد. ثم قمت بنشره إلى الإنتاج، وانهار كل شيء.

الجدول كان يحتوي على ملايين الصفوف. قاعدة بياناتك المحلية كانت تحتوي على اثني عشر صفًا فقط. الترحيل أضاف عمودًا غير قابل للقيم الفارغة (NOT NULL)، لكن الإنتاج كان يحتوي على قيم فارغة (NULL) لم تكن تعلم بوجودها. أمر ALTER TABLE قام بقفل عمليات الكتابة لمدة خمس عشرة دقيقة خلال ذروة الحركة.

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

لماذا تطابق المخطط (Schema) مهم

سكريبت الترحيل يُكتب بناءً على افتراضات. يفترض وجود أعمدة معينة، وفهارس (indexes) معينة، وقيود (constraints) معينة. إذا كانت قاعدة بيانات الاختبار لديك تحتوي على مخطط مختلف، فأنت تختبر ضد مجموعة مختلفة من الافتراضات.

خذ مثالًا: ترحيل يضيف عمودًا غير قابل للقيم الفارغة (NOT NULL). على قاعدة بيانات اختبار بجدول فارغ، يعمل الترحيل فوريًا. على الإنتاج، الجدول يحتوي على صفوف بقيم فارغة في ذلك العمود. يفشل الترحيل، وتحدث حادثة إنتاج.

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

إليك مثال عملي باستخدام PostgreSQL:

# تفريغ المخطط فقط (بدون بيانات) من الإنتاج
pg_dump --schema-only --no-owner --no-acl production_db > schema.sql

# استعادة المخطط إلى قاعدة بيانات الاختبار
psql test_db < schema.sql

بيانات تكشف المشاكل الحقيقية

الجدول الفارغ أو الجدول الذي يحتوي على بيانات اختبار عشوائية لن يكشف الحالات الحدية (edge cases). غالبًا ما تأتي حالات فشل الترحيل من بيانات موجودة في الإنتاج ولكنها غير موجودة في مجموعة اختبارك.

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

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

التشغيل التجريبي (Dry-Run) كشبكة أمان

التشغيل التجريبي ينفذ SQL الترحيل دون تغيير قاعدة البيانات بشكل دائم. بعض أدوات الترحيل تحتوي على وضع تشغيل تجريبي مدمج. إذا لم يكن لأداتك ذلك، يمكنك لف الترحيل في معاملة (transaction) وتراجعها (rollback) في النهاية.

الغرض من التشغيل التجريبي ليس تأكيد نجاح الترحيل. بل هو رؤية التحذيرات والأخطاء وتغييرات خطة التنفيذ التي قد تشير إلى مشاكل. الترحيل الذي يعمل بشكل جيد على جدول صغير قد يظهر تحذيرًا بمسح جدول كامل (full table scan) عند تشغيله على مخطط واقعي. أمر ALTER TABLE على جدول كبير قد يظهر وقت تنفيذ مقدر غير مقبول لنافذة الصيانة الخاصة بك.

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

محاكاة حمل خفيف أثناء الترحيل

بعض الترحيلات آمنة على قاعدة بيانات خاملة ولكنها تسبب مشاكل تحت حركة المرور الحقيقية. أمر ALTER TABLE الذي يكتسب قفلًا قد يكتمل في ثوانٍ عندما لا يستخدم الجدول أي شخص آخر. تحت حمل الإنتاج، نفس هذا القفل قد يتسبب في انتهاء مهلة الاستعلامات (query timeouts) وأخطاء التطبيق.

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

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

أتمتة بيئة الاختبار

إعداد بيئة اختبار يدويًا لكل ترحيل هو عملية بطيئة وعرضة للخطأ. النهج الأفضل هو أتمتتها كجزء من خط أنابيب التكامل المستمر (CI pipeline).

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

هذه الأتمتة تضمن اختبار كل ترحيل مقابل خط أساس (baseline) ثابت. لا يمكن لأحد تخطي الاختبار لأنه كان في عجلة من أمره. لا يمكن لأحد الادعاء بأن الاختبار نجح لأنه استخدم مخططًا مختلفًا. يفرض خط الأنابيب نفس الظروف في كل مرة.

قائمة تحقق عملية

قبل تشغيل ترحيل في الإنتاج، تأكد من استيفاء هذه الشروط:

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

ماذا يعني هذا لخط الأنابيب الخاص بك

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

اختبار الترحيلات في بيئة واقعية ليس اختياريًا. إنه الفرق بين معرفة أن الترحيل سيعمل والأمل في أن يعمل. البيئة التي تبنيها للاختبار لا تحتاج أن تكون باهظة الثمن أو معقدة. يجب أن تكون ممثلة للواقع.

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