كيف بنيت نظام مزامنة "اضبطه وانسه" (Set It and Forget It)
تتغير أسعار المنتجات في أماكن عديدة؛ سواء في لوحة التحكم (admin panel)، أو عبر عمليات الاستيراد الجماعي (bulk imports)، أو من خلال API webhooks.
إذا كنت ترغب في مزامنة هذه التغييرات مع سوق خارجي (external marketplace)، فستواجه مشكلة. إن إضافة استدعاء للمزامنة في كل مسار برمجي هو خطأ؛ فمن المؤكد أنك ستنسى أحدها، أو ستتسبب في تعطل أحدها، مما يجعل عملية الصيانة كابوساً.
تحل Django signals هذه المشكلة، حيث يمكنك الربط بحدث حفظ النموذج (model save event)، مما يسمح بالتقاط كل تغيير في مكان واحد.
لكن الـ signals بها عيب؛ فإذا قمت بتحديث 100 سعر دفعة واحدة، سيتم إطلاق الـ signal مئة مرة، مما يؤدي إلى 100 استدعاء لـ API. سيؤدي ذلك إما إلى تجاوز حدود معدل الطلبات (rate limits) أو إهدار الموارد.
أستخدم نمطاً مكوناً من ثلاثة أجزاء لإصلاح ذلك:
• معالج إشارات (signal handler) يقوم بجمع المعرفات (IDs) بدلاً من التنفيذ الفوري.
• مجموعة (set) خاصة بكل خيط معالجة (per-thread) لإزالة التكرارات.
• دالة استدعاء (flush callback) تستخدم transaction.on_commit لمعالجة كل شيء دفعة واحدة.
إليك طريقة عمل ذلك.
استخدم
threading.local()لا تستخدم متغيراً عاماً (global variable)؛ فالمتغيرات العامة تشارك الحالة عبر الطلبات المختلفة، مما يؤدي إلى تسرب البيانات. بينما يحافظthreading.local()على عزل البيانات داخل خيط معالجة واحد.سجّل ولا تنفذ يقوم معالج الإشارة ببساطة بإضافة معرف المنتج (product ID) إلى مجموعة (set). ثم يخبر Django بتشغيل دالة المسح (flush function) فقط بعد نجاح عملية المعاملة في قاعدة البيانات (database transaction). هذا يمنع مزامنة البيانات التي فشل حفظها.
معالجة العمل في دفعات (Batching) عند إتمام المعاملة (transaction commits)، يتم تشغيل دالة المسح. تقوم بنسخ المجموعة وتفريغها، ثم ترسل قائمة المعرفات بالكامل إلى طبقة الخدمة (service layer).
تقوم طبقة الخدمة بإجراء استعلام جماعي واحد (bulk query) لجلب جميع المنتجات، ثم تقوم بتجميعها حسب المتجر. وأخيراً، ترسل مهمة واحدة فقط إلى Celery لكل متجر.
الفوائد واضحة:
- إزالة التكرار تتم تلقائياً؛ حيث تتولى الـ set هذه المهمة عنك.
- أمان المعاملات مدمج؛ فلن تقوم أبداً بمزامنة بيانات تم التراجع عنها (rolled-back).
- الكفاءة عالية؛ حيث تتجنب مشاكل استعلامات N+1.
- الموثوقية عالية؛ حيث يتولى Celery عمليات إعادة المحاولة في حال فشل الـ API.
أنت تبني النظام مرة واحدة، وكل ميزة جديدة تضيفها لاحقاً ستعمل مع نظام المزامنة تلقائياً.
كيف تتعامل مع مزامنة الـ API الخارجية في Django؟ هل تستخدم الـ signals أم نمطاً مختلفاً؟
المصدر: https://dev.to/acel/how-i-built-a-set-it-and-forget-it-sync-system-with-django-signals-2ld7