प्रॉम्प्ट और संदर्भ
एक Node.js सर्विस बड़े अपलोड प्राप्त करती है और उसे क्रमबद्ध तरीके से डिकंप्रेशन, वायरस स्कैनिंग, फॉर्मेट वैलिडेशन और ऑब्जेक्ट-स्टोरेज राइट्स निष्पादित करने होते हैं। पुराना कार्यान्वयन data इवेंट्स को मैन्युअल रूप से सुनता है, कभी-कभी मेमोरी बढ़ाता है, क्लाइंट डिस्कनेक्ट होने के बाद भी चलता रहता है, और इंटरमीडिएट चरण विफल होने पर टेम्पररी फाइल्स छोड़ देता है।
stream.compose() या इसके समकक्ष पाइपलाइन कंपोजिशन दृष्टिकोण का उपयोग करें। समझाएं कि readable, writable और Transform चरण कैसे जुड़ते हैं, और बैकप्रेशर, AbortSignal, एरर प्रोपेगेशन और अंतिम क्लीनअप एक कार्य को कैसे नियंत्रणीय बनाए रखते हैं।
इंटरव्यूअर क्या टेस्ट करता है
- क्या आप
pipe,pipelineऔरcomposeकी सीमाओं और लाइफसाइकिल में अंतर पहचानते हैं। - क्या आप समझा सकते हैं कि बैकप्रेशर केवल कतार सीमा (queue limits) बढ़ाने के बजाय उत्पादन को कैसे सीमित करता है।
- क्या कैंसलेशन, एक्सेप्शन्स और क्लाइंट डिस्कनेक्ट्स पूरी पाइपलाइन में प्रोपेगेट होते हैं।
- क्या आप async जनरेटर, संसाधन रिलीज, इडेम्पोटेंट राइट्स और ऑब्ज़र्वेबिलिटी को संभालते हैं।
स्पष्ट करने के लिए प्रश्न
- क्या इनपुट HTTP रिक्वेस्ट, फाइल या ऑब्जेक्ट-स्टोरेज SDK से आता है? क्या मल्टीपार्ट आउटपुट और पुनः प्रयास (retries) समर्थित हैं?
- क्या प्रत्येक चरण एक Node स्ट्रीम, Web Stream, AsyncIterable, या सामान्य फ़ंक्शन है?
- क्या स्कैनिंग और वैलिडेशन चाइल्ड प्रोसेस, टेम्पररी फाइल या डेटाबेस स्टेट बनाते हैं?
- क्या ऑब्जेक्ट स्टोरेज मल्टीपार्ट अपलोड को निरस्त (abort) कर सकता है, और क्या कैंसल किए गए जॉब को फिर से शुरू किया जाना चाहिए?
30-सेकंड का उत्तर
मैं प्रत्येक चरण को readable, writable, Transform, या AsyncIterable के रूप में परिभाषित करूँगा और उन्हें stream.compose() के साथ एक Duplex में कंपोज़ करूँगा, फिर अंतिम गंतव्य को चलाने के लिए pipeline का उपयोग करूँगा। प्रोड्यूसर्स केवल तभी जारी रहते हैं जब डाउनस्ट्रीम डेटा स्वीकार कर सकता है, इसलिए बैकप्रेशर मेमोरी को सीमित रखता है। रिक्वेस्ट डिस्कनेक्ट्स, डेडलाइन और बिज़नेस कैंसलेशन एक ही AbortSignal साझा करते हैं जिसे समर्थित चरणों में पास किया जाता है; कोई भी एरर पूरी चेन को विफल कर देती है। टेम्पररी फाइल्स, चाइल्ड प्रोसेस और मल्टीपार्ट अपलोड्स को finally या abort हैंडलर्स में साफ किया जाता है, और राइट्स इडेम्पोटेंसी कीज़ (idempotency keys) का उपयोग करते हैं। मेट्रिक्स थ्रूपुट, क्यू डेप्थ, पीक RSS, कैंसलेशन का कारण और क्लीनअप के परिणामों को कवर करते हैं।
स्टेप-बाय-स्टेप डीप डाइव
प्रत्येक चरण का अनुबंध (contract) परिभाषित करें
इनपुट और आउटपुट प्रकार, चंक साइज, क्या null की अनुमति है, ब्लॉकिंग व्यवहार और पूरा होने पर संसाधन स्वामित्व निर्दिष्ट करें। एक async जनरेटर को अपने स्रोत का उपभोग करना चाहिए और मांग पर यील्ड (yield) करना चाहिए; एक सामान्य फ़ंक्शन को चुपचाप पूरी फ़ाइल को मेमोरी में नहीं पढ़ना चाहिए।
पुन: प्रयोज्य चरणों को कंपोज़ करें
compose स्ट्रीम्स, AsyncIterables, या फ़ंक्शंस को एक नए Duplex में जोड़ता है और पाइपलाइन सेमेंटिक्स के साथ आसन्न चरणों को संभालता है। उदाहरण:
import { compose } from 'node:stream';
async function* validate(source) {
for await (const chunk of source) {
checkChunk(chunk);
yield chunk;
}
}
const processing = compose(decompress(), validate, scan());वास्तविक कार्यान्वयन को प्रोसेसिंग को डेस्टिनेशन writable से जोड़ना चाहिए और प्रत्येक चरण में एरर को दबाने के बजाय पूर्णता और एरर्स को केंद्रीकृत करना चाहिए।
बैकप्रेशर को डिफ़ॉल्ट कंट्रोल प्लेन बनाएं
जब डाउनस्ट्रीम तैयार न हो, तो Readable और Transform चरणों को उत्पादन बंद कर देना चाहिए। data कॉलबैक में असीमित पुश न करें या हाई-वॉटर मार्क को अंतहीन रूप से बढ़ाकर धीमे कंज्यूमर को न छिपाएं। लोड टेस्ट्स को प्रत्येक कतार, थ्रूपुट और RSS को रिकॉर्ड करना चाहिए ताकि सबसे धीमा चरण कुल गति निर्धारित करे।
कैंसलेशन और डिस्कनेक्ट्स को प्रोपेगेट करें
रिक्वेस्ट डिस्कनेक्ट, डेडलाइन और मैन्युअल कैंसलेशन को एक AbortController में संयोजित करें। इसके सिग्नल को समर्थित कंपोज़ चरणों और बाहरी SDKs में पास करें; अबॉर्ट के बाद, पढ़ना बंद करें, डाउनस्ट्रीम को नष्ट (destroy) करें, और बंद होने की प्रतीक्षा करें। लॉग्स को सामान्य अबॉर्ट, बिज़नेस विफलता और नेटवर्क एरर के बीच स्पष्ट अंतर करना चाहिए।
एरर्स और क्लीनअप को केंद्रीकृत करें
एक ओनर को pipeline/compose को इनवोक करना चाहिए, पहला एरर प्राप्त करना चाहिए और चेन को नष्ट करना चाहिए। सफलता, विफलता और कैंसलेशन पर टेम्पररी फाइल्स, स्कैनर्स, सॉकेट्स और मल्टीपार्ट अपलोड्स को रिलीज़ किया जाना चाहिए; क्लीनअप विफल होने पर मूल एरर को बदले बिना जॉब आईडी के साथ अलर्ट भेजा जाता है।
इडेम्पोटेंसी और मेट्रिक्स डिज़ाइन करें
अपलोड आईडी और स्टेज वर्शन से इडेम्पोटेंसी कीज़ जनरेट करें, फिर ऑब्जेक्ट राइट पूरा होने के बाद ही बिज़नेस स्टेट कमिट करें। इनपुट और आउटपुट बाइट्स, अवधि, पीक RSS, कैंसलेशन का कारण, विफल चरण और क्लीनअप समय रिकॉर्ड करें। डुप्लिकेट राइट्स से बचने के लिए केवल एक रिकवरेबल सीमा से पुनः प्रयास करें।
मॉडल उत्तर
मैं अपलोड, डिकंप्रेशन, स्कैनिंग, वैलिडेशन और स्टोरेज को स्पष्ट इनपुट, आउटपुट और स्वामित्व वाले चरणों के रूप में परिभाषित करूँगा, उन्हें readable, writable, या async-iterable पाइपलाइन में कंपोज़ करूँगा, और एक पाइपलाइन ओनर को इसे चलाने दूँगा। डाउनस्ट्रीम खपत बैकप्रेशर को नियंत्रित करती है; data हैंडलर्स में असीमित बफरिंग प्रतिबंधित है। डिस्कनेक्ट, टाइमआउट और मैन्युअल कैंसलेशन एक AbortSignal साझा करते हैं जिसे समर्थित चरणों और स्टोरेज SDK को पास किया जाता है। ओनर पहली एरर को संभालता है और चेन को नष्ट करता है; टेम्पररी फाइल्स, स्कैनर्स और मल्टीपार्ट अपलोड्स को सफलता, विफलता और कैंसलेशन पर साफ किया जाता है। इडेम्पोटेंसी कीज़ राइट्स की सुरक्षा करती हैं, मेट्रिक्स थ्रूपुट, कतारों, RSS, कैंसलेशन और क्लीनअप को कवर करते हैं, और पुनः प्रयास केवल सुरक्षित सीमाओं से शुरू होते हैं।
सामान्य गलतियाँ
- पूरी स्ट्रीम को एक Buffer में एकत्र करना और फिर भी compose का उपयोग करने का दावा करना।
- केवल अंतिम writable के
errorको सुनना, जिससे async-जनरेटर या चाइल्ड-प्रोसेस की विफलताएं छूट जाती हैं। - क्लाइंट के डिस्कनेक्ट होने के बाद भी पढ़ना और लिखना जारी रखना।
- हाई-वॉटर मार्क को अंतहीन रूप से बढ़ाकर बैकप्रेशर की समस्या को हल करने का प्रयास करना।
- केवल सफलता पर टेम्पररी फाइल्स को हटाना, अबॉर्ट और एक्सेप्शन्स को अनदेखा करना।
- इडेम्पोटेंसी कीज़ के बिना पुनः प्रयास करना और ऑब्जेक्ट्स या बिज़नेस स्टेट को डुप्लिकेट करना।
फॉलो-अप प्रश्न
compose और pipeline में क्या अंतर है?
Compose चरणों से एक पुन: प्रयोज्य Duplex बनाता है; pipeline एंड-टू-एंड कनेक्शन चलाता है, एरर्स को प्रोपेगेट करता है, और क्लोज़र की प्रतीक्षा करता है। उन्हें संयोजित किया जा सकता है, लेकिन स्वामित्व और एरर हैंडलिंग केंद्रीकृत होनी चाहिए।
जब कोई async जनरेटर थ्रो करता है तो क्या होता है?
कंपोज़ की गई स्ट्रीम विफल हो जानी चाहिए और आसन्न चरणों को नष्ट कर देना चाहिए। कॉलर अभी भी केवल प्रोसेस एग्जिट पर निर्भर रहने के बजाय पाइपलाइन क्लीनअप की प्रतीक्षा करता है और विफल चरण को रिकॉर्ड करता है।
आप कैसे सत्यापित करते हैं कि बैकप्रेशर काम कर रहा है?
एक नियंत्रित धीमे सिंक की तुलना में तेज़ प्रोड्यूसर के साथ लोड-टेस्ट करें, सीमित कतार गहराई और RSS का निरीक्षण करें, और पुष्टि करें कि थ्रूपुट असीमित बफरिंग के बजाय सबसे धीमे चरण का अनुसरण करता है।
क्या आप कैंसलेशन के तुरंत बाद पुनः प्रयास कर सकते हैं?
पहले पुष्टि करें कि सभी संसाधन बंद हैं और टेम्पररी स्टेट पहचान योग्य है। एक इडेम्पोटेंट रिकवरेबल सीमा से पुनः प्रयास करें; उन बाहरी राइट्स के लिए कॉम्पेंसेट करें या अज्ञात स्थिति को चिह्नित करें जिन्हें बाधित नहीं किया जा सकता है।