TypeID - Stripe ID से प्रेरित type-safe, K-sortable, globally unique identifiers
(github.com/jetpack-io)- TypeID UUIDv7 का एक विस्तारित type-safe identifier है, जो Stripe API के prefix इस्तेमाल करने के तरीके से प्रेरित globally unique identifier format है
- canonical string लोअरकेस स्ट्रिंग होती है और यह तीन हिस्सों से बनी होती है: अधिकतम 63 अक्षरों का लोअरकेस snake_case ASCII type prefix,
_separator, और modified base32 में encoded 26-अक्षरों का UUIDv7 suffix - type prefix की मदद से
userID को गलती से वहाँ इस्तेमाल करने से रोका जा सकता है जहाँpostID चाहिए, और debugging के समय तुरंत समझा जा सकता है कि identifier किस entity type को दर्शाता है - TypeID, UUID का superset है, और type information हटाकर decode करने पर एक वैध UUIDv7 मिलता है
- यह K-sortable है, इसलिए database primary key के रूप में इस्तेमाल किया जा सकता है, और पूरी तरह random global ID वाले UUIDv4 की तुलना में database locality खराब होने की समस्या को कम करने के लिए बनाया गया है
- base32 encoding में URL safety, case-insensitivity, ambiguous characters से बचाव, double-click selection की सुविधा, और UUID की पारंपरिक hex encoding की तुलना में कम लंबाई जैसी विशेषताएँ हैं
- आधिकारिक implementation Go, SQL, TypeScript में उपलब्ध हैं, और नवीनतम spec version v0.3.0 है
- command-line tool से TypeID generate करना, मौजूदा TypeID का UUID decode करना, और UUID को TypeID में encode करना संभव है
typeid new prefixसे नया TypeID generate करेंtypeid decode prefix_01h2xcejqtf2nbrexx3vqjhp41से type और uuid output करेंtypeid encode prefix 0188bac7-4afa-78aa-bc3b-bd1eef28d881से TypeID generate करें
- Jetify के TypeID Converter में TypeID string को UUID में बदला जा सकता है या
prefix:UUIDformat से TypeID में convert किया जा सकता है
1 टिप्पणियां
Hacker News की टिप्पणियाँ
कुछ सुझाव हैं: prefix string को देर होने से पहले फिक्स करके डॉक्युमेंट करना बेहतर होगा
Go implementation को देखकर लगता है कि lowercase ASCII ठीक है, लेकिन
article-commentजैसे composite type के लिए यह अस्पष्ट हैजटिल प्रोजेक्ट या ORM में delimiter से बचना मुश्किल होता है, इसलिए single delimiter की अनुमति देने पर विचार किया जा सकता है
Go implementation में tests नहीं हैं, जबकि यह unit test करने में आसान code है, इसलिए इन्हें ज़रूर जोड़ना चाहिए
Go में Google के UUID implementation की तरह proper parsing function और internal byte array का उपयोग करना सही होगा, और string को rendering और prefix तक सीमित रखना बेहतर है
अभी parsing बहुत उदार है और अगर suffix खाली हो तो लगता है कि यह generation mode में चला जाता है, और
SplitNके बाद indexing करने पर underscore न हो तो panic हो सकता हैडिज़ाइन में खामी ढूँढ़ने की कोशिश की, लेकिन आखिरकार लगता है कि इसने trade-off के sweet spot को अच्छी तरह पकड़ा है
फिर भी बात सही है, और जैसे-जैसे अलग-अलग भाषाओं की implementations बढ़ेंगी, यह जाँचना होगा कि वे एक-दूसरे के साथ compatible हैं या नहीं, इसलिए और सख्त test suite जोड़ने की योजना है
prefix के बारे में, क्या चिंता इस बात की है कि spec में allowed character set परिभाषित नहीं है?
और spec में prefix भी स्पष्ट कर दिया गया है
prefix उपयोग के domain के अनुसार तय होता है
हम यह तरीका कई सालों से इस्तेमाल कर रहे हैं, लेकिन दो फ़र्क हैं
पहला, हमें नहीं लगता कि लोग वास्तव में values हाथ से टाइप करते हैं, इसलिए
lऔर1के बीच भ्रम को लेकर ज़्यादा चिंता नहीं करतेइसके बजाय, शब्दों—खासकर गाली-गलौज—के संयोग से बन जाने की संभावना कम करने के लिए
eiouvowels हटाकर base32 का उपयोग करते हैंदूसरा, salted base32 characters के दो अक्षर checksum के रूप में जोड़ते हैं
इससे value गलती से या दुर्भावनापूर्ण तरीके से invalid हो तो data store तक query किए बिना ही पता चल जाता है, समझ नहीं आता कि दूसरी implementations यह क्यों नहीं करतीं
analritaआ गया था, जिस पर शिकायत मिली थीexcluded characters में
aजोड़ने पर भी विचार किया जा सकता हैअगर मानते हैं कि इंसान इसे हाथ से टाइप नहीं करते, तो गलती से गलत value जाने का रास्ता क्या है, यह स्पष्ट नहीं है
copy/paste errors हो सकते हैं, या कोई page इसे uppercase में render कर दे, लेकिन base32 हो तो case normalize किया जा सकता है
और यह भी समझना है कि 2-byte, cryptographically insecure checksum दुर्भावनापूर्ण मामले में कैसे मदद करेगा
सोच रहे हैं कि क्या इसे TypeID spec में शामिल करना उचित होगा
इससे database lookups कितने कम हुए, यह नहीं पता, लेकिन decoding के दौरान कम अस्पष्ट errors मिलना अच्छा लगा
मुख्य लेख से अलग एक “Crockford's alphabet” लिंक भी है: https://www.crockford.com/base32.html
यह base32 स्कीम alphanumeric वर्णों में से
1से भ्रमित होने वालेI,L,0से भ्रमित होने वालेO, औरUको हटाती हैपेज में
Uको हटाने की वजह “accidental obscenity” बताई गई है, लेकिन इसका मतलब आखिर क्या है, समझ नहीं आताअगर बिना भ्रम वाले alphanumeric से एक ठीक-ठाक base32 alphabet बनाना हो, तो
O,I,Lको हटाना ही काफ़ी हैतब भी 33 अक्षर बचते हैं, इसलिए एक और हटाना पड़ता है, और क्या हटाना है इससे खास फ़र्क नहीं पड़ता, तो आख़िरी हटाए गए अक्षर के लिए बस एक मनमाना कारण जोड़ दिया गया
अगर ID यूज़र को दिखाई जाती हो तो यह वजह पूरी तरह बेमानी भी नहीं है, लेकिन जाहिर है कि इससे कोई परफेक्ट रोकथाम नहीं होती
IऔरOको पहले ही हटाकर,Uको भी हटा दें तो काफ़ी सारे खुरदरे लगने वाले तीन-अक्षरी या चार-अक्षरी संयोजन हट जाते हैंबेशक
Aहोने की वजह से कुछ संयोजन अब भी संभव हैं, लेकिन संभावना बहुत कम हो जाती हैUएक इतना भद्दा अक्षर है कि अश्लीलता के करीब पड़ता है, क्योंकि Cicero के ज़माने में इसका अस्तित्व ही नहीं थाहमारी कंपनी में QR code के लिए कई नई “radix” भी परिभाषित की गई थीं, और निजी तौर पर मुझे यह computer science का कम इस्तेमाल किया गया क्षेत्र लगता है
हम URL slug की समस्या हल कर रहे थे, और स्ट्रिंग काफ़ी लंबी होने वाली थी, इसलिए हमें लगा कि प्रति byte 5 bit से हाथ से लिखने की असुविधा कम होगी
आखिर में कुछ और अक्षर हटाने की गुंजाइश थी, और सबके दफ़्तर से जाने के बाद हम दोनों बैठकर गालियों की अपमानजनकता के हिसाब से रैंकिंग बनाकर हटाने वाले अक्षर चुन रहे थे
बाद में मैंने समझाया कि slur उससे बड़ा मुद्दा है, इसलिए
uकी जगहnहटाने पर ध्यान दिया गयाtggrतो बस प्यारा लगता है, लेकिनn**rकी वजह से कई HR टीमों के साथ असहज बातचीत करनी पड़ सकती थीअंतिम character set अब थोड़ा धुंधला याद है, लेकिन हम आम तौर पर
[a-z][0-9]संभाल रहे थे और URL में इस्तेमाल न किए जा सकने वाले या ज़ोर से पढ़ने में कठिन कई चिन्ह थेयाद के मुताबिक हमने
0,l,1तीनों हटा दिए थे, और शायद यह मानकर चल रहे थे कि हाथ से लिखना या तो पूरा uppercase में होगा या पूरा lowercase में0oसमस्या नहीं था, और1Lभी नहींCrockford encoding मुझे खास पसंद नहीं है
वास्तव में, इस तरह encoded वैल्यूज़ को technical support या analysis में संभालते समय मुझे लगा कि यह साफ़ गलती थी
यह alphabet ऐसे लक्ष्य के लिए डिज़ाइन किया गया था जो व्यवहार में कम ही आता है, जैसे फ़ोन पर identifier पढ़कर सुनाना, और यह ambiguity लाता है
encoded string से logs को
grepकरना या cross-reference करना हो तो यह तबाही है, और hyphen की अनुमति देना copy/paste तथा line-break errors का बड़ा कारण बनता हैobject identifier को सीधे टाइप करने की ज़रूरत कम ही पड़ती है, लेकिन application, chat और forum के बीच copy/paste करना, उसे email से भेजना, और log files में खोजना आम बात है
इन परिस्थितियों में pronounceability का कोई मतलब नहीं, case-insensitive होना उल्टा बाधा है, और ज़रूरत consistency तथा paste और line-break resilience की है
base58 इन ज़रूरतों के लिए ज़्यादा उपयुक्त bijective encoding है, और लंबाई में भी छोटी है
Stripe से प्रेरित होकर मैं कई सालों से
user_1BzGURpnHGn6oNru84B3Riजैसे type prefix लगे base58-encoded UUID को object identifier के रूप में इस्तेमाल कर रहा हूँहालाँकि यह भी ध्यान में रखना चाहिए कि Douglas Crockford की base32 encoding 20 साल पहले, एक काफ़ी अलग उपयोग-परिवेश के लिए डिज़ाइन की गई थी
लेकिन मैं case sensitivity को आधार मानकर नहीं चलना चाहता था, इसलिए आख़िरकार base32 की ओर झुक गया
उदाहरण के लिए, हो सकता है आप ID को filename के रूप में इस्तेमाल करना चाहें, और आपका environment case-insensitive file system से बंधा हो
TypeID हमेशा Crockford alphabet को lowercase में इस्तेमाल करता है; यह Crockford encoding के पूरे नियमों का उपयोग नहीं करता
TypeID में hyphen की अनुमति नहीं है, और न ही ambiguous अक्षरों को बदलकर एक ही ID को कई तरीकों से encode करने की अनुमति है
लेकिन कभी-कभी screenshot या screen share में दिख रहे identifier, या किसी दूसरे device पर मौजूद identifier जिसे आसानी से लिया नहीं जा सकता, उसे हाथ से दर्ज करना पड़ता है
Crockford base32 काम तो करता है, लेकिन मुझे बहुत पसंद नहीं है
निजी तौर पर मेरी पहली पसंद type prefix लगे KSUID का उपयोग करना है
इससे base62-encoded 160-bit K-sortable ID मिलती है, और अगर compatibility की वजह से 128-bit ID अनिवार्य न हो तो यह अच्छा विकल्प है
मेरी पसंदीदा base32 encoding z-base-32 है, क्योंकि यह आँखों को ज़्यादा आरामदेह लगती है: https://philzimmermann.com/docs/human-oriented-base-32-encod...
base58 की सबसे बड़ी समस्या यह है कि यह integers के लिए तो अच्छी बैठती है, लेकिन cryptographic keys जैसे arbitrary binary data के लिए कम उपयुक्त है, और case-sensitive strings देखने में अच्छी नहीं लगतीं
साफ़-सुथरा
“type-safe” prefix पसंद आया
हमारे ORM में हम इसी तरह DB के integer ID पर entity-वार tag अपने-आप जोड़ने के तरीके को tagged ids कहते थे: https://joist-orm.io/docs/advanced/tagged-ids
हमने delimiter के तौर पर
:इस्तेमाल किया था, लेकिन double-click से copy/paste करने के लिहाज़ से_न इस्तेमाल करने का थोड़ा अफ़सोस हैसिद्धांततः Joist से “DB के UUID column” को “domain model के TypeID” में बदलवाना भी काफ़ी आसान होना चाहिए, लेकिन अभी शायद यह सिर्फ user-side configuration से नहीं हो पाएगा
फिर भी यह अच्छा आइडिया है
element ID
t3_15bfi0जैसे होते हैं, औरt3_type prefix हैt3post है,t1comment है,t5subreddit है, और बाकी auto-increment primary key की base36 encoding है.) से बदल दिया गयाcolon URL encoding में
%-encoding हो जाता था, जिससे ID लंबा हो जाता था, URL बदसूरत और लंबा दिखता था, और यह बहुत परेशान करने वाला थाUUIDv7 कई सालों से HN पर लोकप्रिय है, लेकिन सोचता हूँ कि यह कब ठीक से standard बनेगा और libraries, databases, और बाकी ecosystem इसे कब native support देंगे
ज़्यादातर software UUID के अंदर के specific bits की परवाह नहीं करते, इसलिए इसे आज भी इस्तेमाल किया जा सकता है
अगर कोई software specific bits की परवाह करता है, तो उसे UUIDv4 की तरह mimic किया जा सकता है, और ऐसे bits वैसे भी random generate हो सकते हैं
अगर generation procedure चाहिए, तो उसे खुद लिख सकते हैं, और यह मुश्किल नहीं है
उदाहरण के लिए, मैं Dart UUID library maintain करता हूँ, और उसकी latest beta major release में v6, v7, और custom v8 शामिल हैं
implementations की एक सूची भी कहीं है, और मैं उस पेज पर library maintainers की सूची में हूँ, इसलिए हर नया draft आने पर authors से notification मिल जाता है
“double-click से copy/paste किया जा सकता है” वाली बात अच्छी लगी
details मायने रखते हैं
मुझे UUID से थोड़ी शिकायत है
समझ नहीं आता कि UUID versioning जैसी रस्म क्यों निभानी चाहिए, सीधे time + random को जोड़ क्यों नहीं देते
अगर locality महत्वपूर्ण नहीं है, तो 128-bit random वैसे ही इस्तेमाल किया जा सकता है
अनुभव से लगता है कि ज़्यादातर लोग सोचते हैं कि UUID को human-readable hex representation में, यहाँ तक कि hyphen सहित, store करना चाहिए
यह database, network, और memory में बहुत ज़्यादा जगह बर्बाद करता है
उदाहरण के लिए ULID https://github.com/ulid/spec छोटा है और समय store करता है, इसलिए lexicographically sort हो जाता है
हक़ीक़त दूसरे engineering problems जैसी ही है: requirements लिखिए और देखिए कि उन्हें क्या solve करता है
अलग-अलग formats के फ़ायदे पढ़ना मददगार है, क्योंकि इससे आप दूसरों की मुश्किल से सीखी बातों का लाभ उठा सकते हैं
sortability और time-based locality जैसी चीज़ें शायद सहज रूप से ध्यान में न आएँ, लेकिन अगर उनकी ज़रूरत है, तो project के 4 साल बाद यह समझने से पहले जान लेना कहीं बेहतर है कि आपने वह data फेंक दिया
कुछ UUID formats छोटे security issues भी अपने साथ लाते हैं, जैसे UUID v1 में MAC address leak होना, इसलिए उनसे बचना बेहतर है
अगर मौजूदा समाधान आपके use case के लिए सही है, तो बस वही इस्तेमाल कीजिए
अगर नहीं, तो ज़्यादा चिंता की ज़रूरत नहीं है
निजी तौर पर, जहाँ मैं UUID सबसे ज़्यादा इस्तेमाल करता हूँ, वहाँ मैंने बस इतना तय किया है: “कोई unique string भेजो, और मन हो तो अपनी पसंद की UUID library इस्तेमाल करो”
इस system में लगता है कि हर data source का अपना unique format है, लेकिन यह ठीक है
scale भी कोई समस्या नहीं है, क्योंकि यह रोज़ कुछ दसियों हज़ार entries के स्तर पर है, और UUID के आधार पर sort करने की ज़रूरत भी नहीं है
यह असली identifier से ज़्यादा, sender द्वारा हर message के लिए बनाया गया unique token है, जिसका उपयोग कई queues वाले heterogeneous system में downstream duplicate arrivals पकड़ने के लिए होता है
global uniqueness की भी ज़रूरत नहीं है; सिर्फ छोटे shard के भीतर unique होना काफ़ी है, और सिद्धांततः समय बीतने पर दोहराव हो जाए तो भी फ़र्क नहीं पड़ता
लेकिन simplicity के लिए हम system को पूरी अवधि में unique बनाए रखते हैं
जब मैं खुद generate करता हूँ, तो
/dev/urandomसे data पढ़कर उसे base64 में encode करता हूँ, और ऐसा size चुना है जो==पर ख़त्म न होयह भी किसी असली समस्या की वजह से नहीं, सिर्फ दिखने की वजह से है
फिर भी यह अब तक समझ नहीं आया कि hyphen की ज़रूरत क्यों है, और बाकी versions मुझे ज़्यादा मायने नहीं रखते
अगर UUIDv7 या UUID की खास ज़रूरत नहीं है, तो https://github.com/segmentio/ksuid कहीं बड़ा key space देता है
अगर namespace चाहिए, तो string prefix जोड़ सकते हैं, और KSUID collision की संभावना UUID के किसी भी version से बहुत कम है
sortable timestamp वाले general-purpose ID generators में ksuid सबसे अच्छा लगा, और ज़्यादातर भाषाओं के लिए इसकी libraries उपलब्ध हैं
UUID v1~v7 काफ़ी wasteful हैं
अगर आप extra bits संभाल सकते हैं, और ज़्यादातर cases में कर सकते हैं, तो KSUID एक बेहतरीन format है
[1] https://github.com/sophiabits/resource-id
लेकिन ksuid की बड़ी समस्या यह है कि यह 160-bit का है, इसलिए database के native UUID type, जैसे PostgreSQL, में फिट नहीं बैठता और performance cost आती है
1: https://github.com/svix/rust-ksuid
2: https://github.com/svix/python-ksuid
K-sortable एक शानदार concept है, और loosely ordered keys कई use cases को हल कर देती हैं
type वाली compact string representation भी पसंद है
लेकिन चिंता है कि UUID v7 के unintended side effects की वजह से कई security issues पैदा हो सकते हैं
लोगों को UUID को token की तरह इस्तेमाल नहीं करना चाहिए, और न ही database primary key के रूप में ऐसा करना चाहिए, लेकिन व्यवहार में लोग ऐसा करते हैं
UUID v4 लगभग cryptographic random होता है, इसलिए लगता है कि अब तक कई security vulnerabilities बस संयोग से टलती रही हैं
मुझे लगा था कि UUID v7 में actual random data केवल 32-bit तक रह जाता है, और developers को यह अच्छी तरह सिखाना होगा कि UUID guessable हो सकता है
सुधार: लगता है कि v7 UUID की guessability के बारे में मैंने गलत पढ़ लिया था
draft में random bits के लिए CSPRNG की सिफारिश है, entropy कम से कम 74-bit है, और इसे “unguessable” बनाने के लिए design किया गया है
security-related use cases के लिए “UUID v4” इस्तेमाल करने को कहा गया है, लेकिन शायद उसका मतलब timestamp से जुड़े अर्थों के कारण हो सकता है
अगर नाम UUIDv4 और UUIDv7 हों, तो क्या यह लगातार याद रखना कि कौन-सा database के लिए अच्छा है और कौन-सा one-time token के लिए, अंतहीन confusion नहीं पैदा करेगा?
कोई backward-compatible समाधान भी समझ में नहीं आता
Elixir में v4 UUID बनाने के लिए
UUID.uuid4()function इस्तेमाल होता हैसिद्धांत रूप में code में इसके usage को scan किया जा सकता है, लेकिन ऐसी चीज़ें कुल मिलाकर error की संभावना बढ़ाती हैं
v3 जैसे hash-based variants के साथ भी यही हुआ
v4 simple है और उसके misuse की संभावना कम है