আপনার asyncio টাস্ক কাজ চলাকালীন মাঝপথে বন্ধ হয়ে যেতে পারে

অনেক Python সার্ভিস ব্যাকগ্রাউন্ডে কাজ চালানোর জন্য এই প্যাটার্নটি ব্যবহার করে:

async def create_order(order):
    await save(order)
    asyncio.create_task(send_webhook(order))
    return {"status": "created"}

লক্ষ্য হলো ক্লায়েন্টকে অপেক্ষা না করিয়ে একটি webhook পাঠানো। এটি ডেভেলপমেন্টে কাজ করে। কিন্তু প্রোডাকশনে এটি এলোমেলোভাবে (randomly) ব্যর্থ হয়।

সমস্যাটি হলো garbage collection।

যখন আপনি asyncio.create_task কল করেন এবং ফলাফলটি সেভ করে না রাখেন, তখন টাস্কটি garbage collection-এর জন্য যোগ্য হয়ে পড়ে। event loop টাস্কগুলোর শুধুমাত্র weak references রাখে। যদি অন্য কিছু সেই টাস্কটিকে নির্দেশ না করে, তবে Python সেটি চলাকালীন সময়েই রিক্লেম (reclaim) করে নিতে পারে।

আপনার কাজ হঠাৎ থেমে যায়। আপনার ট্র্যাকার বা লগ-এ কোনো এরর দেখায় না। কোড একদম নিখুঁত থাকে। টাস্কটি স্রেফ অদৃশ্য হয়ে যায়।

আপনি আপনার stderr-এ এই লগটি দেখতে পারেন: Task was destroyed but it is pending!

এই এররটি খুঁজে বের করা কঠিন কারণ এটি প্রায়শই আসল ব্যর্থতার অনেক পরে দেখা দেয়।

এটি কীভাবে সমাধান করবেন:

রেফারেন্স ধরে রাখার জন্য একটি 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 টাস্কটিকে সচল রাখে। কাজ শেষ হয়ে গেলে callback সেটি রিমুভ করে দেয়। এটি memory leaks প্রতিরোধ করে এবং garbage collector-কে আপনার টাস্কগুলো বন্ধ করা থেকে বিরত রাখে।

Python 3.11 এবং তার পরবর্তী ভার্সনগুলোর জন্য, আপনি TaskGroups ব্যবহার করতে পারেন। একটি TaskGroup এর ভেতরে থাকা প্রতিটি টাস্কের strong references ধরে রাখে। মনে রাখবেন, একটি TaskGroup ব্লক থেকে বের হওয়ার আগে সমস্ত টাস্ক শেষ হওয়া পর্যন্ত অপেক্ষা করবে। যদি ব্যবহারকারীকে রেসপন্স করার আগে কাজটি সম্পন্ন হওয়া জরুরি হয়, তবে এটি ব্যবহার করুন।

ভারী ব্যাকগ্রাউন্ড কাজের জন্য, একটি supervisor pattern ব্যবহার করুন:

  • স্টার্টআপের সময় একটি long-lived টাস্ক তৈরি করুন।
  • এতে কাজ পাঠানোর জন্য একটি queue ব্যবহার করুন।
  • রিকোয়েস্ট হ্যান্ডলার শুধুমাত্র কিউতে (queue) আইটেম যোগ করবে।

এটি নিশ্চিত করে যে আপনার কাজ টিকে থাকবে এবং পরবর্তীতে retries যোগ করার সুযোগ দেয়।

Python-এ, আপনাকে অবশ্যই আপনার টাস্কগুলোর মালিকানা নিতে হবে। আপনি যদি কোনো রেফারেন্স ধরে না রাখেন, তবে runtime সেগুলোকে রক্ষা করবে না।

Source: https://dev.to/r9v/your-asyncio-task-can-be-garbage-collected-mid-flight-3kg1