GitHub की हर object के पास दो ID होती हैं
(greptile.com)- GitHub API का उपयोग करते समय PR comment link generation feature में ID mismatch की वजह से लिंक काम नहीं करने की समस्या सामने आई
- जांच में पता चला कि GitHub GraphQL के node ID और REST API के database ID नाम की दो अलग ID systems को साथ-साथ इस्तेमाल करता है
- node ID को base64 decode करने पर यह पुष्टि हुई कि उसके निचले 32-bit में database ID शामिल है, इसलिए एक सरल bitmask operation से इसे बदला जा सकता है
- अतिरिक्त विश्लेषण से यह भी सामने आया कि GitHub MessagePack-आधारित नए ID format और string-आधारित legacy format को मिलाकर इस्तेमाल कर रहा है
- यह संरचना GitHub के आंतरिक object identification system की द्वैधता को दिखाती है, और developers को API integration के समय सावधान रहने की जरूरत है
GitHub की दोहरी ID system की खोज
- Greptile के AI code review tool feature को विकसित करते समय GitHub PR comment links के काम न करने की समस्या आई
- saved comment ID को URL में जोड़ा गया, लेकिन click करने पर GitHub page पर नहीं गया
- GitHub docs देखने पर पता चला कि GraphQL API का node ID और REST API का database ID अलग-अलग systems में मौजूद हैं
- node ID उदाहरण:
PRRC_kwDOL4aMSs6Tkzl8 - database ID उदाहरण:
2475899260
- node ID उदाहरण:
- node ID, GitHub भर में objects को globally identify करने के लिए इस्तेमाल होने वाली base64-encoded string है, जबकि database ID integer URL identifier के रूप में उपयोग होती है
node ID और database ID के संबंध का विश्लेषण
- कई PR comments के node ID और database ID की तुलना करने पर पता चला कि दोनों values एक निश्चित अंतराल के साथ बढ़ती हैं
- node ID के base64 हिस्से को decode करने पर 96-bit integer बना, और इस value के निचले 32-bit database ID से मेल खाते थे
- उदाहरण:
PRRC_kwDOL4aMSs6Tkzl8→ निचले 32-bit =2475899260
- उदाहरण:
- एक सरल bitmask operation से database ID निकाली जा सकती है
decoded & ((1 << 32) - 1)जैसे operation से conversion किया जा सकता है
GitHub का legacy ID format
- पुराने repository (
torvalds/linux) के node ID को decode करने पर अलग format की string दिखाई दी- उदाहरण:
MDEwOlJlcG9zaXRvcnkyMzI1Mjk4→010:Repository2325298
- उदाहरण:
- इस format की संरचना
[object type number]:[object name][Database ID]है, यानी यह स्पष्ट string-based identifier है - tree object के मामले में
04:Tree2325298:7201bfb9...जैसा रूप मिलता है, जिसमें repository ID और SHA value दोनों शामिल होते हैं - GitHub legacy format और नए format दोनों का समानांतर उपयोग कर रहा है, और object type व creation time के अनुसार format बदलता है
नए node ID format की संरचना
- GitHub की GraphQL migration guide साफ कहती है कि node ID को opaque string की तरह मानना चाहिए, लेकिन इसकी आंतरिक संरचना मौजूद है
- base64 decode करने के बाद MessagePack से unpack करने पर array रूप में data दिखाई देता है
- उदाहरण:
[0, 47954445, 2475899260]
- उदाहरण:
- array की संरचना
- पहला element (0): version identifier होने का अनुमान
- दूसरा element (47954445): repository का database ID
- तीसरा element (2475899260): object का database ID
- object type के अनुसार array की length अलग होती है, और commit में SHA शामिल होता है, जबकि repository में सिर्फ दो elements होते हैं
व्यावहारिक उपयोग और निष्कर्ष
- नए node ID से database ID निकालने वाले Python code का उदाहरण
import base64, msgpack def node_id_to_database_id(node_id): prefix, encoded = node_id.split('_') packed = base64.b64decode(encoded) array = msgpack.unpackb(packed) return array[-1] - इस तरीके से PR comment का database ID सीधे निकालकर URL link की समस्या हल की जा सकती है
- GitHub फिलहाल MessagePack-आधारित नए ID system और string-based legacy system दोनों को एक साथ बनाए हुए है
- यह संरचना GitHub के आंतरिक transition process और compatibility बनाए रखने के प्रयास को दिखाती है, और API इस्तेमाल करने वाले developers को ID format के अंतर पर ध्यान देना चाहिए
1 टिप्पणियां
Hacker News टिप्पणियाँ
नवीनतम GitHub global node ID को
'X-Github-Next-Global-ID'हेडर के ज़रिए जबरन इस्तेमाल कराया जा सकता हैID, ऑब्जेक्ट के type prefix और base64-encoded msgpack payload से मिलकर बनता है
उदाहरण के लिए मेरा user ID
"U_kgDOAAhEkg"[0, 541842]में decode होता है, जो REST API केdatabaseIdसे मेल खाता हैलेकिन ऐसी internal implementation पर निर्भर न करें; GraphQL API के
databaseIdफ़ील्ड को सीधे query करना बेहतर हैसंबंधित दस्तावेज़: GraphQL global node ID migration guide, मेरी GitHub user जानकारी, CyberChef decoding उदाहरण, GitHub ETag implementation
इस तरह decode करना नाज़ुक लगता है
GraphQL का global node ID मूल रूप से opaque होना चाहिए
GitHub के कई types (
PullRequestआदि)databaseIdफ़ील्ड देते हैं, इसलिए वही इस्तेमाल करना सही हैज़्यादातर GraphQL APIs type name और DB ID को base64 में encode करती हैं, लेकिन यह नियम हमेशा बना रहेगा इसकी गारंटी नहीं है
संदर्भ: PullRequest object docs, GraphQL global ID spec
permalink,urlजैसे फ़ील्ड औरUniformResourceLocatableinterface मौजूद हैं, इसलिए URL को खुद बनाने की ज़रूरत नहीं हैइसी वजह से API permalink देता है। ID या link pattern कभी भी बदल सकते हैं
pagination token में भी यह तरीका अक्सर इस्तेमाल होता है
010:Repository2325298जैसी ID की संरचना साफ़ दिखती है010type enum है,Repositoryनाम है, और2325298DB ID हैयानी यह length prefix जैसा रूप है।
Repository10 अक्षरों का है,Tree4 काOpus 4.5 को यह GitHub ID decoding trick पता है, और यह अपने-आप decoding code लिख देता है
लेखक ने जो पाया है वह तकनीकी रूप से सही है, लेकिन documented नहीं है और supported भी नहीं है
GitHub पहले भी node ID की internal structure को चुपचाप बदल चुका है
अगर वे MessagePack array में field जोड़ दें, encoding बदल दें, encrypt कर दें, या UUID-आधारित format पर चले जाएँ,
तो ऐसी internal structure पर निर्भर सिस्टम तुरंत टूट जाएगा
मैं जिन GitHub identifiers को स्पष्ट रूप से store करता हूँ, वे ज़्यादातर immutable URL keys (issue/PR नंबर या commit hash) होते हैं
comment ID को JSON blob के अंदर वैसे ही रख देता हूँ
हर चीज़ को normalize करने की ज़रूरत नहीं होती। JSON काफ़ी तेज़ है
जब तक आप comment स्तर पर cross-query नहीं कर रहे, performance समस्या बनने की संभावना बहुत कम है
अगर repository का नाम बदल जाए या उसे किसी दूसरी organization में move कर दिया जाए, तो URL बदल सकता है
पुराने v3 API में ID नहीं थी, इसलिए अगर कोई username या repository name बदल देता था, तो यह ट्रैक करना मुश्किल हो जाता था कि वह कौन है
इसलिए मैंने team-स्तरीय ownership management system खुद बनाया
Terraform provider अच्छा नहीं था, इसलिए offboarding के समय “जो एकमात्र admin था वही चला गया” जैसी समस्याएँ बार-बार आती थीं
हर repository किसी team की ownership में होती है, और access permission भी सिर्फ़ team स्तर पर दी जाती है
ऐसा team-based access control सिर्फ़ GitHub ही नहीं, दूसरे systems में भी उपयोगी है
यह Hyrum’s Law का क्लासिक मामला है — जैसे ही लोग undocumented behavior पर निर्भर होने लगते हैं, वह अंततः टूटता ही है
database design में आमतौर पर बाहर की दुनिया को opaque natural key दी जाती है, और अंदर incrementing integer ID इस्तेमाल होती है
लेकिन composite ID इस्तेमाल करने पर यह समस्या कुछ कम हो जाती है
उदाहरण के लिए अगर repository ID के भीतर object ID शामिल हो, तो ID बढ़ाने पर भी उसी repository के objects ही explore किए जा सकेंगे
इसमें entropy या timestamp मिला दिया जाए तो दुरुपयोग लगभग असंभव हो जाता है
इसलिए अर्थहीन surrogate key को expose करना ज़्यादा सुरक्षित है
उदाहरण के लिए YouTube अंदरूनी तौर पर index number इस्तेमाल करे, फिर भी बाहर की दुनिया को अर्थहीन code-आधारित ID देता है
अब समझ आता है कि पिछले कुछ वर्षों में GitHub team ने Rails में sharded/multi-database support को इतना क्यों बढ़ाया है