మీ 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ని కాల్ చేసి, దాని ఫలితాన్ని సేవ్ చేయకపోతే, ఆ టాస్క్ గార్బేజ్ కలెక్షన్కు అర్హత పొందుతుంది. ఈవెంట్ లూప్ టాస్క్లకు కేవలం వీక్ రిఫరెన్స్లను (weak references) మాత్రమే ఉంచుతుంది. ఆ టాస్క్కు వేరే ఏదీ రిఫరెన్స్ ఇవ్వకపోతే, అది ఇంకా రన్ అవుతున్నప్పుడే Python దానిని తిరిగి తీసుకోవచ్చు (reclaim).
మీ పని అకస్మాత్తుగా ఆగిపోతుంది. మీ ట్రాకర్లో ఎటువంటి ఎర్రర్స్ కనిపించవు. కోడ్ కూడా పర్ఫెక్ట్గానే ఉంటుంది. కానీ ఆ టాస్క్ మాయమైపోతుంది.
మీ stderrలో ఈ లాగ్ కనిపించవచ్చు:
Task was destroyed but it is pending!
ఈ ఎర్రర్ను కనుగొనడం కష్టం, ఎందుకంటే ఇది అసలు వైఫల్యం జరిగిన చాలా కాలం తర్వాత కనిపిస్తుంది.
దీన్ని ఎలా పరిష్కరించాలి:
రిఫరెన్స్లను ఉంచడానికి ఒక బ్యాక్గ్రౌండ్ టాస్క్ సెట్ను ఉపయోగించండి.
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) టాస్క్ను సజీవంగా ఉంచుతుంది. పని పూర్తయిన తర్వాత కాల్బ్యాక్ (callback) దానిని తొలగిస్తుంది. ఇది మెమరీ లీక్లను నివారిస్తుంది మరియు గార్బేజ్ కలెక్టర్ మీ టాస్క్లను ఆపివేయకుండా చేస్తుంది.
Python 3.11 మరియు కొత్త వెర్షన్ల కోసం, మీరు TaskGroupsని ఉపయోగించవచ్చు. TaskGroup దాని లోపల ఉన్న ప్రతి టాస్క్కు స్ట్రాంగ్ రిఫరెన్స్లను (strong references) కలిగి ఉంటుంది. అయితే, ఒక TaskGroup బ్లాక్ నుండి బయటకు వచ్చే ముందు అందులోని అన్ని టాస్క్లు పూర్తయ్యే వరకు వేచి ఉంటుందని గమనించండి. మీరు యూజర్కు స్పందించే ముందు పని తప్పనిసరిగా పూర్తి కావాలంటే దీనిని ఉపయోగించండి.
భారీ బ్యాక్గ్రౌండ్ వర్క్ కోసం, సూపర్వైజర్ ప్యాటర్న్ను (supervisor pattern) ఉపయోగించండి:
- స్టార్టప్లో ఒక లాంగ్-లివ్డ్ (long-lived) టాస్క్ను సృష్టించండి.
- దానికి పనిని పంపడానికి ఒక క్యూ (queue)ని ఉపయోగించండి.
- రిక్వెస్ట్ హ్యాండ్లర్ కేవలం క్యూలో ఐటమ్స్ను మాత్రమే జోడిస్తుంది.
ఇది మీ పని సజావుగా సాగేలా చూస్తుంది మరియు తర్వాత రిట్రైలను (retries) జోడించడానికి అనుమతిస్తుంది.
Pythonలో, మీరు మీ టాస్క్లపై నియంత్రణ కలిగి ఉండాలి. మీరు రిఫరెన్స్ను ఉంచుకోకపోతే, రన్టైమ్ వాటిని రక్షించదు.
Source: https://dev.to/r9v/your-asyncio-task-can-be-garbage-collected-mid-flight-3kg1
