आपका asyncio टास्क काम के बीच में ही रुक सकता है
कई Python सर्विसेज बैकग्राउंड वर्क चलाने के लिए इस पैटर्न का उपयोग करती हैं:
async def create_order(order):
await save(order)
asyncio.create_task(send_webhook(order))
return {"status": "created"}
इसका लक्ष्य क्लाइंट को इंतज़ार कराए बिना एक webhook भेजना है। यह डेवलपमेंट में तो काम करता है, लेकिन प्रोडक्शन में यह रैंडमली फेल हो जाता है।
समस्या garbage collection की है।
जब आप asyncio.create_task को कॉल करते हैं और परिणाम (result) को सेव नहीं करते हैं, तो वह टास्क garbage collection के लिए पात्र हो जाता है। इवेंट लूप टास्क के केवल weak references रखता है। यदि कोई और चीज़ उस टास्क की ओर इशारा नहीं करती है, तो Python उसे चलते समय ही वापस ले सकता है।
आपका काम बस रुक जाता है। आपके ट्रैकर में कोई एरर नहीं दिखता। कोड एकदम सही है। टास्क बस गायब हो जाता है।
आपको अपने stderr में यह लॉग दिख सकता है:
Task was destroyed but it is pending!
इस एरर को ढूँढना मुश्किल होता है क्योंकि यह अक्सर वास्तविक विफलता (failure) के काफी समय बाद दिखाई देता है।
इसे कैसे ठीक करें:
रेफरेंस बनाए रखने के लिए एक background task set का उपयोग करें।
background_tasks = set()
def fire_and_forget(coro):
task = asyncio.create_task(coro)
background_tasks.add(task)
task.add_done_callback(background_tasks.discard)
return task
यह set टास्क को जीवित रखता है। कॉलबैक काम खत्म होने के बाद उसे हटा देता है। यह memory leaks को रोकता है और garbage collector को आपके टास्क खत्म करने से रोकता है।
Python 3.11 और उसके बाद के वर्ज़न के लिए, आप TaskGroups का उपयोग कर सकते हैं। एक TaskGroup अपने अंदर के हर टास्क का strong reference रखता है। ध्यान दें कि एक TaskGroup ब्लॉक से बाहर निकलने से पहले सभी टास्क के खत्म होने का इंतज़ार करेगा। इसका उपयोग तब करें जब यूजर को जवाब देने से पहले काम पूरा होना ज़रूरी हो।
भारी बैकग्राउंड वर्क के लिए, supervisor pattern का उपयोग करें:
- स्टार्टअप पर एक long-lived टास्क बनाएं।
- उस तक काम भेजने के लिए एक queue का उपयोग करें।
- रिक्वेस्ट हैंडलर केवल queue में आइटम जोड़ता है।
यह सुनिश्चित करता है कि आपका काम सुरक्षित रहे और आपको बाद में retries जोड़ने की सुविधा देता है।
Python में, आपको अपने टास्क का स्वामित्व (ownership) रखना होगा। यदि आप उनका रेफरेंस नहीं रखते हैं, तो runtime उनकी रक्षा नहीं करेगा।
Source: https://dev.to/r9v/your-asyncio-task-can-be-garbage-collected-mid-flight-3kg1
