प्रॉम्प्ट और संदर्भ
एक एसिंक्रोनस रिक्वेस्ट डाउनस्ट्रीम कॉल्स के लिए बजट जानने से पहले रूटिंग मेटाडेटा को पढ़ती है। डिज़ाइन में इवेंट लूप की मोनोटोनिक क्लॉक का उपयोग करना चाहिए, बिना किसी प्रारंभिक डेडलाइन के एक टाइमआउट बनाना चाहिए, मेटाडेटा आने पर एक एब्सोल्यूट डेडलाइन को रीशेड्यूल करना चाहिए, और कॉलर कैंसलेशन को टाइमआउट से अलग रखना चाहिए।
Python में asyncio.timeout() को रीशेड्यूल करने योग्य एसिंक्रोनस कॉन्टेक्स्ट मैनेजर के रूप में और timeout_at() को इवेंट-लूप क्लॉक पर आधारित एब्सोल्यूट डेडलाइन के रूप में प्रलेखित किया गया है। कॉन्टेक्स्ट के कारण होने वाले कैंसलेशन को कॉन्टेक्स्ट के बाहर TimeoutError में बदल दिया जाता है।
इंटरव्यूअर क्या टेस्ट कर रहा है
रिलेटिव अवधि और एब्सोल्यूट डेडलाइन, बिज़नेस टाइमआउट और एक्सटर्नल कैंसलेशन के बीच स्पष्ट अंतर के साथ-साथ reschedule(), expired(), नेस्टिंग, और wait_for() के सही उपयोग की जाँच की जाती है। बेहतरीन उत्तर हर I/O में एक ही बजट को प्रोपेगेट करते हैं और संसाधनों को क्लीनअप करते हैं।
पहले पूछे जाने वाले स्पष्टीकरण प्रश्न
- बजट का स्वामित्व किस कंपोनेंट के पास है, और मेटाडेटा पढ़ने में कितना खर्च हुआ?
- क्या HTTP क्लाइंट, डेटाबेस, और कतार (queue) टाइमआउट या कैंसलेशन सिग्नल स्वीकार करते हैं?
- क्या चाइल्ड कॉल्स को एक ही एब्सोल्यूट डेडलाइन साझा करनी चाहिए या उनके पास स्वतंत्र बजट होना चाहिए?
- क्या टाइमआउट एक डिग्रेडेड रिस्पांस, पुनः प्रयास (retry), या एक एरर है?
- प्रोडक्शन में न्यूनतम Python वर्शन क्या है?
30-सेकंड उत्तर ढाँचा
"मैं loop.time() के साथ एक एब्सोल्यूट डेडलाइन की गणना करता हूँ। मैं asyncio.timeout(None) से शुरुआत करता हूँ, फिर मेटाडेटा आने के बाद cm.reschedule(deadline) को कॉल करता हूँ। कॉन्टेक्स्ट द्वारा बनाया गया कैंसलेशन बाहर निकलने पर TimeoutError बन जाता है, इसलिए मैं इसे बाहर कैच करता हूँ; क्लीनअप के बाद एक बाहरी CancelledError कैंसलेशन ही बना रहता है। प्रत्येक डाउनस्ट्रीम ऑपरेशन को शेष बजट प्राप्त होता है, और नेस्टेड कॉल्स पूरे टाइमआउट को रीसेट नहीं कर सकती हैं। मैं डायग्नोसिस के लिए cm.expired() का उपयोग करता हूँ और रेस कंडीशंस का परीक्षण करता हूँ।"
चरण-दर-चरण गहन विश्लेषण
चरण 1: मोनोटोनिक क्लॉक के साथ डेडलाइन व्यक्त करें
वॉल-क्लॉक टाइम से शेष समय की गणना न करें क्योंकि क्लॉक करेक्शन के कारण समय में जंप हो सकता है। loop.time() का उपयोग करें और प्रत्येक डाउनस्ट्रीम ऑपरेशन को एक एब्सोल्यूट डेडलाइन पास करें।
loop = asyncio.get_running_loop()
deadline = loop.time() + 2.0
async with asyncio.timeout_at(deadline):
await call_dependency(deadline)चरण 2: एक रीशेड्यूल करने योग्य कॉन्टेक्स्ट शुरू करें
जब बजट अज्ञात हो, तो asyncio.timeout(None) as cm का उपयोग करें। मेटाडेटा आने के बाद, एब्सोल्यूट डेडलाइन की गणना करें और cm.reschedule(deadline) को कॉल करें। एक ही कॉन्टेक्स्ट बनाए रखें ताकि प्रारंभिक कार्य और डाउनस्ट्रीम कॉल्स एक ही सीमा साझा करें।
चरण 3: शेष बजट को प्रोपेगेट करें
प्रत्येक क्लाइंट deadline - loop.time() की गणना करता है और गैर-सकारात्मक मान को तत्काल विफलता मानता है। समान डेडलाइन पास करने से रूटिंग, डेटाबेस और HTTP लेयर्स को अलग-अलग पूरा रिलेटिव टाइमआउट देने से रोका जा सकता है।
चरण 4: कैंसलेशन-से-टाइमआउट रूपांतरण को समझें
कॉन्टेक्स्ट आंतरिक रूप से वर्तमान Task को कैंसिल करता है और बाहर निकलते समय उस कैंसलेशन को TimeoutError में बदल देता है। नतीजतन, TimeoutError को async with के बाहर कैच किया जाना चाहिए, न कि उसके अंदर।
try:
async with asyncio.timeout(1.0):
await slow_call()
except TimeoutError:
return degraded_result()चरण 5: बाहरी कैंसलेशन को सुरक्षित रखें
यदि कोई क्लाइंट डिस्कनेक्ट हो जाता है या कोई पैरेंट रिक्वेस्ट को कैंसिल कर देता है, तो CancelledError बिज़नेस टाइमआउट नहीं है। कनेक्शन, लॉक और अस्थायी फ़ाइलों को रिलीज़ करें, फिर डिग्रेडेड सफलता लौटाने के बजाय इसे पुनः रेज़ (re-raise) करें।
चरण 6: timeout, timeoutat, और waitfor की तुलना करें
timeout(delay) एक रिलेटिव डिले का उपयोग करता है और इसे रीशेड्यूल किया जा सकता है; timeout_at(when) एक एब्सोल्यूट मोनोटोनिक डेडलाइन लेता है और प्रोपेगेशन के लिए आदर्श है। wait_for(aw, timeout) एक awaitable को लक्षित करता है, टाइमआउट होने पर उसे कैंसिल करता है, और कैंसलेशन समाप्त होने की प्रतीक्षा कर सकता है। कई wait_for कॉल्स को कंपोज़ करने से रिक्वेस्ट बजट से अधिक खर्च हो सकता है।
चरण 7: नेस्टिंग और एक्सपायरी को संभालें
टाइमआउट कॉन्टेक्स्ट नेस्ट हो सकते हैं; एक आंतरिक डेडलाइन बाहरी डेडलाइन से अधिक नहीं होनी चाहिए। बाहर निकलने के बाद, cm.expired() बताता है कि क्या कॉन्टेक्स्ट वास्तव में अपनी डेडलाइन तक पहुँच गया था। सामान्य अपवाद, बाहरी कैंसलेशन और सफल परिणाम अलग-अलग पहचाने जाने योग्य रहने चाहिए।
चरण 8: रेस कंडीशंस और क्लीनअप का परीक्षण करें
विलंबित मेटाडेटा, पहले से समाप्त डेडलाइन, पहले आंतरिक टाइमआउट, एक साथ बाहरी कैंसलेशन और टाइमआउट, कैंसलेशन को अनदेखा करने वाला डाउनस्ट्रीम ऑपरेशन, reschedule(None), बार-बार रीशेड्यूलिंग, और क्लीनअप विफलता का परीक्षण करें। संवेदनशील पेलोड के बिना डेडलाइन, शेष बजट, कैंसलेशन का कारण, और डाउनस्ट्रीम अवधि रिकॉर्ड करें।
मॉडल उच्च-गुणवत्ता उत्तर
"मैं loop.time() के साथ एक एब्सोल्यूट डेडलाइन बनाता हूँ और इसे डाउनस्ट्रीम में पास करता हूँ। जब बजट अज्ञात होता है, तो मैं timeout(None) में प्रवेश करता हूँ और मेटाडेटा आने के बाद रीशेड्यूल करता हूँ। कॉन्टेक्स्ट अपने आंतरिक कैंसलेशन को ब्लॉक के बाहर TimeoutError में बदल देता है; बाहरी CancelledError प्रोपेगेट होता रहता है। नेस्टेड कॉल्स सबसे प्रारंभिक डेडलाइन का उपयोग करती हैं, और क्लाइंट्स तथा डेटाबेस शेष बजट प्राप्त करते हैं। रेस टेस्ट यह सत्यापित करते हैं कि कोई Task या संसाधन लीक न हो।"
सामान्य गलतियाँ
- डेडलाइन के लिए
time.time()का उपयोग करना → क्लॉक करेक्शन बजट को बदल देते हैं →loop.time()का उपयोग करें। - कॉन्टेक्स्ट के अंदर TimeoutError को कैच करना → ट्रांसफॉर्म किया गया अपवाद वहां मौजूद नहीं होता है → इसे बाहर कैच करें।
- बाहरी कैंसलेशन को टाइमआउट मानना → क्लाइंट कैंसलेशन एक नकली डिग्रेडेड परिणाम बन जाता है → अपवाद प्रकारों को अलग रखें।
- प्रत्येक लेयर पर पूरा टाइमआउट देना → कुल लेटेंसी अनुबंध सीमा से अधिक हो जाती है → एक एब्सोल्यूट डेडलाइन प्रोपेगेट करें।
- wait_for कैंसलेशन प्रतीक्षा को अनदेखा करना → वास्तविक अवधि संख्यात्मक टाइमआउट से अधिक हो जाती है → कैंसलेशन कन्वर्जेंस को मापें।
- केवल सफलता और एक टाइमआउट का परीक्षण करना → रेस पाथ्स संसाधनों को लीक कर देते हैं → रीशेड्यूल, समवर्ती कैंसलेशन, और क्लीनअप विफलताओं का परीक्षण करें।
फॉलो-अप प्रश्न और ठोस प्रतिक्रियाएं
फॉलो-अप 1: प्रति-लेयर टाइमआउट की तुलना में timeout_at अधिक स्थिर क्यों है?
प्रत्येक लेयर समान एब्सोल्यूट क्षण को लक्षित करती है, इसलिए रिलेटिव-टाइम राउंडिंग और बार-बार मिलने वाले बजट रिक्वेस्ट अनुबंध से अधिक जमा नहीं हो सकते।
फॉलो-अप 2: क्या होगा यदि reschedule को बीती हुई डेडलाइन प्राप्त हो?
कॉन्टेक्स्ट अगले इवेंट-लूप अवसर पर समाप्त हो जाता है। रीशेड्यूल करने से पहले शेष बजट की जांच करें और इसके पहले ही समाप्त हो जाने पर नॉन-कैंसिलेबल I/O शुरू करने से बचें।
फॉलो-अप 3: डेटाबेस डेडलाइन का पालन कैसे करता है?
शेष सेकंड को स्टेटमेंट टाइमआउट या कैंसलेशन API में पास करें। केवल Python Task को कैंसिल करने से सर्वर-साइड क्वेरी अनिवार्य रूप से बंद नहीं होती है।
फॉलो-अप 4: जब कैंसलेशन और टाइमआउट में रेस हो तो आप क्या रिकॉर्ड करते हैं?
पैरेंट कैंसलेशन कारण और कैंसलेशन संख्या बनाए रखें, और बाहरी कैंसलेशन को प्रोपेगेट करें। प्रत्येक निकास को बिज़नेस टाइमआउट के रूप में लेबल करने के बजाय expired() को एक डायग्नोस्टिक फ़ील्ड के रूप में स्टोर करें।
फॉलो-अप 5: आप wait_for को कब चुनेंगे?
सरल रिलेटिव सीमा वाले किसी एक awaitable के लिए इसका उपयोग करें। जब कॉल्स एक डायनेमिक डेडलाइन या पूरे रिक्वेस्ट के बजट को साझा करती हैं, तो timeout या timeout_at को प्राथमिकता दें।