إضافة نسخة قراءة لقاعدة بيانات PostgreSQL في SaaS تبدو بسيطة حتى تواجه تأخر النسخ على مستأجر أنشأ سجلاً وحاول قراءته فوراً. هذا كيف نتعامل مع توجيه الاستعلامات والتأخر وإدارة الاتصالات دون إدخال فئة جديدة من أخطاء الاتساق.
إضافة نسخة قراءة لقاعدة بيانات PostgreSQL في SaaS تبدو بسيطة حتى تواجه تأخر النسخ على مستأجر أنشأ سجلاً وحاول قراءته فوراً. التطبيق يقرأ بيانات قديمة، المستأجر يُبلّغ عن خطأ، وتقضي يوماً في إقناع نفسك أن عملية الكتابة نجحت فعلاً. هذا الواقع التشغيلي لنسخ القراءة في الأنظمة متعددة المستأجرين.
متى تُساعد نسخ القراءة فعلاً في أحمال عمل SaaS متعدد المستأجرين؟
تُساعد نسخ القراءة عندما يكون حمل العمل ثقيلاً في القراءة فعلاً ولا تتبع القراءات عمليات الكتابة مباشرةً. التقارير والتحليلات وعروض القوائم واستعلامات البحث التي تعمل على بيانات عمرها دقائق أو ساعات هي المرشحة المثالية لتوجيه النسخ.
نسخ القراءة لا تُساعد في أحمال العمل الثقيلة في الكتابة. النسخ المتدفق في PostgreSQL غير متزامن افتراضياً، لذا إضافة نسخة إلى قاعدة بيانات ثقيلة الكتابة لا يُقلل الحمل على القاعدة الأساسية بشكل ملحوظ.
لتطبيق SaaS نموذجي يخدم عملاء مؤسسيين في لبنان والخليج، يميل توزيع حمل العمل إلى:
- لوحات التحكم والتقارير: 60 إلى 70% من الاستعلامات، قراءة فقط، يمكن أن تكون البيانات عمرها ثوانٍ
- عروض القوائم والبحث: 20 إلى 25% من الاستعلامات، قراءة فقط، تأخر النسخ مقبول
- عمليات الكتابة والقراءة الفورية: 10 إلى 15%، يجب أن تذهب للقاعدة الأساسية
كيف توجّه الاستعلامات بين القاعدة الأساسية والنسخة في Go؟
أبسط وأأمن استراتيجية توجيه هي التوجيه الصريح لكل استعلام باستخدام مجموعتَي اتصال منفصلتين: واحدة تشير إلى القاعدة الأساسية وأخرى إلى النسخة.
type DB struct {
primary *sqlx.DB
replica *sqlx.DB
}
func (d *DB) Primary() *sqlx.DB { return d.primary }
func (d *DB) Replica() *sqlx.DB { return d.replica }
كود المستودع يتخذ قرار التوجيه بشكل صريح: استعلامات التقارير تذهب للنسخة، والقراءات التي تتبع الكتابة تذهب للقاعدة الأساسية. هذا النهج قابل للتدقيق ويمنع القراءة العرضية من النسخة بعد الكتابة.
كيف تتعامل مع مشكلة الاتساق عند الكتابة ثم القراءة فوراً؟
مشكلة "اكتب ثم اقرأ" هي الأكثر شيوعاً في التشغيل عند استخدام نسخ القراءة. مستأجر ينشئ طلباً، تُودَع الكتابة في القاعدة الأساسية، وقبل انتشار WAL للنسخة، يُعيد العميل تحميل الصفحة ويستعلم النسخة عن الطلب الجديد. الطلب غير موجود بعد.
ثلاثة أساليب تعمل في الإنتاج:
اقرأ ما كتبته عبر علامة في السياق: بعد الكتابة، ضع علامة في سياق الطلب تُلزم استخدام القاعدة الأساسية للقراءات اللاحقة في نفس الطلب.
فحص تأخر النسخ: اقرأ تأخر النسخ قبل التوجيه. إذا تجاوز التأخر عتبة معينة، ارجع إلى القاعدة الأساسية.
اقبل الاتساق التدريجي صراحةً: للميزات التي يُعدّ فيها القراءة من بيانات قديمة مقبولاً فعلاً (التقارير، لوحات الإدارة)، وثّق ذلك في الكود وأرسل رؤوس cache-control مناسبة.
كيف تُهيّئ مجموعات الاتصال للقاعدة الأساسية والنسخة؟
تحتاج مجموعتا الاتصال إلى ضبط مختلف. القاعدة الأساسية تتعامل مع الكتابة وتحتل الأقفال مما يحدّ من التزامن الفعّال. النسخة تتعامل مع القراءة فقط وتدعم مزيداً من الاتصالات المتزامنة.
إعداد معقول كنقطة بداية على AWS RDS db.t4g.medium:
- القاعدة الأساسية: 20 اتصالاً مفتوحاً كحد أقصى، 5 اتصالات خاملة
- النسخة: 40 اتصالاً مفتوحاً كحد أقصى، 10 اتصالات خاملة
راقب pg_stat_activity على كلا النظامين. إذا أظهرت القاعدة الأساسية باستمرار أكثر من 15 اتصالاً نشطاً على t4g.medium، فأنت تعمل قرب الحد وينبغي إضافة PgBouncer أمام القاعدة الأساسية.
ما الذي يجب مراقبته لصحة النسخة؟
ثلاثة مقاييس تغطي معظم المخاوف التشغيلية للنسخة:
تأخر النسخ بالثواني: استعلم pg_last_xact_replay_timestamp() على النسخة كل 30 ثانية. أنشئ تنبيهاً عندما يتجاوز التأخر 10 ثوانٍ لقاعدة بيانات تتعامل مع بيانات قريبة من الوقت الفعلي، أو 60 ثانية لنسخة تحليلات.
اتصالات النسخة مقابل الحد الأقصى: تتبّع pg_stat_activity على النسخة بنفس طريقة القاعدة الأساسية.
حالة مستقبِل WAL: يُظهر pg_stat_wal_receiver ما إذا كانت النسخة تستقبل WAL من القاعدة الأساسية بنشاط. توقّف مستقبِل WAL إشارة مبكرة لتأخر النسخة.
أبرز الدروس من بيئة الإنتاج
راقب أي قاعدة بيانات يضرب كل استعلام قبل إضافة النسخة. بدون وسم الاستعلامات بمعرف قاعدة البيانات، لا يمكنك التحقق من أن التوجيه يعمل بشكل صحيح.
ابدأ بتوجيه استعلامات التقارير والتحليلات فقط للنسخة. لا تحاول توجيه جميع القراءات فوراً. أضف فئات تدريجياً، تحقق من الاتساق في كل دفعة، ثم انتقل للتالية.
aختبر الانتقال عند فشل القاعدة الأساسية. إذا فشلت القاعدة الأساسية ورقّت AWS النسخة إلى قاعدة أساسية، يحتاج تطبيقك إلى إعادة الاتصال بنقطة النهاية الجديدة. تحقق من أن منطق إعادة الاتصال يتعامل مع هذا قبل حدوثه في الإنتاج الساعة الثالثة صباحاً.



