عندما يكون مخطط قاعدة البيانات صحيحًا ولكن بياناتك خاطئة
لقد قمت للتو بتشغيل ترحيل قاعدة بيانات أضاف عمودًا جديدًا. بدا كل شيء جيدًا. نجح تغيير المخطط، والعمود موجود، والتطبيق يعمل. ولكن بعد ذلك لاحظ أحدهم: جميع المستخدمين الذين سجلوا قبل ثلاث سنوات كان يجب أن يتم وضع علامة "تم التحقق" عليهم، ولكنهم ليسوا كذلك. أو أن أرقام الهواتف التي قمت بترحيلها أصبحت الآن بتنسيقات غير متناسقة لأن البيانات القديمة لم تتبع القواعد الجديدة.
المخطط صحيح. نوع العمود مطابق. القيود صالحة. المشكلة هي البيانات نفسها.
هذا موقف يبدو أسوأ من فشل الترحيل. فشل الترحيل واضح. ترى الخطأ، وتعرف أن شيئًا ما تعطل، ويمكنك التصرف. لكن الترحيل الذي ينجح مع بيانات خاطئة يكون خفيًا. يمكن أن يبقى في الإنتاج لساعات أو أيام قبل أن يلاحظه أحد. وعندما تلاحظه، يكون رد الفعل الطبيعي هو الذعر والبحث عن طرق للتراجع عن كل شيء.
لكن التراجع عن المخطط لإصلاح البيانات يخلق مجموعة جديدة من المشاكل. قد لا يعمل التطبيق مع المخطط القديم بعد الآن. قد تفقد بيانات كانت صحيحة بالفعل. وأنت تلغي تغييرًا هيكليًا كان صحيحًا في الواقع، فقط لإصلاح محتوى كان خاطئًا.
المشكلة الحقيقية ليست في المخطط
عندما يضيف ترحيل عمودًا مثل is_verified بقيمة افتراضية false، فإن تغيير المخطط يكون مباشرًا. العمود موجود، القيمة الافتراضية تعمل، والسجلات الجديدة ستتصرف بشكل صحيح. المشكلة هي أن المستخدمين الحاليين الذين يجب أن يكونوا "تم التحقق" أصبحوا الآن مُعلمين كـ "لم يتم التحقق". المخطط لم يسبب هذا. منطق الترحيل لم يسبب هذا. الفجوة كانت في فهم ما يجب أن تكون عليه البيانات الحالية.
مثال شائع آخر: ترحيل يغير تخزين أرقام الهواتف ليتطلب رموز البلدان. التنسيق الجديد صحيح، والإدخالات الجديدة ستتبع القواعد. لكن أرقام الهواتف القديمة التي تم تخزينها بدون رموز بلدان أصبحت الآن غير متناسقة. المخطط جيد. البيانات ليست كذلك.
في كلتا الحالتين، الحل ليس التراجع عن المخطط. الحل هو إصلاح البيانات مع الحفاظ على المخطط سليمًا.
النصوص التعويضية: إصلاح البيانات دون لمس الهيكل
النص التعويضي هو ترحيل يغير البيانات فقط، وليس المخطط. يتم تشغيله كترحيل عادي، ويمر عبر نفس خط الأنابيب، ويتبع نفس عملية النشر. ولكن بدلاً من ALTER TABLE، أو CREATE INDEX، أو ADD COLUMN، فإنه يحتوي فقط على عبارات UPDATE أو INSERT أو DELETE التي تصحح البيانات.
الهدف بسيط: جلب البيانات إلى الحالة الصحيحة دون تغيير هيكل الجدول.
إليك مثال عملي. بعد إضافة عمود currency بقيمة افتراضية 'IDR'، أدرك الفريق أن جميع المعاملات من الشركاء الدوليين يجب أن تستخدم 'USD'. يبدو النص التعويضي كالتالي:
UPDATE transactions SET currency = 'USD' WHERE partner_type = 'international';
لا تغييرات في المخطط. لا أعمدة جديدة. لا تحويلات نوع. مجرد تصحيح بيانات مستهدف.
تتعامل النصوص التعويضية أيضًا مع حالات فشل الترحيل الجزئي. تخيل ترحيلًا ينشئ جدولًا جديدًا وينقل البيانات من جدول قديم. تفشل بعض الصفوف في النقل بسبب انتهاك قيد. بدلاً من التراجع عن الترحيل بأكمله والبدء من جديد، يمكن لنص تعويضي التعامل مع الصفوف المتبقية. يقوم بإدراج أو تحديث السجلات التي تم تفويتها فقط، دون إعادة تشغيل الترحيل الكامل.
اجعل نصوصك التعويضية غير قابلة للتكرار (Idempotent)
هناك قاعدة واحدة أهم من أي قاعدة أخرى: يجب أن تكون النصوص التعويضية غير قابلة للتكرار. تشغيل النص مرتين يجب أن ينتج نفس نتيجة تشغيله مرة واحدة.
هذا ليس مصدر قلق نظري. في الممارسة العملية، يتم إعادة تشغيل الترحيلات. يعاد تشغيل خط الأنابيب. يتم تحديث بيئة. يقوم شخص ما بتشغيل الترحيل يدويًا أثناء تصحيح الأخطاء. إذا كان النص الخاص بك غير قابل للتكرار، فإن تشغيله مرتين يمكن أن يفسد بياناتك.
الإصلاح واضح. تحقق دائمًا من الحالة الحالية قبل إجراء التغييرات. استخدم جملة WHERE محددة بما يكفي لتؤثر فقط على الصفوف التي تحتاج إلى تصحيح. إذا كانت قاعدة بياناتك تدعم ذلك، استخدم جمل ON CONFLICT للإدراج.
بدلاً من هذا:
UPDATE transactions SET currency = 'USD' WHERE partner_type = 'international';
اكتب هذا:
UPDATE transactions SET currency = 'USD' WHERE partner_type = 'international' AND currency IS DISTINCT FROM 'USD';
الفرق صغير ولكنه حاسم. الإصدار الثاني يقوم فقط بتحديث الصفوف حيث العملة ليست بالفعل 'USD'. تشغيله مائة مرة سيؤثر فقط على الصفوف التي تحتاج إلى تغيير، وفقط في المرة الأولى.
عندما لا تكون النصوص التعويضية كافية
النصوص التعويضية ليست حلاً شاملاً. إنها تعمل عندما يكون المخطط صحيحًا والبيانات فقط تحتاج إلى إصلاح. إذا كان المخطط نفسه خاطئًا، فلا تزال بحاجة إلى ترحيل مخطط مناسب.
على سبيل المثال، إذا أضفت عمودًا بنوع بيانات خاطئ، أو إذا كانت قيود العمود صارمة جدًا بالنسبة للبيانات التي يجب تخزينها، فلا يمكن للنص التعويضي المساعدة. تحتاج إلى تغيير المخطط. وبالمثل، إذا أدخل ترحيل خطأ أفسد البيانات بطريقة لا يمكن تصحيحها بعبارات UPDATE بسيطة، فقد تحتاج إلى نهج أكثر تعقيدًا.
لكن بالنسبة للحالة الشائعة حيث يكون المخطط صحيحًا والبيانات خاطئة، فإن النصوص التعويضية أكثر أمانًا من أي بديل. إنها تتجنب مخاطر ترحيلات الهبوط (down migrations)، ولا تتطلب الاستعادة من النسخ الاحتياطية، ويمكن تشغيلها بينما لا يزال التطبيق يخدم حركة المرور.
قائمة مراجعة سريعة لكتابة النصوص التعويضية
- تأكد من أن المخطط صحيح قبل كتابة النص. إذا كان الهيكل يحتاج إلى تغيير، تعامل مع ذلك أولاً.
- اكتب النص كترحيل جديد، وليس كإصلاح عاجل (hotfix) يتم تطبيقه مباشرة على قاعدة البيانات.
- اجعل كل عبارة غير قابلة للتكرار. تحقق من الشروط قبل التحديث أو الإدراج.
- اختبر النص مقابل نسخة من بيانات الإنتاج، وليس فقط مقابل قاعدة بيانات فارغة.
- قم بتضمين تسجيل أو تعليقات تشرح سبب الحاجة إلى تصحيح البيانات، حتى يفهم أعضاء الفريق المستقبليون السياق.
الخلاصة
عندما يحدث خطأ في الترحيل، غالبًا ما يكون رد الفعل الغريزي هو التراجع عن كل شيء. لكن التراجع عن المخطط هو عملية ثقيلة يمكن أن تعطل التطبيق وتفقد البيانات الصحيحة. تمنحك النصوص التعويضية أداة أخف وأكثر دقة. إنها تتيح لك إصلاح البيانات مع الحفاظ على المخطط الذي يعمل. في المرة القادمة التي ترى فيها ترحيلاً نجح ولكن ترك وراءه بيانات خاطئة، اسأل نفسك: هل المخطط خاطئ، أم البيانات خاطئة؟ إذا كانت الإجابة هي البيانات، فاكتب نصًا تعويضيًا. إنه أسرع وأكثر أمانًا وأقل إزعاجًا من التراجع.