समस्या का विवरण और यह कब लागू होता है
चार क्षेत्रों (regions) में एक डिस्ट्रिब्यूटेड यूनिक ID जनरेटर डिज़ाइन करें। प्रत्येक क्षेत्र में अधिकतम 200 जनरेटर वर्कर हैं। पूरा फ्लीट अधिकतम 2 मिलियन IDs प्रति सेकंड उत्पन्न करता है, जबकि कोई भी एक वर्कर एक मिलीसेकंड में 3,000 IDs तक उत्पन्न कर सकता है। प्रत्येक ID एक धनात्मक 64-बिट पूर्णांक होना चाहिए, विश्व स्तर पर यूनिक होना चाहिए, निर्माण समय के अनुसार मोटे तौर पर क्रमबद्ध होना चाहिए, और कम से कम 60 वर्षों तक उपयोग योग्य होना चाहिए। हॉट पाथ प्रत्येक ID के लिए नेटवर्क अनुरोध नहीं कर सकता है। विलंबता (latency) का लक्ष्य p99 5 मिलीसेकंड या उससे कम है और उपलब्धता (availability) का लक्ष्य 99.99% है, लेकिन डुप्लिकेट का जोखिम लेने के बजाय जनरेटर को रुक जाना चाहिए।
Gaps की अनुमति है, और क्षेत्रों में सख्त मोनोटोनिक क्रम की आवश्यकता नहीं है। IDs आंतरिक संदेश पहचानकर्ता और डेटाबेस प्राथमिक कुंजियाँ (primary keys) हैं, इसलिए पूर्वानुमेयता (predictability) फिलहाल स्वीकार्य है। यदि किसी पहचानकर्ता को अविश्वसनीय क्लाइंट्स के सामने उजागर किया जाना है, तो एक अलग समाधान की आवश्यकता होगी। ये क्षमताएं और SLOs साक्षात्कार की सीमाएं हैं, किसी कंपनी के प्रोडक्शन ट्रैफ़िक के बारे में दावे नहीं हैं।
यह प्रश्न सीनियर बैकएंड, इंफ्रास्ट्रक्चर और सिस्टम डिज़ाइन साक्षात्कारों के लिए उपयुक्त है। वास्तविक कार्य जीवनकाल (lifetime), वर्कर संख्या और मिलीसेकंड बर्स्ट आवश्यकताओं को एक बिट बजट में बदलना है, फिर यह सिद्ध करना है कि क्लॉक, कॉनकरेंसी और वर्कर-पहचान की विफलताएं डुप्लिकेट नहीं बना सकती हैं। केवल "Snowflake का उपयोग करें" कहना कोई डिज़ाइन नहीं है।
साक्षात्कारकर्ता क्या मूल्यांकन कर रहा है
पहला, क्या उम्मीदवार विशिष्टता (uniqueness), क्रमबद्धता (ordering), निरंतरता (continuity) और अप्रत्याशितता (unpredictability) को अलग कर सकता है? Snowflake यूनिक, मोटे तौर पर समय-क्रमबद्ध 64-बिट पूर्णांक प्रदान कर सकता है। यह स्वचालित रूप से gapless सीक्वेंस, क्षेत्रों के बीच कारण क्रम (causal order), या एक सुरक्षा टोकन प्रदान नहीं करता है। एक भिन्न आवश्यकता डिज़ाइन को बदल देती है।
दूसरा, क्या उम्मीदवार बाधाओं से लेआउट प्राप्त (derive) कर सकता है? एक मजबूत उत्तर यह गणना करता है कि 41 टाइमस्टैम्प बिट्स, 10 वर्कर बिट्स और 12 सीक्वेंस बिट्स क्या कवर करते हैं, फिर 60 साल के जीवनकाल, 800 वर्कर्स और प्रति वर्कर प्रति मिलीसेकंड 3,000 IDs की जांच करता है। यह क्लासिक विभाजन की नकल नहीं करता और बाद में कोई औचित्य नहीं ढूंढता।
तीसरा, क्या उम्मीदवार यह सिद्ध कर सकता है कि IDs दोहराई नहीं जाती हैं? शुद्धता के लिए किसी भी ओवरलैपिंग समय सीमा के भीतर एक वर्कर कोड के लिए एक सक्रिय ओनर की आवश्यकता होती है, एक मिलीसेकंड के भीतर उस वर्कर द्वारा कोई सीक्वेंस पुन: उपयोग न हो, और कोई क्लॉक रोलबैक या रीस्टार्ट न हो जो पहले उपयोग किए गए टाइमस्टैम्प-और-सीक्वेंस रेंज में फिर से प्रवेश करे।
चौथा, क्या उम्मीदवार विफलता डिज़ाइन (failure design) को पूरा कर सकता है? सीक्वेंस की समाप्ति, क्लॉक का पीछे जाना, खोई हुई लीज (lost leases), पुनर्जीवित प्रोसेस (resurrected processes), क्षेत्रीय अलगाव और टाइमस्टैम्प की समाप्ति, ये सभी विशिष्टता या उपलब्धता के लिए खतरा पैदा कर सकते हैं। उत्तर में स्पष्ट स्टॉप स्थितियां, अलर्ट और रिकवरी प्रक्रियाएं होनी चाहिए।
पांचवां, क्या उम्मीदवार बाधाओं के आधार पर Snowflake, UUIDv7, रैंडम UUIDs, और सेंट्रलाइज्ड या सेगमेंट एलोकेशन के बीच चयन कर सकता है? 64-बिट की आवश्यकता और प्रति-ID समन्वय (coordination) न होना यहाँ Snowflake के पक्ष में जाता है। यदि 128 बिट स्वीकार्य होते और वर्कर रजिस्ट्रेशन अवांछनीय होता, तो UUIDv7 एक सरल उम्मीदवार होता।
उत्तर देने से पहले स्पष्ट करने योग्य प्रश्न
- क्या विशिष्टता नियतात्मक (deterministic) है, या टकराव (collision) की थोड़ी संभावना स्वीकार्य है? नियतात्मक विशिष्टता के लिए पृथक वर्कर नेमस्पेस की आवश्यकता होती है। यदि संभाव्य विशिष्टता स्वीकार्य है, तो UUIDv4 या UUIDv7 वर्कर आवंटन को समाप्त कर सकते हैं।
- क्या पहचानकर्ता 64 बिट का ही होना चाहिए? यदि डेटाबेस, प्रोटोकॉल और इंडेक्स 128 बिट स्वीकार करते हैं, तो मानक UUIDv7 एक सरल कंट्रोल प्लेन के साथ एक समय उपसर्ग (time prefix) और रैंडम स्पेस प्रदान करता है। यह समस्या आउटपुट को एक धनात्मक 64-बिट पूर्णांक तक सीमित करती है।
- "Ordered" की शर्त कितनी सख्त है? मोटे तौर पर समय क्रम स्थानीय क्लॉक का उपयोग कर सकता है। क्षेत्रों में सख्त कुल क्रम (strict total order) के लिए एक सेंट्रलाइज्ड सीक्वेंसर, सर्वसम्मत लॉग (consensus log), या व्यावसायिक रूप से विभाजित सीक्वेंस की आवश्यकता होती है और इसकी लागत बहुत भिन्न होती है।
- क्या Gaps की अनुमति है? सेगमेंट प्री-एलोकेशन, क्रैश और पुनः प्रयास (retries) से gaps रह सकते हैं। Gaplessness को सख्त वैश्विक वृद्धि के साथ संयोजित करने से आवंटन एक सीरियल ट्रांजेक्शन पाथ में चला जाता है। यहाँ gaps की अनुमति है।
- क्या IDs सार्वजनिक होंगी? टाइमस्टैम्प और वर्कर बिट्स निर्माण समय और परिनियोजन (deployment) जानकारी को उजागर कर सकते हैं, जबकि बढ़ते मूल्यों की गणना (enumerate) की जा सकती है। यह आंतरिक कुंजियों के लिए स्वीकार्य हो सकता है; सार्वजनिक पहचानकर्ताओं को एक अलग अपारदर्शी (opaque) मान का उपयोग करना चाहिए।
- वर्कर्स का निर्माण और प्रतिस्थापन कैसे किया जाता है? निश्चित मशीनें, ऑर्केस्ट्रेटेड कंटेनर और ऑटोस्केलिंग में अलग-अलग वर्कर-पहचान पुन: उपयोग जोखिम होते हैं। डायनेमिक वर्कर्स को लीज, फेंसिंग और एक सुरक्षित पुन: उपयोग नियम की आवश्यकता होती है।
- नेटवर्क विभाजन (network partition) के दौरान क्या प्राथमिकता होगी? मौजूदा वर्कर्स तब तक जारी रह सकते हैं जब तक उनका क्षेत्रीय कंट्रोल प्लेन स्वस्थ है और उनकी लीज सुरक्षित रहती है। जब विशिष्ट स्वामित्व सिद्ध नहीं किया जा सकता है तो एक वर्कर को रुकना चाहिए, भले ही कुछ उपलब्धता का त्याग करना पड़े।
30-सेकंड का उत्तर
"मैं 64 बिट्स को एक निश्चित साइन बिट, मिलीसेकंड समय के 41 बिट्स, क्षेत्र और वर्कर के लिए 10 बिट्स, और प्रति-मिलीसेकंड सीक्वेंस के लिए 12 बिट्स में विभाजित करूँगा। यह लगभग 69.7 वर्ष, 1,024 वर्कर कोड और 4,096 IDs प्रति वर्कर प्रति मिलीसेकंड को कवर करता है, इसलिए यह बाधाओं को पूरा करता है। प्रत्येक वर्कर स्थानीय क्रिटिकल सेक्शन के अंदर (timestamp, worker, sequence) उत्पन्न करता है, जिसमें हॉट पाथ पर कोई नेटवर्क कॉल नहीं होता है। एक क्षेत्रीय कंट्रोल प्लेन वर्कर लीज आवंटित और नवीनीकृत करता है। वर्कर सीक्वेंस समाप्त होने पर कभी भी रैप (wrap) नहीं होता है और लीज हानि या असुरक्षित क्लॉक रोलबैक पर रुक जाता है। परिणाम मोटे तौर पर समय-क्रमबद्ध होता है, न कि सभी क्षेत्रों में कड़ाई से क्रमबद्ध। यदि 128 बिट्स की अनुमति होती और मैं वर्कर रजिस्ट्रेशन को समाप्त करना चाहता, तो मैं UUIDv7 का मूल्यांकन करता।"
स्टेप-बाय-स्टेप डीप डाइव
स्टेप 1: अनुपयुक्त डिफ़ॉल्ट्स को खारिज करने के लिए बाधाओं का उपयोग करें
एक सेंट्रलाइज्ड डेटाबेस सीक्वेंस स्पष्ट क्रम प्रदान करता है, लेकिन प्रत्येक आवंटन एक साझा राइट पाथ में प्रवेश करता है। यह नो-नेटवर्क हॉट पाथ और क्षेत्रीय-अलगाव आवश्यकताओं के विपरीत है। बैचों में सीमाओं (ranges) को आवंटित करने से समन्वय कम हो जाता है लेकिन जब कोई वर्कर क्रैश हो जाता है तो gaps रह जाते हैं। Gaps की अनुमति है, इसलिए सेगमेंट एलोकेशन एक व्यवहार्य विकल्प बना हुआ है, हालांकि इसे अभी भी एक रेंज सर्विस और प्रीफेच नीति की आवश्यकता है।
UUIDv4 और UUIDv7 दोनों 128-बिट प्रारूप हैं। UUIDv4 रैंडम है और समय क्रम प्रदान नहीं करता है। RFC 9562 के तहत, UUIDv7 सबसे महत्वपूर्ण हिस्से में 48-बिट यूनिक्स मिलीसेकंड टाइमस्टैम्प रखता है और शेष स्थान का उपयोग वर्शन, वैरिएंट और रैंडम या मोनोटोनिक फ़ील्ड्स के लिए करता है। यह वर्कर्स को पंजीकृत किए बिना मोटे तौर पर क्रमबद्ध मान उत्पन्न कर सकता है, लेकिन यह इस समस्या की 64-बिट आवश्यकता का उल्लंघन करता है।
यह एक Snowflake-शैली का 64-बिट लेआउट छोड़ता है। "समन्वय-मुक्त" (Coordination-free) केवल हॉट पाथ पर प्रत्येक आवंटन पर लागू होता है; वर्कर की पहचान के लिए अभी भी एक कंट्रोल प्लेन की आवश्यकता होती है। उस लागत को छिपाने से सिस्टम समन्वय से मुक्त नहीं हो जाता है।
स्टेप 2: क्षमता से 1 + 41 + 10 + 12 लेआउट प्राप्त करें
सबसे महत्वपूर्ण बिट (MSB) को शून्य के रूप में आरक्षित करें ताकि मान एक धनात्मक हस्ताक्षरित (signed) 64-बिट पूर्णांक बना रहे, फिर शेष 63 बिट्स को निम्नानुसार आवंटित करें:
| फ़ील्ड | बिट्स | रेंज | आवश्यकता के अनुसार उपयुक्तता |
|---|---|---|---|
| साइन (Sign) | 1 | 0 पर निश्चित | BIGINT को धनात्मक रखता है |
| कस्टम युग (epoch) से मिलीसेकंड | 41 | 2^41 मिलीसेकंड, लगभग 69.7 वर्ष | 60 साल के जीवनकाल से अधिक है |
| क्षेत्र + वर्कर | 10 | 1,024 कोड | 2 क्षेत्र बिट्स × 8 स्थानीय-वर्कर बिट्स 4 × 256 वर्कर्स का समर्थन करते हैं |
| एक मिलीसेकंड के भीतर सीक्वेंस | 12 | 0–4,095, या 4,096 मान | प्रति वर्कर प्रति मिलीसेकंड 3,000 IDs से अधिक है |
कुल मिलाकर 1 + 41 + 10 + 12 = 64 बिट्स हैं। फ्लीट में अधिकतम 4 × 200 = 800 वर्कर हैं, जो 1,024 से कम हैं, और प्रति क्षेत्र 200 वर्कर 8 बिट्स द्वारा प्रदान किए गए 256 मानों के भीतर फिट होते हैं। 41-बिट समय सीमा लगभग 2^41 ÷ 1000 ÷ 60 ÷ 60 ÷ 24 ÷ 365.2425 = 69.7 वर्ष है। "धनात्मक" को कड़ाई से संतुष्ट करने के लिए, सभी-शून्य ID को आरक्षित करें और पहले आवंटन से पहले कस्टम युग (epoch) रखें। तब पहले वर्कर पर शून्य सीक्वेंस भी कभी 0 वापस नहीं कर सकता है।
2 मिलियन IDs प्रति सेकंड की फ्लीट-व्यापी दर एक समग्र क्षमता जांच है। सीक्वेंस फ़ील्ड को प्रति-वर्कर, प्रति-मिलीसेकंड बर्स्ट को पूरा करना चाहिए। प्रति-सेकंड औसत की गणना यह साबित नहीं कर सकती कि 12 बिट पर्याप्त हैं: एक अकेला वर्कर तब भी अपने सीक्वेंस को समाप्त कर सकता है जब फ्लीट-व्यापी QPS कम हो।
स्टेप 3: स्थानीय जनरेशन लागू करें और विशिष्टता साबित करें
प्रत्येक वर्कर 64-बिट पूर्णांक संचालन का उपयोग करता है और lastMs और sequence को एक लॉक या एटॉमिक क्रिटिकल सेक्शन से सुरक्षित करता है:
nextId():
lock
now = wallClockMs() - customEpochMs
if now < lastMs:
fail("clock_moved_back")
if now == lastMs:
if sequence == 4095:
now = waitUntilAfter(lastMs)
sequence = 0
else:
sequence = sequence + 1
else:
sequence = 0
lastMs = now
return (now << 22) | (workerCode << 12) | sequenceघड़ी के उसी मिलीसेकंड में रहने के दौरान 4,095 के बाद सीक्वेंस चुपचाप शून्य पर रैप नहीं होना चाहिए; यह तुरंत पहले के मान का डुप्लिकेट बना देगा। 4,096वीं ID मान्य है क्योंकि सीक्वेंस शून्य से शुरू होता है। उसी मिलीसेकंड में 4,097वें अनुरोध को अगले मिलीसेकंड की प्रतीक्षा करनी चाहिए या ओवरलोड त्रुटि प्राप्त करनी चाहिए।
विशिष्टता के प्रमाण में तीन स्थितियां हैं। विभिन्न वर्कर्स के पास अलग-अलग 10-बिट वर्कर कोड होते हैं। अलग-अलग मिलीसेकंड में एक ही वर्कर के पास अलग-अलग 41-बिट टाइमस्टैम्प होते हैं। एक ही मिलीसेकंड में एक ही वर्कर के पास अलग-अलग 12-बिट सीक्वेंस मान होते हैं। यदि फ़ील्ड सीमा में रहते हैं, वर्कर का स्वामित्व ओवरलैप नहीं होता है, और क्लॉक कभी भी उपयोग की गई स्थिति में दोबारा प्रवेश नहीं करता है, तो पूरा 63-बिट पेलोड दोहराया नहीं जा सकता है।
स्टेप 4: कंट्रोल प्लेन में वर्कर पहचान रखें
परिनियोजन कॉन्फ़िगरेशन के माध्यम से क्षेत्र के 2-बिट कोड को ठीक करते हुए, प्रत्येक क्षेत्र में एक स्वतंत्र वर्कर-लीज एलोकेटर चलाएं। कंट्रोल प्लेन स्टोर करता है:
| फ़ील्ड | उद्देश्य |
|---|---|
region_id, worker_id | 10-बिट वैश्विक वर्कर कोड बनाते हैं |
owner_id | वर्तमान प्रक्रिया या परिनियोजन इंस्टेंस की पहचान करें |
fencing_token | एक वर्कर कोड के नए और पुराने स्वामियों के बीच अंतर करें |
lease_expires_at | स्वामित्व कब तक मान्य रहता है इसकी सीमा तय करें |
timestamp_ceiling_ms | सुरक्षित पुन: उपयोग के दौरान पुराने स्वामी द्वारा उपयोग किए जा सकने वाले टाइमस्टैम्प की सीमा तय करें |
स्टार्टअप पर, एक नोड एक वर्कर ID और लीज प्राप्त करता है। ID हॉट पाथ केवल ओनर स्थिति, फेंसिंग स्थिति, इन-मेमोरी लीज सुरक्षा समय सीमा और now <= timestamp_ceiling_ms की जांच करता है; नवीनीकरण एसिंक्रोनस रूप से होता है, इसलिए आवंटन प्रत्येक ID के लिए कंट्रोल प्लेन से संपर्क नहीं करता है। जैसे ही इसकी लीज असुरक्षित होती है या यह टाइमस्टैम्प सीमा तक पहुंच जाती है, नोड जनरेशन बंद कर देता है। प्रतिस्थापन के लिए उस वर्कर ID का पुन: उपयोग करने से पहले, कंट्रोल प्लेन को पुराने ओनर को फेंस (fence) करना होगा और तब तक प्रतीक्षा करनी होगी जब तक कि नए नोड की घड़ी पुरानी लीज के timestamp_ceiling_ms से आगे न निकल जाए। फिर एक विलंबित पुराना नोड और उसका प्रतिस्थापन भी समान टाइमस्टैम्प रेंज का उपयोग नहीं कर सकते हैं।
फेंसिंग टोकन को अंतिम ID में एन्कोड नहीं किया जाता है, इसलिए टकराव होने के बाद यह उसे ठीक नहीं कर सकता है। यह जनरेशन पाथ से एक पुराने ओनर को बाहर रखता है। यदि कोई प्लेटफ़ॉर्म पुरानी प्रक्रियाओं को मज़बूती से फेंस नहीं कर सकता है, तो उसे परिनियोजन स्लॉट को लंबे समय तक चलने वाले, गैर-पुन: उपयोग किए गए वर्कर IDs सौंपने चाहिए या केवल यह दिखावा करने के बजाय कि अकेले लीज प्रोसेस के पुनरुत्थान को हल करती है, UUIDv7 का उपयोग करना चाहिए।
स्टेप 5: क्लॉक, ओवरफ्लो और पार्टिशन के लिए स्टॉप स्थितियां परिभाषित करें
now < lastMs की नीति सीमित होनी चाहिए। एक उदाहरण केवल तभी अधिकतम 5 मिलीसेकंड के रोलबैक की प्रतीक्षा करना है जब अनुरोध में पर्याप्त विलंबता बजट बचा हो; यह प्रतीक्षा 5-मिलीसेकंड p99 बजट में गिनी जाती है। बड़े रोलबैक या अपर्याप्त शेष बजट के लिए, एक पुनः प्रयास योग्य त्रुटि लौटाएं, नोड को सेवा से हटाएं और अलर्ट करें। प्रोसेस स्टार्टअप पर, वर्तमान समय की तुलना उस वर्कर के स्थायी हाई-वॉटर मार्क से करें और यदि क्लॉक पीछे है तो शुरू करने से इनकार कर दें। केवल मेमोरी में lastMs रखने से रीस्टार्ट कवर नहीं होता है।
जब सीक्वेंस समाप्त हो जाए, तो अगले मिलीसेकंड की प्रतीक्षा करें और एक sequence_exhausted मीट्रिक बढ़ाएं। बार-बार सीक्वेंस का समाप्त होना यह दर्शाता है कि लोड असममित है या बिट बजट गलत है। ट्रैफ़िक फैलाएं, वर्कर्स जोड़ें, या नए प्रारूप में अधिक सीक्वेंस बिट्स आवंटित करें। कभी भी रैप न करें।
क्षेत्रों के बीच या वैश्विक नेटवर्क से अलगाव के दौरान, 2-बिट क्षेत्र फ़ील्ड अभी भी नेमस्पेस को अलग करता है। मौजूदा वर्कर्स स्थानीय स्तर पर तब तक जारी रह सकते हैं जब तक उनकी क्षेत्रीय लीज सुरक्षित रहती है। यदि क्षेत्रीय लीज सेवा अनुपलब्ध है और सुरक्षा समय सीमा समाप्त हो जाती है, तो वे वर्कर्स रुक जाते हैं। विशिष्टता 99.99% उपलब्धता लक्ष्य से अधिक प्राथमिकता लेती है, और अपवाद को SLO और अलर्ट में प्रलेखित किया जाना चाहिए।
41-बिट टाइमस्टैम्प अंततः समाप्त हो जाता है। शेष युग (epoch) जीवनकाल को उजागर करें और वर्षों पहले माइग्रेशन शुरू करें। ओवरफ्लो के बाद कभी भी टाइमस्टैम्प रीसेट न करें या नए लेआउट के साथ उसी 64-बिट कॉलम की चुपचाप पुनर्व्याख्या न करें। क्षेत्रों को जोड़ने या वर्कर की संख्या बढ़ाने के लिए उसी प्रकार के प्रारूप माइग्रेशन की आवश्यकता होती है।
स्टेप 6: बताएं कि "ordered" और "secure" का वास्तव में क्या अर्थ है
उच्च बिट्स में समय रखने से IDs मोटे तौर पर क्रमबद्ध हो जाती हैं, लेकिन क्लॉक स्क्यू बाद के रिकॉर्ड को एक छोटी ID दे सकता है। वर्कर बिट्स एक मिलीसेकंड के अंदर ऑर्डरिंग को भी प्रभावित करते हैं। एक Snowflake ID क्षेत्रों में कारण संबंध (causality) को साबित नहीं करता है और भुगतान लेज़र के कमिट सीक्वेंस को प्रतिस्थापित नहीं कर सकता है।
यदि कोई API id > cursor के साथ वृद्धिशील सिंक्रनाइज़ेशन करता है, तो धीमी क्लॉक से देर से आने वाली ID सहेजे गए कर्सर से छोटी हो सकती है और हमेशा के लिए छूट सकती है। जब पूर्ण क्रम मायने रखता है, तो एक डेटाबेस कमिट सीक्वेंस, एक सर्वसम्मत-लॉग स्थिति, या एक व्यावसायिक पार्टीशन (जैसे बातचीत या खाता) तक सीमित सीक्वेंसर का उपयोग करें। Snowflake पहचान बना रहता है, ऑर्डरिंग प्राधिकारी नहीं।
रॉ ID एक अनुमानित निर्माण समय को भी उजागर करती है और क्षेत्र या वर्कर बिट्स को प्रकट कर सकती है। यह कोई प्रमाणीकरण क्रेडेंशियल नहीं है। एक सार्वजनिक संसाधन 64-बिट आंतरिक कुंजी रख सकता है और एक स्वतंत्र अपारदर्शी पहचानकर्ता को उजागर कर सकता है। डेटा की सुरक्षा के लिए कभी भी ID के अनुमान लगाने में कठिन होने पर भरोसा न करें।
स्टेप 7: समान बाधाओं के तहत विकल्पों की तुलना करें
| दृष्टिकोण | चौड़ाई और क्रम | समन्वय | सबसे उपयुक्त | मुख्य लागत |
|---|---|---|---|---|
| Snowflake शैली | 64-बिट, मोटे तौर पर क्रमबद्ध | स्टार्टअप और नवीनीकरण पर समन्वय; स्थानीय हॉट पाथ | 64-बिट IDs, बहुत उच्च थ्रूपुट, अनुमानित क्रम | क्लॉक और वर्कर प्रबंधन |
| UUIDv7 | 128-बिट, समय-उपसर्ग | कोई वर्कर पंजीकरण नहीं | 128 बिट स्वीकार्य हैं और वर्कर कंट्रोल प्लेन के बिना एक मानक प्रारूप पसंद किया जाता है | व्यापक मान; विशिष्टता रैंडमनेस गुणवत्ता और कार्यान्वयन पर निर्भर करती है |
| UUIDv4 | 128-बिट, रैंडम क्रम | कोई नहीं | क्रम अनावश्यक है और अपारदर्शिता मायने रखती है | खराब इंडेक्स लोकैलिटी और ID से समय का कोई अनुमान नहीं |
| सेंट्रल सीक्वेंसर / सेगमेंट्स | आमतौर पर 64-बिट, कड़ाई से या मोटे तौर पर बढ़ता हुआ | एक सेवा या डेटाबेस के माध्यम से आवंटित करें; सेगमेंट को बैच किया जा सकता है | केंद्रीय क्रम की आवश्यकता होती है या सिस्टम पहले से ही डेटाबेस पर निर्भर होता है | नेटवर्क/डेटाबेस निर्भरता; बैच gaps छोड़ते हैं |
कठिन 64-बिट बाधा और प्रति-ID नेटवर्क कॉल पर प्रतिबंध के कारण Snowflake इस समस्या में जीतता है। यदि साक्षात्कारकर्ता 64-बिट बाधा को हटा देता है, तो UUIDv7 पर पुनर्विचार करें। यदि सख्त पूर्ण क्रम की आवश्यकता है, तो स्वीकार करें कि Snowflake इसे पूरा नहीं करता है और गलत प्रिमिटिव को ठीक करने के बजाय एक सीरियलाइज्ड सीक्वेंसर की ओर बढ़ें।
स्टेप 8: विफलता इंजेक्शन (failure injection) के साथ मान्य करें
सत्यापन बिना किसी डुप्लिकेट के एक बड़े सामान्य नमूने को उत्पन्न करने से परे होना चाहिए। कम से कम इन मामलों को कवर करें:
- समय को फ्रीज करें और एक मिलीसेकंड में एक वर्कर से 4,096 IDs उत्पन्न करें। सभी यूनिक होने चाहिए; 4,097वें को प्रतीक्षा करनी चाहिए या विफल होना चाहिए।
- एक जनरेटर को समवर्ती रूप से कॉल करें और सत्यापित करें कि स्थानीय क्रिटिकल सेक्शन दो थ्रेड्स को एक सीक्वेंस का पुन: उपयोग करने से रोकता है।
- क्लॉक को 1, 5, और 2,000 मिलीसेकंड पीछे ले जाएं और प्रतीक्षा, टाइमआउट, निष्कासन और अलर्ट पाथ्स को सत्यापित करें।
- पुराने ओनर को रोकें, उसकी लीज समाप्त होने दें, नए ओनर को सक्रिय करें, फिर पुराने ओनर को फिर से शुरू करें। सत्यापित करें कि फेंसिंग बासी (stale) प्रक्रिया को जनरेट करने से रोकती है।
- चारों क्षेत्रों और सभी 800 वर्कर्स को एक ही मिलीसेकंड पर फ्रीज करें, फिर विभिन्न सीक्वेंस उत्पन्न करें और डीकोड किए गए फ़ील्ड सीमाओं और वैश्विक विशिष्टता को सत्यापित करें।
- क्षेत्रीय लीज सेवा को अलग करें। मौजूदा वर्कर्स सुरक्षित लीज अंतराल के भीतर जारी रह सकते हैं और उसके बाद फेल-क्लोज (fail closed) होना चाहिए।
- समय को
2^41 - 1तक आगे बढ़ाएं और सत्यापित करें कि अगला मिलीसेकंड अस्वीकार कर दिया गया है और माइग्रेशन अलर्ट ट्रिगर करता है।
कम से कम, प्रोडक्शन मॉनिटरिंग में clock_rollback_ms, sequence_exhausted, लीज-नवीनीकरण विफलताएं, उपलब्ध वर्कर-कोड हेडरूम, जनरेशन विलंबता, त्रुटि दर और शेष युग जीवनकाल शामिल होना चाहिए। एक डेटाबेस यूनिक बाधा रक्षा की एक उपयोगी अंतिम पंक्ति और अलर्ट स्रोत है, लेकिन टकराव को पकड़ना और पुनः प्रयास करना जनरेटर की शुद्धता का विकल्प नहीं है।
उच्च गुणवत्ता वाला नमूना उत्तर
"मैं पहले पुष्टि करूँगा कि आवश्यकता एक नियतात्मक रूप से यूनिक, धनात्मक 64-बिट ID की है। Gaps की अनुमति है, और ऑर्डरिंग केवल अनुमानित होनी चाहिए। चार क्षेत्रों, प्रति क्षेत्र 200 वर्कर्स और प्रति वर्कर प्रति मिलीसेकंड 3,000 IDs तक के साथ, मैं 128-बिट UUIDv7 या प्रत्येक ID के लिए एक सेंट्रलाइज्ड सीक्वेंसर कॉल के बजाय एक Snowflake-शैली का डिज़ाइन चुनूंगा।
सबसे महत्वपूर्ण बिट शून्य रहता है। एक 41-बिट कस्टम-युग मिलीसेकंड टाइमस्टैम्प लगभग 69.7 साल तक रहता है। दस वर्कर बिट्स 2 क्षेत्र बिट्स और 8 स्थानीय-वर्कर बिट्स में विभाजित होते हैं, जो 4 × 256 नोड्स को कवर करते हैं। एक 12-बिट सीक्वेंस प्रति वर्कर प्रति मिलीसेकंड 4,096 मान देता है। तीनों आयाम बताई गई सीमाओं से अधिक हैं और ठीक 63-बिट पेलोड का उपयोग करते हैं।
प्रत्येक वर्कर lastMs और sequence को एक स्थानीय क्रिटिकल सेक्शन में रखता है। यह समय आगे बढ़ने पर सीक्वेंस को रीसेट करता है, उसी मिलीसेकंड के भीतर बढ़ाता है, और सीक्वेंस 4,095 के बाद प्रतीक्षा करता है। यह पीछे चलने वाले क्लॉक के दौरान कभी भी जारी नहीं रहता है: विलंबता बजट के भीतर 5 मिलीसेकंड तक प्रतीक्षा हो सकती है, जबकि एक बड़ा रोलबैक वर्कर को हटा देता है और अलर्ट करता है। विभिन्न वर्कर कोड वर्कर्स को अलग करते हैं, टाइमस्टैम्प एक वर्कर के लिए मिलीसेकंड को अलग करते हैं, और सीक्वेंस एक वर्कर-मिलीसेकंड के भीतर IDs को अलग करते हैं।
मैं एक क्षेत्रीय वर्कर-लीज कंट्रोल प्लेन चलाऊंगा। नोड्स केवल स्टार्टअप पर और एसिंक्रोनस नवीनीकरण के लिए इससे संपर्क करते हैं, जबकि जनरेशन स्थानीय रहता है। एक पुराना ओनर अपनी लीज खोने के बाद रुक जाता है। अपनी वर्कर ID का पुन: उपयोग करने से पहले, एलोकेटर पुराने ओनर को फेंस करता है और यह सुनिश्चित करता है कि प्रतिस्थापन का क्लॉक पुरानी लीज की टाइमस्टैम्प सीमा से आगे है। यदि विशिष्ट स्वामित्व सिद्ध नहीं किया जा सकता है, तो वर्कर रुक जाता है।
अंत में, मैं बताऊंगा कि यह केवल अनुमानित क्रम देता है। क्षेत्रों में सख्त क्रम के लिए एक सर्वसम्मत लॉग या व्यावसायिक रूप से विभाजित सीक्वेंसर की आवश्यकता होती है, और सार्वजनिक गैर-गणना योग्य IDs को एक अलग अपारदर्शी मान की आवश्यकता होती है। सत्यापन में एक मिलीसेकंड में 4,097वां अनुरोध, समवर्ती सीक्वेंस एक्सेस, दो-सेकंड क्लॉक रोलबैक, लीज अधिग्रहण और युग समाप्ति शामिल होगी—न केवल हैप्पी पाथ।"
सामान्य गलतियाँ
- तुरंत "UUID का उपयोग करें" कहना → उत्तर कभी भी चौड़ाई, क्रम या टकराव सिमेंटिक्स को स्पष्ट नहीं करता है → पहले UUIDv4, UUIDv7 और एक नियतात्मक वर्कर नेमस्पेस को अलग करें।
- 41-10-12 की नकल करना → इसका कोई प्रमाण नहीं है कि जीवनकाल, वर्कर्स या बर्स्ट क्षमता फिट बैठती है → अलग से
2^41मिलीसेकंड,2^10वर्कर कोड, और2^12सीक्वेंस मानों की गणना करें। - केवल औसत फ्लीट QPS का उपयोग करना → प्रति-वर्कर, प्रति-मिलीसेकंड बर्स्ट अभी भी सीक्वेंस को समाप्त कर सकता है → सबसे छोटी समय इकाई और सबसे व्यस्त वर्कर पर क्षमता को मान्य करें।
- सीक्वेंस को मास्क के साथ रैप करना → वही वर्कर उसी मिलीसेकंड में एक ID दोहराता है → अगले मिलीसेकंड की प्रतीक्षा करें, बैकप्रेशर लागू करें, या विफल करें।
- कॉन्फ़िगरेशन में एक वर्कर ID डालना और वहीं रुक जाना → ऑटोस्केलिंग, कॉपी किए गए कॉन्फ़िगरेशन और प्रोसेस पुनरुत्थान दो ओनर बनाते हैं → लीज, फेंसिंग, सुरक्षित पुन: उपयोग और स्टॉप स्थितियां डिज़ाइन करें।
- रोलबैक के बाद वर्तमान वॉल क्लॉक के साथ जारी रखना → वर्कर एक टाइमस्टैम्प-और-सीक्वेंस जोड़ी को दोहरा सकता है → एक सीमा के भीतर प्रतीक्षा करें या फेल क्लोज करें, और रीस्टार्ट के बाद एक स्थायी हाई-वॉटर मार्क की जांच करें।
- मोटे तौर पर क्रम को विश्व स्तर पर मोनोटोनिक कहना → क्लॉक स्क्यू और समवर्ती वर्कर्स क्रम को बदलते हैं → सख्त क्रम की आवश्यकता होने पर एक सीरियलाइज्ड लॉग या पार्टिशन्ड सीक्वेंसर का उपयोग करें।
- ID को एक्सेस कंट्रोल के रूप में मानना → एक डिकोड करने योग्य या गणना योग्य ID अनुरोध को अधिकृत नहीं करती है → एक अलग अपारदर्शी पहचानकर्ता को उजागर करें और फिर भी सर्वर-साइड प्राधिकरण लागू करें।
- केवल लाखों सामान्य IDs का परीक्षण करना → सामान्य रैंडम लोड शायद ही कभी खतरनाक सीमाओं तक पहुँचता है → समय को फ्रीज करें और रोलबैक, लीज अधिग्रहण और फ़ील्ड समाप्ति को इंजेक्ट करें।
फॉलो-अप प्रश्न और उत्तर
फॉलो-अप 1: क्या बदलता है यदि IDs बिना किसी gaps के क्षेत्रों में सख्ती से बढ़नी चाहिए?
Snowflake अब योग्य नहीं रहता है। सख्त कुल क्रम के लिए प्रत्येक आवंटन को एक लीनियरिज़ेशन बिंदु से गुजरना पड़ता है, जैसे कि सर्वसम्मत लॉग में एक एकल सीक्वेंस स्थिति। Gaplessness का अर्थ यह भी है कि संख्या को सफल व्यावसायिक लेनदेन के साथ कमिट होना चाहिए, इसलिए अप्रयुक्त प्रीफेच किए गए सेगमेंट अस्वीकार्य हैं। नेटवर्क विभाजन के अल्पसंख्यक पक्ष को रुकना चाहिए, जिससे थ्रूपुट, उपलब्धता कम हो जाएगी और विलंबता बढ़ जाएगी। पूछें कि क्या व्यवसाय को वास्तव में gaplessness की आवश्यकता है; कई ऑडिट सिस्टम को एक अपरिवर्तनीय व्यावसायिक संदर्भ की आवश्यकता होती है, न कि एक gapless डेटाबेस प्राथमिक कुंजी की।
फॉलो-अप 2: आप पाँचवाँ क्षेत्र कैसे जोड़ेंगे या प्रति क्षेत्र 300 वर्कर्स का समर्थन कैसे करेंगे?
वर्तमान 2 क्षेत्र बिट्स और 8 स्थानीय-वर्कर बिट्स किसी भी स्थिति का प्रतिनिधित्व नहीं कर सकते हैं। लॉन्च से पहले, एक क्षेत्र बिट जोड़कर और स्थानीय-वर्कर क्षमता को कम करके 10 बिट्स को पुनर्वितरित किया जा सकता है। एक बार IDs मौजूद होने के बाद, नई सीमा के तहत पुराने मानों की ऑनलाइन पुनर्व्याख्या नहीं की जा सकती है। एक स्पष्ट नया प्रारूप पेश करें या 128-बिट पहचानकर्ताओं पर माइग्रेट करें, जिसमें रीडर्स और राइटर्स दोनों संस्करणों को पहचानते हों। बिट सीमा को चुपचाप स्थानांतरित करने से डिकोडिंग, ऑर्डरिंग और विशिष्टता की धारणाएं टूट जाती हैं।
फॉलो-अप 3: एक नोड का क्लॉक दो सेकंड पीछे चला जाता है। क्या सीक्वेंस बिट्स इसे उपलब्ध रख सकते हैं?
बिना स्टोर किए गए वर्कअराउंड के साथ सुरक्षित रूप से नहीं। रीस्टार्ट के बाद, प्रोसेस भूल सकती है कि उसने कौन सा लॉजिकल टाइमस्टैम्प उधार लिया था। बताई गई नीति के तहत, नोड को तुरंत हटाएं, अलर्ट करें और ट्रैफ़िक को कहीं और स्थानांतरित करें। इसे केवल तभी पुनर्स्थापित करें जब वॉल टाइम lastMs तक पहुंच जाए या किसी स्थायी हाई-वॉटर मार्क से सिद्ध लॉजिकल समय को पुनर्प्राप्त करने के बाद। यदि रोलबैक के माध्यम से उपलब्धता अनिवार्य है, तो जानबूझकर स्थायी लॉजिकल-क्लॉक डिज़ाइन का उपयोग करें और ओवरफ़्लो, रीस्टार्ट और ऑर्डरिंग व्यवहार को पुनः सिद्ध करें; केवल "लॉजिकल क्लॉक का उपयोग करें" कहना कठिन स्टेट-रिकवरी समस्या को छोड़ देता है।
फॉलो-अप 4: ID एक सार्वजनिक ऑर्डर URL में दिखाई देगी। आप गणना (enumeration) और बिक्री-मात्रा रिसाव को कैसे रोकते हैं?
Snowflake को आंतरिक जॉइन कुंजी के रूप में रखें और एक अलग रैंडम बाहरी पहचानकर्ता उत्पन्न करें। UUIDv4 उपयुक्त है जब पर्याप्त एन्ट्रापी और अपारदर्शिता वांछित हो; UUIDv7 एक विकल्प है यदि समय उपसर्ग लीक करना स्वीकार्य है। ऑर्डर पंक्ति पर मैपिंग को स्टोर करें। प्रत्येक ऑर्डर रीड को पहचानकर्ता प्रारूप की परवाह किए बिना वर्तमान उपयोगकर्ता को अधिकृत करना चाहिए। अप्रत्याशितता गणना जोखिम को कम करती है लेकिन प्राधिकरण को प्रतिस्थापित नहीं करती है।
फॉलो-अप 5: क्या वैश्विक कंट्रोल प्लेन से किसी क्षेत्र का संपर्क टूटने पर भी जनरेशन जारी रह सकता है?
हाँ, क्योंकि क्षेत्र बिट्स नेमस्पेस को अलग करते हैं और आवंटन के लिए वैश्विक कंट्रोल प्लेन की आवश्यकता नहीं होती है। मौजूदा वर्कर्स तब तक जारी रहते हैं जब तक उनकी क्षेत्रीय लीज सुरक्षित अंतराल के भीतर रहती है। यदि क्षेत्रीय लीज एलोकेटर भी अनुपलब्ध है, तो वे वर्कर्स रुक जाते हैं जिनकी नवीनीकरण समय सीमा समाप्त हो जाती है। क्षेत्रीय लीज स्टोर उपलब्धता के लिए इन-रीजन सर्वसम्मत क्लस्टर का उपयोग कर सकता है, लेकिन दो पार्टिशन को कभी भी एक ही वर्कर कोड को समवर्ती रूप से नवीनीकृत नहीं करना चाहिए।
फॉलो-अप 6: क्या होगा यदि एक वर्कर को अचानक प्रति मिलीसेकंड 5,000 IDs की आवश्यकता हो?
एक 12-बिट सीक्वेंस केवल 4,096 मानों की आपूर्ति करता है। तत्काल प्रतिक्रिया बैकप्रेशर और अधिक वर्कर्स के लिए लोड वितरण है; रैप करना मना है। लंबी अवधि में, टाइमस्टैम्प जीवनकाल या वर्कर क्षमता से लेकर एक नए प्रारूप में अधिक सीक्वेंस बिट्स आवंटित करें, या 64-बिट बाधा को हटा दें और UUIDv7 का उपयोग करें। लेआउट बदलने से पहले वास्तविक एक-मिलीसेकंड बर्स्ट वितरण को मापें; प्रति-सेकंड औसत इस प्रश्न का उत्तर नहीं देते हैं।
फॉलो-अप 7: यदि केवल एक बातचीत (conversation) के भीतर सख्त क्रम की आवश्यकता है, तो क्या एक वैश्विक सीक्वेंसर आवश्यक है?
नहीं। प्रत्येक बातचीत को एक स्थिर पार्टिशन में मैप करें और उस पार्टिशन के अंदर एक कमिट सीक्वेंस बनाए रखें। Snowflake वैश्विक पहचान बना रहता है, जबकि पार्टिशन सीक्वेंस बातचीत का क्रम रखता है। यह सभी क्षेत्रों को सीरियलाइज़ करने की तुलना में समन्वय और विफलता डोमेन को कम करता है। रीडर्स (conversation_id, sequence) द्वारा सॉर्ट करते हैं और कभी यह नहीं मानते कि Snowflake ID बातचीत कमिट क्रम के बराबर है।