हमने tracio.ai की सब-30ms पाइपलाइन कैसे बनाई
सिग्नल संग्रह से विज़िटर ID तक 30ms से कम में: Go, ClickHouse, Redis और वितरित प्रोसेसिंग का उपयोग करने वाला हमारा आर्किटेक्चर।
जब हमने tracio.ai का डिवाइस आइडेंटिफिकेशन इंजन बनाने का बीड़ा उठाया, तो हमारी एक ऐसी आवश्यकता थी जिस पर कोई समझौता नहीं हो सकता था: पूरी पाइपलाइन — एन्क्रिप्टेड सिग्नल प्राप्त करने से लेकर विज़िटर ID लौटाने तक — 95वें पर्सेंटाइल पर 30 मिलीसेकंड से कम में पूरी होनी चाहिए। यह लेख उस आर्किटेक्चर का विस्तृत विवरण है जिसे हमने इस लक्ष्य को पूरा करने के लिए बनाया।
पाइपलाइन का अवलोकन
आइडेंटिफिकेशन पाइपलाइन के पाँच चरण हैं: सिग्नल डिक्रिप्शन, सिग्नल नॉर्मलाइज़ेशन, हैश कंप्यूटेशन, आइडेंटिटी रिज़ॉल्यूशन और रिस्पॉन्स सीरियलाइज़ेशन। प्रत्येक चरण को स्वतंत्र रूप से अनुकूलित किया गया है, और जो चरण समानांतर में चल सकते हैं वे ऐसा करते हैं। कुल बजट 30ms है, जिसे मोटे तौर पर इस तरह आवंटित किया गया है: डिक्रिप्शन 2ms, नॉर्मलाइज़ेशन 3ms, हैशिंग 2ms, आइडेंटिटी रिज़ॉल्यूशन 20ms, सीरियलाइज़ेशन 1ms। बचे हुए 2ms बफ़र हैं।
सिग्नल डिक्रिप्शन क्लाइंट-साइड एन्क्रिप्टेड ट्रांसपोर्ट को उलट देता है। हम Go के क्रिप्टो पैकेजों का हार्डवेयर एक्सेलरेशन के साथ उपयोग करते हैं, जो एक सामान्य 4KB पेलोड का डिक्रिप्शन 1ms से कम में पूरा कर देते हैं। नॉर्मलाइज़ेशन सिग्नल JSON को पार्स करता है, टाइप वैलिडेट करता है और प्लेटफ़ॉर्म-विशिष्ट रूपांतरण लागू करता है — उदाहरण के लिए, वर्शन-विशिष्ट शोर हटाने के लिए यूज़र एजेंट स्ट्रिंग्स को नॉर्मलाइज़ करना।
वितरित आइडेंटिटी रिज़ॉल्यूशन
आइडेंटिटी रिज़ॉल्यूशन — यह निर्धारित करना कि यह डिवाइस पहले देखा गया है या नहीं — सबसे अधिक लेटेंसी-संवेदनशील चरण है। हम डिवाइस प्रोफ़ाइल Redis में संग्रहीत करते हैं, जिन्हें एक वितरित की-रूटिंग परत का उपयोग करके एक क्लस्टर में शार्ड किया जाता है। यह रूटिंग हार्डवेयर-टियर फ़िंगरप्रिंट के आधार पर कीज़ को वितरित करती है, जो सुनिश्चित करती है कि एक ही डिवाइस के लिए लुकअप हमेशा उसी Redis नोड पर पहुँचें।
हमारा शार्डिंग कार्यान्वयन समान वितरण सुनिश्चित करने के लिए वर्चुअल नोड्स (प्रति भौतिक नोड 150) का उपयोग करता है। जब कोई नोड जोड़ा या हटाया जाता है, तो केवल 1/N कीज़ को रीमैप करने की आवश्यकता होती है, जहाँ N नोड्स की संख्या है। हमने रूटिंग परत को Go में O(log n) लुकअप समय और शून्य आवंटन के साथ कार्यान्वित किया।
आइडेंटिटी स्टोर के रूप में Redis
हमने विकल्पों (Memcached, ScyllaDB, DynamoDB) की तुलना में Redis को इसके निरंतर सब-मिलीसेकंड रिस्पॉन्स समय और जटिल डेटा संरचनाओं के समर्थन के कारण चुना। प्रत्येक डिवाइस प्रोफ़ाइल एक Redis हैश के रूप में संग्रहीत होती है, जिसमें प्रत्येक सिग्नल टियर के हैश, विज़िटर ID, अंतिम-बार-देखे-गए टाइमस्टैम्प और कॉन्फ़िडेंस मेटाडेटा के लिए फ़ील्ड होते हैं।
आइडेंटिटी रिज़ॉल्यूशन क्वेरी एक एकल HGETALL कॉल है, जिसके बाद आने वाले सिग्नल हैश की संग्रहीत हैश से तुलना की जाती है। यदि हार्डवेयर टियर मेल खाता है, तो हम मौजूदा विज़िटर ID को उच्च कॉन्फ़िडेंस के साथ लौटाते हैं। यदि केवल सॉफ़्टवेयर टियर मेल खाता है, तो हम यह निर्धारित करने के लिए सिग्नल-स्तरीय डेटा की समानता तुलना करते हैं कि क्या यह अपडेटेड ब्राउज़र वाला वही डिवाइस है। यदि कुछ भी मेल नहीं खाता, तो हम एक नया विज़िटर ID उत्पन्न करते हैं।
इवेंट स्टोरेज के लिए ClickHouse
प्रत्येक आइडेंटिफिकेशन इवेंट असिंक्रोनस रूप से ClickHouse में लिखा जाता है। हम एक बफ़र्ड राइटर का उपयोग करते हैं जो इंसर्ट को बैच करता है — 100ms तक या 1,000 इवेंट जमा होने तक, जो भी पहले हो, इवेंट एकत्र करता है। यह बैचिंग महत्वपूर्ण है क्योंकि ClickHouse व्यक्तिगत रो इंसर्ट के बजाय बड़े इंसर्ट (एक बार में हज़ारों रो) के साथ सबसे अच्छा प्रदर्शन करता है।
हमारा ClickHouse स्कीमा दो सबसे सामान्य क्वेरी पैटर्न के लिए अनुकूलित है: किसी विशिष्ट विज़िटर ID के लिए सभी इवेंट देखना, और समयावधि पर इवेंट को एकत्रित करना। हम (visitor_id, timestamp) की प्राइमरी की वाले एक MergeTree इंजन का उपयोग करते हैं, जो तेज़ पॉइंट लुकअप और कुशल रेंज स्कैन प्रदान करता है। मटीरियलाइज़्ड व्यूज़ पूर्व-एकत्रित दैनिक और प्रति-घंटा मेट्रिक्स बनाए रखते हैं।
स्केल पर सब-30ms हासिल करना
हमारे लेटेंसी लक्ष्य को पूरा करने के लिए तीन आर्किटेक्चरल निर्णय महत्वपूर्ण थे। पहला, पाइपलाइन पूरी तरह स्ट्रीमिंग है — हम पूरे HTTP रिक्वेस्ट बॉडी के प्राप्त होने से पहले ही सिग्नल प्रोसेस करना शुरू कर देते हैं। दूसरा, Redis लुकअप स्थायी कनेक्शन वाले कनेक्शन पूलिंग का उपयोग करते हैं, जो TCP हैंडशेक ओवरहेड को समाप्त करता है। तीसरा, ClickHouse राइट्स पूरी तरह असिंक्रोनस हैं और कभी भी रिस्पॉन्स पाथ को ब्लॉक नहीं करते।
50 हज़ार रिक्वेस्ट/सेकंड के साथ लोड टेस्टिंग में, हमारी p50 लेटेंसी 12ms, p95 24ms और p99 38ms है। Redis क्लस्टर रीबैलेंसिंग के दौरान p99 कभी-कभी हमारे 30ms लक्ष्य से अधिक हो जाता है, लेकिन p95 निरंतर 30ms से नीचे रहता है। सख्त लेटेंसी आवश्यकताओं वाले ग्राहकों के लिए, हम समर्पित Redis क्लस्टर प्रदान करते हैं जो मल्टी-टेनेंट प्रतिस्पर्धा को समाप्त करते हैं।