· دقيقتا قراءة
صدّ النقرات الإعلانية غير الصالحة والحملة لا تزال قيد التشغيل
كيف يحوّل ClickFortify تدفقاً من النقرات الإعلانية إلى استبعادات داخل Google Ads وMeta، ولماذا بُني على شكل خدمات قائمة على الأحداث عبر NATS.
ترجمة آلية. اقرأ النسخة الأصلية

يدفع المعلنون مقابل كل نقرة. بعض هذه النقرات يأتي من الروبوتات وشبكات VPN والخوادم الوسيطة ومراكز البيانات، والتقرير الذي يصل بعد أسابيع لا يفعل سوى توثيق الخسارة. ClickFortify مشروع مستقل أتولى تصميمه المعماري وتشغيله بنفسي. مهمته رصد حركة المرور غير الصالحة واستبعادها بينما لا تزال الحملة تنفق ميزانيتها.
طبيعة المشكلة
ثلاث خصائص تحكم كل قرار تصميمي تقريباً:
- لا يتوقف أبداً. تصل النقرات باستمرار، على مدار الساعة، وعبر مناطق زمنية مختلفة.
- كل معلن مستأجر. لكلٍّ منهم حساباته وقواعده وبياناته الخاصة، ويجب ألا تتسرب أبداً من مستأجر إلى آخر.
- التنفيذ يحدث في مكان آخر. كشف النقرة المشبوهة ليس سوى نصف المهمة. يجب كتابة الاستبعاد في حساب Google Ads أو Meta الإعلاني الخاص بالمعلن عبر واجهات API الخاصة بهما، وهي واجهات لها حصص استخدام وتتعطل أحياناً.
قائم على الأحداث عن قصد
لجمع النقرات وتقييمها ومزامنة الاستبعادات أنماط أحمال مختلفة تماماً. الجمع متذبذب ويجب أن يبقى سريعاً. التقييم يستهلك المعالج بكثافة. والمزامنة محدودة بواجهة API يملكها طرف آخر. وضعها جميعاً في عملية واحدة يعني أن أبطأ جزء سيفرض إيقاعه على البقية.
لذلك أصبح لكل اهتمام خدمته الخاصة، وترتبط الخدمات عبر أحداث على NATS. تُجمع النقرة وتُنشر؛ تشترك خدمة التقييم فيها، فتقرر وتنشر قرارها؛ ثم تلتقط خدمة المزامنة القرارات التي تستلزم استبعاداً. تتوسع كل خدمة بمعزل عن غيرها، ولا تتسبب واجهة API بطيئة لمنصة إعلانية أبداً في تكدّس عملية الجمع.
تقييم النقرة
لا توجد إشارة واحدة تثبت أن النقرة غير صالحة، لذا يجمع التقييم بين عدة إشارات: الإشارات السلوكية، وبصمة الجهاز، وتصفية حركة VPN والخوادم الوسيطة ومراكز البيانات. وفصل التقييم عن الجمع يعني إمكانية تغيير القواعد دون المساس بالجزء الذي يجب أن يبقى عاملاً.
الكتابة العكسية دون إغراق واجهات API
الكتابة العكسية هي الجزء الذي يتعطل بطرق مثيرة للاهتمام. تمر الاستبعادات عبر طوابير مهام Redis Bull بحيث تحظى كل عملية مزامنة بإعادة محاولة مع تأخير تصاعدي، وتتحول دفعة مفاجئة من القرارات إلى تدفق ثابت من استدعاءات API بدلاً من ذروة حادة.
// Illustrative: one queued exclusion per tenant, platform and IP
await exclusions.add(
'exclude-ip',
{tenantId, platform, ip},
{
jobId: `${tenantId}:${platform}:${ip}`, // idempotent: the same IP is never queued twice
attempts: 5,
backoff: {type: 'exponential', delay: 30_000},
removeOnComplete: true
}
);معرّف المهمة الثابت يجعل العملية متساوية الأثر (idempotent): إذا رُصد عنوان IP نفسه عشر مرات في دقيقة واحدة، فإنه يُزامَن مرة واحدة فقط.
تعدد المستأجرين، وترحيل حيّ
لأن لكل معلن قاعدة بيانات معزولة خاصة به، يمكن التخطيط للتغييرات الكبيرة مستأجراً تلو الآخر بدلاً من تحويل واحد محفوف بالمخاطر. وبهذه الطريقة نفّذتُ ترحيلاً حياً لقواعد بيانات الإنتاج عبر 500+ قاعدة بيانات للمستأجرين بتوقف شبه معدوم.
ما سأقوله لمن يبني الشيء نفسه
- تعامل مع الكتابة العكسية إلى منصة الإعلانات على أنها الجزء الصعب، لا الكشف.
- افصل الخدمات بحسب نمط الحمل، لا بحسب الأسماء.
- اجعل كل أثر جانبي خارجي متساوي الأثر قبل أن تحتاج إلى إعادة محاولته.