asyncio görevleriniz çalışma sırasında ölebilir

Birçok Python servisi, arka plan işlerini çalıştırmak için bu deseni kullanır:

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

Amaç, istemciyi bekletmeden bir webhook göndermektir. Bu, geliştirme aşamasında çalışır. Ancak üretim (production) ortamında rastgele başarısız olur.

Sorun çöp toplama (garbage collection) işlemidir.

asyncio.create_task fonksiyonunu çağırdığınızda sonucu kaydetmezseniz, görev çöp toplama işlemine uygun hale gelir. Olay döngüsü (event loop), görevlere yalnızca zayıf referanslar (weak references) tutar. Eğer başka hiçbir şey o görevi işaret etmiyorsa, Python görev hâlâ çalışırken onu geri kazanabilir.

İşiniz öylece durur. Takip sisteminizde (tracker) hiçbir hata görünmez. Kod kusursuzdur. Görev sadece yok olur.

stderr çıktınızda şu günlüğü görebilirsiniz: Task was destroyed but it is pending!

Bu hatayı bulmak zordur çünkü genellikle asıl hatadan çok daha sonra ortaya çıkar.

Nasıl düzeltilir:

Referansları tutmak için bir arka plan görev seti kullanın.

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, görevi canlı tutar. Callback (geri çağırma) ise iş bittiğinde onu kaldırır. Bu, bellek sızıntılarını (memory leaks) önler ve çöp toplayıcının görevlerinizi sonlandırmasını engeller.

Python 3.11 ve daha yeni sürümler için TaskGroups kullanabilirsiniz. Bir TaskGroup, içindeki her göreve güçlü referanslar (strong references) tutar. Bir TaskGroup'un, bloktan çıkmadan önce tüm görevlerin bitmesini bekleyeceğini unutmayın. Eğer işin, kullanıcıya yanıt vermeden önce tamamlanması gerekiyorsa bunu kullanın.

Yoğun arka plan işleri için bir denetleyici (supervisor) deseni kullanın:

  • Başlangıçta uzun ömürlü bir görev oluşturun.
  • İşleri ona göndermek için bir kuyruk (queue) kullanın.
  • İstek işleyici (request handler) sadece öğeleri kuyruğa ekler.

Bu, işinizin devam etmesini sağlar ve daha sonra yeniden deneme (retry) mekanizmaları eklemenize olanak tanır.

Python'da görevlerinizin sahibi siz olmalısınız. Eğer bir referans tutmazsanız, çalışma zamanı (runtime) onları korumayacaktır.

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