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

कोडिंग इंटरव्यू: Python asyncio.TaskGroup के साथ समवर्ती विफलताओं (Concurrent Failures) को कैसे प्रबंधित करें?

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

प्रश्न

यूज़र, ऑर्डर और अनुशंसा (recommendation) डेटा को समवर्ती रूप से लोड करने के लिए Python asyncio.TaskGroup का उपयोग करें। जब कोई एक टास्क विफल हो जाए तो सहोदर (sibling) टास्क को रद्द करें, निदान योग्य त्रुटियों को बनाए रखें, और कॉलर कैंसलेशन या टाइमआउट के बाद संसाधनों को साफ़ करें। asyncio.gather से अंतर स्पष्ट करें और बताएं कि कौन सा कार्य रिक्वेस्ट स्कोप में नहीं रहना चाहिए।

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

एक async API समवर्ती रूप से यूज़र, ऑर्डर और अनुशंसा डेटा लोड करता है। आवश्यक टास्क में विफलता होने पर सहोदर (sibling) टास्क रद्द होने चाहिए, जबकि क्लाइंट डिस्कनेक्ट या कुल टाइमआउट होने पर कोई ऑर्फन कोरूटीन नहीं बचना चाहिए। asyncio.TaskGroup के साथ कार्यान्वयन को डिज़ाइन करें और अपवाद एकत्रीकरण (exception aggregation), कैंसलेशन और संसाधन सफ़ाई (resource cleanup) की व्याख्या करें।

Python का दस्तावेज़ीकरण TaskGroup को स्ट्रक्चर्ड समवर्तीता (structured concurrency) के रूप में वर्णित करता है: स्कोप से बाहर निकलने से पहले टास्क समाप्त हो जाते हैं; गैर-कैंसलेशन अपवाद शेष टास्क को रद्द कर देता है और ExceptionGroup के रूप में उठाया जाता है। इंटरव्यू मात्र ढेर सारे create_task() कॉल्स के बजाय जीवनचक्र (lifecycle) के तर्क का परीक्षण करता है।

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

पैरेंट-चाइल्ड टास्क ट्री, सिबलिंग कैंसलेशन, CancelledError, ExceptionGroup और async with जॉइनिंग की जांच की जाती है। उम्मीदवार को डेटाबेस, HTTP और फ़ाइलों में कैंसलेशन पास करना चाहिए, और उस कार्य को ड्यूरेबल कतार (durable queue) में स्थानांतरित करना चाहिए जिसे रिस्पॉन्स के बाद भी बने रहना है।

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

  • क्या तीनों रीड्स अनिवार्य हैं, या अनुशंसाएं डिग्रेड हो सकती हैं?
  • कौन से टास्क कनेक्शन, कर्सर या अस्थायी फ़ाइलें खोलते हैं?
  • कुल टाइमआउट कौन बनाता है, और कॉलर कैंसलेशन कैसे आता है?
  • किन त्रुटियों को वर्गीकरण, पुनः प्रयास (retry), या यूज़र-सुरक्षित प्रतिक्रिया की आवश्यकता है?
  • HTTP रिस्पॉन्स के बाद कौन से जॉब्स जारी रहने चाहिए?

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

async with TaskGroup() के अंदर तीन टास्क बनाएं और स्कोप के समाप्त होने की प्रतीक्षा करें। एक गैर-कैंसलेशन अपवाद सिबलिंग्स को रद्द कर देता है और एक एक्सेप्शन ग्रुप उठाता है; except* ज्ञात त्रुटियों को वर्गीकृत करता है। प्रत्येक टास्क finally में संसाधनों को बंद करता है। समूह को एक asyncio.timeout() या कॉलर कैंसलेशन में लपेटें। जो कार्य रिक्वेस्ट के बाद भी बने रहने चाहिए, वे ड्यूरेबल कतार में जाते हैं।”

चरण-दर-चरण समाधान

चरण 1: टास्क ट्री और परिणाम अनुबंध (contract) को परिभाषित करें

प्रत्येक परिणाम और उसकी अनिवार्यता घोषित करें। यूज़र और ऑर्डर आवश्यक हो सकते हैं जबकि अनुशंसाएं वैकल्पिक हो सकती हैं; यह विकल्प तय करता है कि क्या एक अपवाद समूह को रद्द कर देगा। प्रत्येक टास्क रिक्वेस्ट स्कोप से संबंधित है।

python
async with asyncio.TaskGroup() as group:
    user_task = group.create_task(load_user(user_id))
    order_task = group.create_task(load_orders(user_id))
    rec_task = group.create_task(load_recommendations(user_id))

स्कोप से बाहर निकलना टास्क को जॉइन करता है। ब्लॉक के बाहर किसी अधूरे टास्क को पढ़ना उसे जॉइन करने का विकल्प नहीं है।

चरण 2: ExceptionGroup के साथ विफलता को संभालें

एक गैर-CancelledError अपवाद सिबलिंग्स को रद्द कर देता है, और सभी टास्क समाप्त होने के बाद TaskGroup एक ExceptionGroup उठाता है। अपेक्षित डिपेंडेंसी त्रुटियों के लिए except* का उपयोग करें और अज्ञात त्रुटियों को बाहरी हैंडलर के लिए सुरक्षित रखें।

python
try:
    async with asyncio.TaskGroup() as group:
        user = group.create_task(load_user(user_id))
        orders = group.create_task(load_orders(user_id))
except* RetryableDependencyError as errors:
    record_dependency_failures(errors.exceptions)
    raise ServiceUnavailable from errors

BaseException को कैच करके चुपचाप कैंसलेशन को न दबाएं।

चरण 3: वास्तविक I/O में कैंसलेशन लागू करें

TaskGroup केवल Python टास्क को रद्द करता है; HTTP, डेटाबेस और फ़ाइल ड्राइवरों को कैंसलेशन या टाइमआउट समर्थन की आवश्यकता होती है। प्रत्येक टास्क finally में कनेक्शन बंद करता है, सेमाफोर जारी करता है, अस्थायी फ़ाइलों को हटाता है और उपभोग रोकता है।

python
async def load_orders(user_id: str):
    conn = await pool.acquire()
    try:
        return await conn.fetch("SELECT ...", user_id, timeout=1.5)
    finally:
        await pool.release(conn)

यदि कोई ड्राइवर किसी क्वेरी को बाधित नहीं कर सकता है, तो केवल टास्क कैंसलेशन पर निर्भर रहने के बजाय स्टेटमेंट टाइमआउट, अलग कनेक्शन, या सीमित बैकग्राउंड जॉब का उपयोग करें।

चरण 4: एक कुल टाइमआउट सेट करें और बाहरी कैंसलेशन में अंतर करें

पूरे समूह को asyncio.timeout() में लपेटें ताकि सभी कार्य एक ही बजट साझा करें।

python
try:
    async with asyncio.timeout(2.0):
        result = await aggregate(user_id)
except TimeoutError:
    return degraded_response("deadline")
except asyncio.CancelledError:
    raise

बाहरी कैंसलेशन को ऊपर की ओर बढ़ना चाहिए, न कि एक सफल प्रतिक्रिया बनना चाहिए। टाइमआउट और यूज़र कैंसलेशन को अलग-अलग कारणों के रूप में रिकॉर्ड करें।

चरण 5: TaskGroup बनाम gather को समझें

asyncio.gather() सामान्य रूप से अपने वेटर को पहला अपवाद प्रसारित करता है, लेकिन सिबलिंग टास्क अनिवार्य रूप से रद्द नहीं होते हैं; तुरंत लौटने से वे ऑर्फन हो सकते हैं। TaskGroup टास्क के जीवनकाल को एक स्कोप से बांधता है और विफलता पर सिबलिंग्स को रद्द करता है।

gather(return_exceptions=True) स्पष्ट आंशिक-विफलता अनुबंध के लिए उपयोगी है, लेकिन प्रत्येक परिणाम का निरीक्षण किया जाना चाहिए। यह स्ट्रक्चर्ड सफ़ाई का विकल्प नहीं है, और एक समूह को असीमित बैकग्राउंड टास्क नहीं बनाने चाहिए।

चरण 6: विफलता क्रम और सफ़ाई का परीक्षण करें

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

निजी पेलोड के बिना टास्क का नाम, रिक्वेस्ट आईडी, अवधि, कैंसलेशन का कारण, अपवाद प्रकार, और डाउनस्ट्रीम कॉल लॉग करें। रेस कंडीशन को उजागर करने के लिए धीमे-I/O परीक्षणों को दोहराएं; सभी-सफल परीक्षण अपर्याप्त हैं।

आदर्श उत्तर

“मैं TaskGroup के अंदर तीन टास्क बनाता हूं, यूज़र और ऑर्डर को अनिवार्य बनाता हूं, और अनुशंसाओं को डिग्रेड होने की अनुमति देता हूं। स्कोप टास्क को जॉइन करता है; गैर-कैंसलेशन विफलता सिबलिंग्स को रद्द करती है और एक ExceptionGroup उठाती है, जिसे except* वर्गीकृत करता है। प्रत्येक I/O टास्क एक टाइमआउट पास करता है और finally में कनेक्शन जारी करता है।”

“एक बाहरी टाइमआउट एक साझा बजट की आपूर्ति करता है और CancelledError प्रसारित होता है। पोस्ट-रिस्पॉन्स कार्य ड्यूरेबल कतार में जाता है। मैं एक साथ विफलता, कैंसलेशन, टाइमआउट, और धीमे I/O को इंजेक्ट करता हूं और सत्यापित करता हूं कि कोई ऑर्फन टास्क या संसाधन लीक न हो।”

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

  • सादे create_task के बाद रिटर्न करना → ऑर्फन टास्क → टास्क को TaskGroup स्कोप में रखें।
  • CancelledError को दबाना → पैरेंट रुक नहीं सकता → सफ़ाई करें और पुनः उठाएं।
  • प्रत्येक टास्क को पूरा टाइमआउट देना → कुल विलंबता (latency) नियंत्रण से बाहर हो जाती है → एक ही बजट साझा करें।
  • ExceptionGroup को एकल त्रुटि मानना → समानांतर साक्ष्य खो जाते हैं → except* के साथ वर्गीकृत करें।
  • केवल Python टास्क रद्द करना → डेटाबेस या HTTP कार्य जारी रहता है → ड्राइवर कैंसलेशन या टाइमआउट का उपयोग करें।
  • हर मामले के लिए gather का उपयोग करना → आंशिक विफलता और सफ़ाई अस्पष्ट हो जाती है → डिग्रेडेशन अनुबंध को परिभाषित करें।

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

फ़ॉलो-अप 1: क्या TaskGroup तुरंत अंतर्निहित I/O को रोक देता है?

नहीं। ड्राइवर को कैंसलेशन या स्टेटमेंट टाइमआउट का समर्थन करना चाहिए; अन्यथा कनेक्शन को अलग करें या कार्य को एक सीमित बैकग्राउंड जॉब में स्थानांतरित करें।

फ़ॉलो-अप 2: अनुशंसाएं विफल होने पर समूह को रद्द क्यों नहीं करते?

व्यावसायिक अनुबंध का उपयोग करें। एक वैकल्पिक अनुशंसा को कैच किया जा सकता है और एक खाली परिणाम से बदला जा सकता है; सुरक्षा- या बिलिंग-महत्वपूर्ण कार्य को अपवाद को बाहर निकलने देना चाहिए और सिबलिंग्स को रद्द करना चाहिए।

फ़ॉलो-अप 3: आप एक ExceptionGroup को API रिस्पॉन्स में कैसे मैप करते हैं?

ज्ञात डिपेंडेंसी त्रुटियों को एक सुरक्षित 503 या डिग्रेडेड परिणाम पर मैप करें और चाइल्ड त्रुटियों को रिकॉर्ड करें। अज्ञात त्रुटियां वैश्विक हैंडलर के पास जाती हैं; क्लाइंट को कभी भी आंतरिक स्टैक न लौटाएं।

फ़ॉलो-अप 4: कौन सा कार्य TaskGroup के बाहर होना चाहिए?

ईमेल, इंडेक्सिंग और बैच राइट्स जिन्हें रिस्पॉन्स के बाद भी जारी रहना चाहिए, उन्हें कतार में सहेजा जाना चाहिए और एक वर्कर द्वारा पुनः प्रयास किया जाना चाहिए। रिक्वेस्ट स्कोप में केवल रिक्वेस्ट-जीवनकाल का कार्य होना चाहिए।

फ़ॉलो-अप 5: क्या होगा यदि finally कैंसलेशन के दौरान अपवाद उठाता है?

सफ़ाई को छोटा और अवलोकनीय रखें, सफ़ाई की विफलताओं को संदर्भ के रूप में संलग्न करें, और मूल कैंसलेशन या व्यावसायिक अपवाद को सुरक्षित रखें ताकि मूल कारण प्रतिस्थापित न हो।

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

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

संबंधित इंटरव्यू टूल

कोडिंग प्रॉम्प्ट के लिए स्क्रीनशॉट का उपयोग करें

समस्या को कैप्चर करें, फिर क्रम से प्रतिबंधों (constraints), समाधान, कोड, एज केस और जटिलता पर काम करें।

टूल देखें