प्रतिनिधि इंटरव्यू विषय

बैकएंड इंटरव्यू: आप Node.js 24 में AsyncLocalStorage कॉन्टेक्स्ट लॉस को कैसे डीबग करेंगे?

बैकएंडकठिन
Offer.cc संपादकीय टीमप्रकाशित अपडेट किया गया

प्रश्न

प्रोडक्शन लॉग कभी-कभी requestId खो देते हैं: AsyncLocalStorage HTTP एंट्री पर काम करता है लेकिन थर्ड-पार्टी कॉलबैक, इवेंट एमिटर या कस्टम thenable के बाद undefined हो जाता है। बताएं कि पहली टूटी हुई बाउंड्री का पता कैसे लगाएं, AsyncResource का उपयोग कब करें, और Node.js 24 वेरिफिकेशन को कैसे बदलता है।

प्रॉम्प्ट और संदर्भ

एक Node.js सर्विस requestId, tenant और ऑडिट डेटा को AsyncLocalStorage में स्टोर करती है। HTTP एंट्री स्टोर को पढ़ सकती है, लेकिन कुछ लॉग डेटाबेस-ड्राइवर कॉलबैक, इवेंट एमिटर, कस्टम Promise-जैसे ऑब्जेक्ट या वर्कर बाउंड्री के बाद कॉन्टेक्स्ट खो देते हैं। टीम ने हाल ही में Node.js 22 से 24 में अपग्रेड किया है और यह जानना चाहती है कि क्या डिफ़ॉल्ट इम्प्लीमेंटेशन में बदलाव इससे संबंधित है। डायग्नोसिस, रिपेयर, रिग्रेशन और फ़ॉलबैक प्लान डिज़ाइन करें।

Node.js 24 रिलीज़ नोट्स में दर्ज है कि AsyncLocalStorage अब डिफ़ॉल्ट रूप से AsyncContextFrame का उपयोग करता है। आधिकारिक दस्तावेज़ अभी भी कहते हैं कि कॉलबैक-आधारित API या कस्टम thenables को एसिंक्रोनस कार्य को सही निष्पादन संदर्भ से जोड़ने के लिए AsyncResource की आवश्यकता हो सकती है। यह इंटरव्यू हर नुकसान के लिए रनटाइम को दोष देने के बजाय एसिंक बाउंड्री, ऑब्ज़र्वेबिलिटी और वर्जन माइग्रेशन का परीक्षण करता है।

इंटरव्यूअर क्या मूल्यांकन कर रहा है

इंटरव्यूअर run() द्वारा बनाए गए कॉन्टेक्स्ट, एसिंक्रोनस-रिसोर्स लाइफटाइम, क्रॉस-थ्रेड बाउंड्री और स्टोर को ओवरराइट करने वाले व्यावसायिक कोड के बीच अंतर की तलाश कर रहा है। मजबूत उत्तर पहले ब्रेक को खोजने के लिए न्यूनतम रीप्रोडक्शन का उपयोग करते हैं, अंधाधुंध enterWith() से बचते हैं, और थर्ड-पार्टी कॉलबैक, एरर पाथ, सैंपल किए गए डायग्नोस्टिक्स और Node वर्जनों के लिए वेरिफिकेशन को परिभाषित करते हैं।

पूछने के लिए स्पष्टीकरण प्रश्न

  • क्या यह लॉस समान इवेंट लूप, वर्कर, चाइल्ड प्रोसेस या नेटवर्क बाउंड्री में होता है?
  • क्या लाइब्रेरी नेटिव प्रॉमिस, कॉलबैक, इवेंट एमिटर या कस्टम thenable का उपयोग करती है?
  • क्या स्टोर गलती से किसी अन्य run(), enterWith() या पुन: उपयोग किए गए एसिंक टास्क द्वारा ओवरराइट हो गया है?
  • क्या requestId खोने से ऑडिट और बिलिंग की शुद्धता प्रभावित होती है, या केवल लॉग कोरिलेशन?
  • क्या Node.js 22 और 24 पर स्टार्टअप फ़्लैग, डिपेंडेंसी वर्ज़न और एक्सपेरिमेंटल स्विच समान हैं?

30-सेकंड का उत्तर

"मैं Node 24 को दोष देने के बजाय एंट्री, प्रत्येक एसिंक बाउंड्री और अंतिम लॉग पर इम्यूटेबल स्टोर स्नैपशॉट रिकॉर्ड करूंगा, फिर पहले लॉस को खोजने के लिए एक न्यूनतम रीप्रोडक्शन का उपयोग करूंगा। नेटिव प्रॉमिस चेन को रिक्वेस्ट बाउंड्री पर run() का उपयोग करना चाहिए। यदि कोई थर्ड-पार्टी कॉलबैक या कस्टम thenable कॉन्टेक्स्ट को प्रोपेगेट करने में विफल रहता है, तो मैं वहां AsyncResource का उपयोग करूंगा जहां टास्क बनाया गया है और जहां इसका कॉलबैक चलता है। मैं ग्लोबल enterWith() संदूषण से बचूंगा और Node 22/24, एरर, टाइमआउट और वर्कर्स का अलग-अलग रिग्रेशन टेस्ट करूंगा। सुधार requestId की पूर्णता और व्यावसायिक शुद्धता से साबित होता है।"

चरण-दर-चरण विस्तृत उत्तर

1. कॉन्टेक्स्ट अनुबंध (कॉन्ट्रैक्ट) को परिभाषित करें

स्टोर फ़ील्ड, लाइफटाइम और इम्यूटैबिलिटी निर्दिष्ट करें। प्रति रिक्वेस्ट एक नया स्टोर बनाएं; डाउनस्ट्रीम कोड वैल्यूज को पढ़ सकता है या प्राप्त कर सकता है लेकिन रिक्वेस्ट्स में म्यूटेबल ऑब्जेक्ट्स को साझा नहीं करना चाहिए। क्या एक गुम requestId केवल एक लॉगिंग समस्या है या यह ऑथराइजेशन और टेनेंट आइसोलेशन को प्रभावित करती है, यह स्टॉप कंडीशन और रिपेयर प्राथमिकता निर्धारित करता है।

2. स्नैपशॉट के साथ पहले लॉस का पता लगाएं

एंट्री पर, डेटाबेस कॉल से पहले और बाद में, इवेंट लिसनर्स में, प्रॉमिस कॉलबैक, टाइमआउट, एरर हैंडलर्स और अंतिम लॉगिंग में getStore() से एक फ़ील्ड सारांश कैप्चर करें। केवल एक हैश या छोटा रिक्वेस्ट आईडी लॉग करें, संवेदनशील टेनेंट डेटा कभी नहीं। प्रत्येक एसिंक बाउंड्री को लेबल करें और केवल अंतिम एरर का निरीक्षण करने के बजाय वैल्यू से undefined में पहले ट्रांजिशन का पता लगाएं।

js
import { AsyncLocalStorage } from 'node:async_hooks';

const requestContext = new AsyncLocalStorage();

function contextSnapshot(label) {
  const store = requestContext.getStore();
  return { label, requestId: store?.requestId ?? null };
}

3. run(), enterWith() और रिसोर्स एसोसिएशन को अलग करें

run(store, callback) कॉलबैक और उसके द्वारा बनाए गए एसिंक कार्य में स्टोर प्रदान करता है, जिससे यह एक अच्छा रिक्वेस्ट बाउंड्री बन जाता है। enterWith() उसी सिंक्रोनस निष्पादन में बाद के इवेंट हैंडलिंग में कॉन्टेक्स्ट का विस्तार करता है और लिसनर्स को दूषित कर सकता है, इसलिए इसे सिद्ध बाउंड्री के बिना एक सामान्य सुधार नहीं होना चाहिए। एक कॉलबैक लाइब्रेरी जो एसिंक रिसोर्सेज को सही ढंग से नहीं बनाती है, उसे AsyncResource रैपर की आवश्यकता होती है।

4. थर्ड-पार्टी कॉलबैक बाउंड्री को रैप करें

पहले जांचें कि क्या लाइब्रेरी पहले से ही Node के एसिंक रिसोर्सेज का सही ढंग से उपयोग करती है। यदि ऐसा नहीं है, तो प्रति टास्क एक AsyncResource बनाएं, runInAsyncScope के साथ कॉलबैक को कॉल करें, और टास्क पूरा होने पर रिसोर्स को नष्ट कर दें। सभी रिक्वेस्ट्स में एक रिसोर्स का पुन: उपयोग न करें या केवल लॉगर को requestId के साथ पैच न करें; यह वास्तविक कॉन्टेक्स्ट ब्रेक को छुपाता है।

js
import { AsyncResource } from 'node:async_hooks';

function bindCallback(callback) {
  const resource = new AsyncResource('third-party-callback');
  return (...args) => resource.runInAsyncScope(callback, null, ...args);
}

5. thenables, इवेंट्स और वर्कर्स का अलग-अलग परीक्षण करें

एक कस्टम thenable नेटिव Promise कॉन्टेक्स्ट प्रोपेगेशन का पालन नहीं कर सकता है। एक इवेंट एमिटर बाद के टिक पर लिसनर्स को इनवोक कर सकता है। एक वर्कर या चाइल्ड प्रोसेस का एक स्वतंत्र निष्पादन संदर्भ होता है और वह स्टोर को अंतर्निहित रूप से साझा नहीं कर सकता है। प्रत्येक बाउंड्री के लिए एक न्यूनतम परीक्षण लिखें, यह दस्तावेज़ करते हुए कि कौन से फ़ील्ड एक स्पष्ट संदेश के माध्यम से पार होते हैं और कौन से एक नए रिक्वेस्ट बाउंड्री पर फिर से बनाए जाते हैं।

6. Node 22/24 का रिग्रेशन करें और परिणाम देखें

अपग्रेड मैट्रिक्स में Node 24 के डिफ़ॉल्ट इम्प्लीमेंटेशन परिवर्तन को शामिल करें, लेकिन मूल-कारण विश्लेषण के स्थान पर केवल वर्ज़न तुलना का उपयोग न करें। स्टार्टअप फ़्लैग, डिपेंडेंसी वर्ज़न और निष्पादन मोड को ठीक करें, फिर requestId पूर्णता, बाउंड्री ब्रेक, एरर रेट, लेटेंसी और थ्रूपुट की तुलना करें। रिलीज़ के बाद कम-दर बाउंड्री डायग्नोस्टिक्स रखें; यदि ऑथराइजेशन या टेनेंट फ़ील्ड गायब हो जाते हैं, तो कैनरी को रोकें और स्थिर वर्ज़न पर वापस लौटें।

उच्च गुणवत्ता वाला नमूना उत्तर

मैं स्टोर अनुबंध को परिभाषित करूंगा और वैल्यू से खाली में पहले ट्रांजिशन को खोजने के लिए एंट्री, प्रमुख एसिंक बाउंड्री और अंतिम लॉग पर गैर-संवेदनशील स्नैपशॉट रिकॉर्ड करूंगा। नेटिव Promise चेन रिक्वेस्ट बाउंड्री पर run() का उपयोग करती हैं। यदि कोई थर्ड-पार्टी कॉलबैक या कस्टम thenable कॉन्टेक्स्ट को प्रोपेगेट करने में विफल रहता है, तो रैपर प्रति टास्क एक AsyncResource बनाता है और runInAsyncScope के साथ कॉलबैक को इनवोक करता है, फिर इसे नष्ट कर देता है। मैं ग्लोबल enterWith() के साथ समस्या को नहीं छिपाऊंगा। इवेंट एमिटर्स, टाइमआउट, एरर, वर्कर्स और Node 22/24 का अलग-अलग परीक्षण करें, requestId की पूर्णता और व्यावसायिक फ़ील्ड की तुलना करें। Node 24 का AsyncContextFrame टेस्ट मैट्रिक्स में एक वर्ज़न वेरिएबल है, एकमात्र व्याख्या नहीं।

सामान्य गलतियाँ

  • हर लॉस के लिए Node 24 को दोष देना → थर्ड-पार्टी बाउंड्री ब्रेक हो सकती है → पहले एक न्यूनतम रीप्रोडक्शन और बाउंड्री स्नैपशॉट का उपयोग करें।
  • हर जगह enterWith() को कॉल करना → इवेंट लिसनर्स एक-दूसरे को दूषित कर सकते हैं → रिक्वेस्ट-स्कॉप्ड run() को प्राथमिकता दें।
  • केवल लॉगर में requestId जोड़ना → टेनेंट या ऑडिट कॉन्टेक्स्ट अभी भी खोया हुआ है → एसिंक-रिसोर्स एसोसिएशन को सुधारें।
  • सभी रिक्वेस्ट्स के लिए एक AsyncResource का पुन: उपयोग करना → कॉन्टेक्स्ट एक दूसरे को क्रॉस-कंटैमिनेट करते हैं → प्रति टास्क एक बनाएं और नष्ट करें।
  • यह मान लेना कि वर्कर्स स्टोर को इनहेरिट करते हैं → क्रॉस-थ्रेड कॉन्टेक्स्ट अंतर्निहित नहीं है → संदेशों में आवश्यक फ़ील्ड स्पष्ट रूप से पास करें।

फॉलो-अप प्रश्न और उत्तर

आप run() और enterWith() के बीच कैसे चुनते हैं?

रिक्वेस्ट या टास्क बाउंड्री के लिए run() को प्राथमिकता दें क्योंकि इसका स्कोप कॉलबैक और उसके एसिंक काम का अनुसरण करता है। enterWith() वर्तमान सिंक्रोनस निष्पादन में बाद के इवेंट हैंडलिंग को प्रभावित करता है और इसका उपयोग केवल तभी किया जाना चाहिए जब बाउंड्री स्पष्ट हो और लिसनर संदूषण से इंकार किया गया हो।

AsyncResource की आवश्यकता कब होती है?

इसका उपयोग तब करें जब कोई कॉलबैक API, इवेंट रैपर या कस्टम thenable अपने एसिंक ऑपरेशन को Node के एसिंक-रिसोर्स ग्राफ से जोड़ने में विफल रहता है, ताकि कॉलबैक उस कॉन्टेक्स्ट में चल सके जहां टास्क बनाया गया था।

आप किसी वर्कर में requestId को कैसे सुरक्षित रखते हैं?

एक वर्कर का एक स्वतंत्र निष्पादन संदर्भ होता है, इसलिए एक संदेश में requestId या न्यूनतम टेनेंट पहचानकर्ता पास करें, वर्कर एंट्री पर एक नया AsyncLocalStorage स्टोर बनाएं, और संवेदनशील ऑब्जेक्ट भेजने से बचें।

क्या Node 24 का AsyncContextFrame हर लॉस को ठीक कर देगा?

नहीं। यह डिफ़ॉल्ट इम्प्लीमेंटेशन को बदलता है, लेकिन थर्ड-पार्टी कॉलबैक, गलत enterWith(), कस्टम thenables और क्रॉस-थ्रेड बाउंड्री के लिए अभी भी अलग परीक्षणों की आवश्यकता होती है।

आप कैसे दिखाते हैं कि सुधार ने ओवरहेड नहीं जोड़ा?

समान ट्रैफ़िक और डिपेंडेंसी वर्ज़न के तहत लेटेंसी, थ्रूपुट, सीपीयू, मेमोरी और requestId पूर्णता की तुलना करें, और एक बेंचमार्क पर निर्भर रहने के बजाय बाउंड कॉलबैक के लिए बनाए गए रिसोर्सेज की संख्या का निरीक्षण करें।

सार्वजनिक स्रोत

संबंधित प्रश्न