Realtime fraudescoring op schaal
Hoe tracio.ai 50K events/seconde verwerkt met scoring onder 50ms via stream processing, vooraf berekende signaalvectoren en edge-caching.
Fraudescoring op schaal vereist een fundamenteel andere architectuur dan batchverwerking. Wanneer een betaling wordt geautoriseerd of een account wordt aangemaakt, heb je milliseconden — geen minuten — om een risicoscore te leveren. Bij tracio.ai verwerken we meer dan 50.000 events per seconde met een mediane scoring-latency van 22ms. Dit artikel legt de architectuur uit die dit mogelijk maakt.
De scoring-pijplijn
Elk binnenkomend event doorloopt een pijplijn in drie fasen: signaalverrijking, vectorberekening en risicoscoring. Signaalverrijking koppelt device-intelligence-data — de fingerprint van de bezoeker, resultaten van bot-detectie, IP-intelligence en historisch gedrag — aan het ruwe event. Vectorberekening zet deze verrijkte signalen om in een feature-vector met vaste lengte, geoptimaliseerd voor ons scoring-model. Risicoscoring haalt de vector door ons getrainde model en geeft een score tussen 0.0 en 1.0 terug.
De belangrijkste ontwerpbeslissing is dat verrijking en vectorberekening gescheiden zijn van scoring. Verrijkingsdata wordt vooraf berekend en gecachet. Wanneer een bezoeker een pagina laadt, berekenen we zijn device-profiel en slaan we het op in Redis met een TTL van 60 minuten. Wanneer een scoring-verzoek binnenkomt — doorgaans getriggerd door een betaling of login — halen we het vooraf berekende profiel op in plaats van het opnieuw te berekenen. Dit verlaagt de scoring-latency van 200ms+ naar minder dan 30ms.
Stream processing met Go
Onze ingestielaag is geschreven in Go en gebruikt een fan-out-architectuur. Binnenkomende events arriveren via HTTP POST en worden meteen op een intern channel geplaatst. Een pool van worker-goroutines leest uit dit channel, voert verrijking uit en schrijft de verrijkte events naar ClickHouse voor analytics en naar een scoring-queue voor realtime verwerking. De fan-out-pool schaalt dynamisch op basis van de queue-diepte.
We kozen Go voor de ingestielaag vanwege de uitstekende concurrency-primitieven en de voorspelbare geheugenallocatie. Elke worker-goroutine verbruikt ongeveer 4KB stack-ruimte, waardoor we duizenden gelijktijdige workers op één node kunnen draaien. De GC-pauzes van minder dan een milliseconde zijn cruciaal om een consistente latency te behouden bij hoge doorvoer.
Edge-caching en signaalvectoren
Voor onze klanten met het hoogste volume plaatsen we scoring-modellen aan de edge met een cache van vooraf berekende signaalvectoren. Wanneer een device voor het eerst wordt gezien, berekenen we de volledige signaalvector en slaan we deze op in onze edge-cache (uitgerold op Cloudflare Workers KV). Volgende scoring-verzoeken voor hetzelfde device halen de gecachte vector op en voeren de scoring lokaal aan de edge uit, met een latency onder 10ms als resultaat.
Het edge-scoring-model is een gedistilleerde versie van ons volledige model — kleiner en sneller, maar geoptimaliseerd voor dezelfde nauwkeurigheidsdoelen. We hertrainen het edge-model wekelijks en rollen updates uit via rolling deployment om cache-invalidatiestormen te vermijden. Het volledige model draait server-side voor gevallen waarin de confidence van het edge-model onder een instelbare drempel ligt.
ClickHouse voor analytics
Alle verrijkte events worden opgeslagen in ClickHouse, onze kolomgebaseerde analytics-database. De compressie en query-performance van ClickHouse stellen ons in staat miljarden events op te slaan en tegelijk realtime analytische queries te ondersteunen. Onze klanten gebruiken deze analytics om fraudepatronen te begrijpen, scoring-drempels af te stemmen en individuele events te onderzoeken.
We gebruiken materialized views in ClickHouse om vooraf geaggregeerde metrics bij te houden: fraudepercentage per land, scoring-verdeling per device-type en false-positive-percentages per drempel. Deze materialized views worden realtime bijgewerkt zodra events binnenkomen en leveren dashboardklare metrics zonder dure aggregatiequeries.
Geleerde lessen
Het bouwen van een realtime scoring-systeem leerde ons meerdere lessen. Ten eerste is vooraf berekenen de belangrijkste optimalisatie — al het werk dat je kunt doen voordat het scoring-verzoek binnenkomt, telt niet mee in je latency-budget. Ten tweede is het concurrency-model van Go uitstekend geschikt voor eventverwerking met hoge doorvoer, maar je moet gedisciplineerd zijn met geheugenallocatie om GC-druk te vermijden. Ten derde is edge-deployment transformatief voor latency, maar vereist het zorgvuldig modelbeheer om verouderde voorspellingen te voorkomen.