Aller au contenu
HA_

← Tous les articles

· 3 min de lecture

Bloquer les clics publicitaires invalides pendant que la campagne tourne encore

Comment ClickFortify transforme un flux de clics publicitaires en exclusions dans Google Ads et Meta, et pourquoi le système repose sur des services orientés événements au-dessus de NATS.

architectureevent-drivenadtech

Traduction automatique. Lire l’original

Les annonceurs paient chaque clic. Une partie de ces clics provient de bots, de VPN, de proxys et de data centers, et un rapport qui arrive des semaines plus tard ne fait que constater la perte. ClickFortify est un projet indépendant que je conçois et exploite seul. Son rôle : repérer le trafic invalide et l’exclure pendant que la campagne dépense encore.

La nature du problème

Trois propriétés orientent presque toutes les décisions de conception :

  • Ça ne s’arrête jamais. Les clics arrivent en continu, jour et nuit, sur tous les fuseaux horaires.
  • Chaque annonceur est un tenant. Chacun a ses propres comptes, règles et données, qui ne doivent jamais se mélanger.
  • L’action se passe ailleurs. Détecter un mauvais clic ne représente que la moitié du travail. L’exclusion doit être réécrite dans le compte publicitaire Google Ads ou Meta de l’annonceur via leurs API, qui ont des quotas et échouent parfois.

Orienté événements, à dessein

Collecter les clics, les scorer et synchroniser les exclusions ont des profils de charge très différents. La collecte est irrégulière, avec des pics, et doit rester rapide. Le scoring est limité par le CPU. La synchronisation est limitée par l’API de quelqu’un d’autre. Les réunir dans un seul processus laisserait la partie la plus lente imposer son rythme à toutes les autres.

Chaque responsabilité est donc un service à part entière, relié aux autres par des événements sur NATS. Un clic est collecté puis publié ; le scoring s’y abonne, décide et publie une décision ; la synchronisation récupère les décisions qui nécessitent une exclusion. Chaque service évolue indépendamment, et une API de plateforme publicitaire lente ne bloque jamais la collecte.

Scorer un clic

Aucun signal ne prouve à lui seul qu’un clic est invalide : le scoring en combine donc plusieurs, à savoir des signaux comportementaux, l’empreinte d’appareil (device fingerprinting) et le filtrage du trafic issu de VPN, de proxys et de data centers. Séparer le scoring de la collecte permet de modifier les règles sans toucher à la partie qui doit rester disponible.

Réécrire sans marteler les API

La réécriture est la partie qui échoue de manière intéressante. Les exclusions passent par des files de tâches Redis Bull, de sorte que chaque synchronisation bénéficie de nouvelles tentatives avec backoff, et qu’une rafale de décisions devient un flux régulier d’appels d’API plutôt qu’un pic.

// 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
  }
);

Un identifiant de tâche stable rend l’opération idempotente : si la même IP est signalée dix fois en une minute, elle n’est synchronisée qu’une seule fois.

Multi-tenancy et migration à chaud

Comme chaque annonceur dispose d’une base de données tenant isolée, les changements importants peuvent être planifiés tenant par tenant plutôt qu’en une seule bascule risquée. C’est ainsi que j’ai mené une migration de bases de données en production, à chaud, sur plus de 500 bases tenant avec une interruption quasi nulle.

Ce que je dirais à quelqu’un qui construit la même chose

  1. Considérez la réécriture vers la plateforme publicitaire comme la partie difficile, pas la détection.
  2. Séparez les services selon leur profil de charge, pas selon les noms du domaine.
  3. Rendez chaque effet de bord externe idempotent avant d’avoir besoin de le rejouer.