Tugasan asyncio anda boleh terhenti semasa sedang bekerja

Banyak perkhidmatan Python menggunakan corak ini untuk menjalankan kerja latar belakang:

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

Matlamatnya adalah untuk menghantar webhook tanpa membuatkan klien menunggu. Ini berfungsi semasa pembangunan. Dalam produksi, ia gagal secara rawak.

Masalahnya ialah garbage collection.

Apabila anda memanggil asyncio.create_task dan tidak menyimpan hasilnya, tugasan tersebut menjadi layak untuk garbage collection. Event loop hanya menyimpan rujukan lemah (weak references) kepada tugasan. Jika tiada perkara lain yang merujuk kepada tugasan tersebut, Python mungkin akan mengambilnya semula semasa ia masih berjalan.

Kerja anda terhenti begitu sahaja. Tiada ralat dalam penjejak (tracker) anda. Kod anda sempurna. Tugasan tersebut hilang begitu sahaja.

Anda mungkin melihat log ini dalam stderr anda: Task was destroyed but it is pending!

Ralat ini sukar untuk dikesan kerana ia sering muncul jauh lebih lewat daripada kegagalan sebenar.

Cara untuk menyelesaikannya:

Gunakan set tugasan latar belakang untuk memegang rujukan.

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 tersebut memastikan tugasan kekal aktif. Callback akan membuangnya sebaik sahaja kerja selesai. Ini menghalang kebocoran memori (memory leaks) dan menghalang garbage collector daripada menghentikan tugasan anda.

Untuk Python 3.11 dan yang lebih baharu, anda boleh menggunakan TaskGroups. TaskGroup memegang rujukan kuat (strong references) kepada setiap tugasan di dalamnya. Perlu diingat bahawa TaskGroup akan menunggu semua tugasan selesai sebelum keluar daripada blok tersebut. Gunakan ini jika kerja tersebut mesti selesai sebelum anda memberi respons kepada pengguna.

Untuk kerja latar belakang yang berat, gunakan corak penyelia (supervisor pattern):

  • Cipta satu tugasan jangka panjang semasa permulaan (startup).
  • Gunakan baris gilir (queue) untuk menghantar kerja kepadanya.
  • Pengendali permintaan (request handler) hanya menambah item ke dalam baris gilir.

Ini memastikan kerja anda terus berjalan dan membolehkan anda menambah cubaan semula (retries) kemudian.

Dalam Python, anda mesti menguruskan tugasan anda sendiri. Jika anda tidak memegang rujukan, runtime tidak akan melindunginya.

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