ניקוד הונאה בזמן אמת בקנה מידה גדול
כיצד tracio.ai מעבדת 50K אירועים בשנייה עם ניקוד מתחת ל-50ms באמצעות עיבוד זרמים, וקטורי אותות מחושבים מראש ומטמון בקצה.
ניקוד הונאה בקנה מידה גדול דורש ארכיטקטורה שונה מהיסוד מעיבוד אצווה (batch). כאשר תשלום עובר אישור או חשבון נוצר, יש לך מילישניות — לא דקות — להחזיר ציון סיכון. ב-tracio.ai אנחנו מעבדים מעל 50,000 אירועים בשנייה עם השהיית ניקוד חציונית של 22ms. מאמר זה מסביר את הארכיטקטורה שמאפשרת זאת.
צינור הניקוד
כל אירוע נכנס עובר צינור תלת-שלבי: העשרת אותות, חישוב וקטורים וניקוד סיכון. העשרת האותות מצרפת נתוני מודיעין מכשירים — טביעת האצבע של המבקר, תוצאות זיהוי בוטים, מודיעין IP והתנהגות היסטורית — לאירוע הגולמי. חישוב הווקטורים ממיר את האותות המועשרים הללו לווקטור מאפיינים באורך קבוע, המותאם למודל הניקוד שלנו. ניקוד הסיכון מריץ את הווקטור דרך המודל המאומן שלנו ומחזיר ציון בין 0.0 ל-1.0.
החלטת התכן המרכזית היא שההעשרה וחישוב הווקטורים מופרדים מהניקוד. נתוני ההעשרה מחושבים מראש ונשמרים במטמון. כאשר מבקר טוען עמוד, אנחנו מחשבים את פרופיל המכשיר שלו ומאחסנים אותו ב-Redis עם TTL של 60 דקות. כאשר בקשת ניקוד מגיעה — בדרך כלל מופעלת על ידי תשלום או התחברות — אנחנו שולפים את הפרופיל המחושב מראש במקום לחשב אותו מחדש. הדבר מקצר את השהיית הניקוד מ-200ms ומעלה לפחות מ-30ms.
עיבוד זרמים עם Go
שכבת הקליטה שלנו כתובה ב-Go ומשתמשת בארכיטקטורת fan-out. אירועים נכנסים מגיעים דרך HTTP POST ומונחים מיד על ערוץ פנימי. מאגר של goroutines עובדים קורא מהערוץ הזה, מבצע העשרה, וכותב את האירועים המועשרים ל-ClickHouse לצורכי אנליטיקה ולתור ניקוד לעיבוד בזמן אמת. מאגר ה-fan-out מתרחב באופן דינמי בהתאם לעומק התור.
בחרנו ב-Go לשכבת הקליטה בזכות פרימיטיבי המקביליות המצוינים שלה והקצאת הזיכרון הצפויה. כל goroutine עובד צורך כ-4KB של שטח מחסנית, מה שמאפשר לנו להריץ אלפי עובדים במקביל על צומת יחיד. השהיות אספן האשפה (garbage collector) שמתחת למילישנייה הן קריטיות לשמירה על השהייה עקבית בתפוקה גבוהה.
מטמון בקצה ווקטורי אותות
עבור הלקוחות בעלי הנפח הגבוה ביותר שלנו, אנחנו פורסים מודלי ניקוד בקצה באמצעות מטמון וקטורי אותות מחושבים מראש. כאשר מכשיר נראה בפעם הראשונה, אנחנו מחשבים את וקטור האותות המלא שלו ומאחסנים אותו במטמון הקצה שלנו (נפרס על Cloudflare Workers KV). בקשות ניקוד עוקבות עבור אותו מכשיר שולפות את הווקטור מהמטמון ומריצות ניקוד מקומית בקצה, ומשיגות השהייה מתחת ל-10ms.
מודל הניקוד בקצה הוא גרסה מזוקקת של המודל המלא שלנו — קטן ומהיר יותר, אך מותאם לאותם יעדי דיוק. אנחנו מאמנים מחדש את מודל הקצה שבועית ופורסים עדכונים באמצעות פריסה מתגלגלת (rolling deployment) כדי להימנע מסופות ביטול מטמון. המודל המלא רץ בצד השרת עבור מקרים שבהם ביטחון מודל הקצה נמוך מסף הניתן להגדרה.
ClickHouse לאנליטיקה
כל האירועים המועשרים מאוחסנים ב-ClickHouse, מסד הנתונים האנליטי מבוסס-העמודות שלנו. הדחיסה וביצועי השאילתות של ClickHouse מאפשרים לנו לאחסן מיליארדי אירועים תוך תמיכה בשאילתות אנליטיות בזמן אמת. הלקוחות שלנו משתמשים באנליטיקה זו כדי להבין דפוסי הונאה, לכוונן ספי ניקוד ולחקור אירועים בודדים.
אנחנו משתמשים בתצוגות מתומצתות (materialized views) ב-ClickHouse כדי לתחזק מדדים מצטברים מראש: שיעור הונאה לפי מדינה, התפלגות ניקוד לפי סוג מכשיר ושיעורי חיוב-שווא לפי סף. תצוגות מתומצתות אלו מתעדכנות בזמן אמת עם הגעת האירועים, ומספקות מדדים מוכנים ללוח בקרה ללא שאילתות צבירה יקרות.
לקחים שנלמדו
בניית מערכת ניקוד בזמן אמת לימדה אותנו כמה לקחים. ראשית, חישוב מראש הוא האופטימיזציה החשובה ביותר — כל עבודה שאפשר לבצע לפני שבקשת הניקוד מגיעה היא עבודה שאינה נזקפת לחובת תקציב ההשהייה שלך. שנית, מודל המקביליות של Go מתאים היטב לעיבוד אירועים בתפוקה גבוהה, אך יש להקפיד על משמעת בהקצאת זיכרון כדי להימנע מלחץ על ה-GC. שלישית, פריסה בקצה היא מהפכנית מבחינת השהייה, אך דורשת ניהול מודלים זהיר כדי להימנע מתחזיות מיושנות.