ನಿಮ್ಮ asyncio task ಕೆಲಸದ ಮಧ್ಯದಲ್ಲಿ ನಿಲ್ಲಬಹುದು

ಅನೇಕ Python ಸೇವೆಗಳು ಹಿನ್ನೆಲೆ ಕೆಲಸವನ್ನು (background work) ನಡೆಸಲು ಈ ಮಾದರಿಯನ್ನು ಬಳಸುತ್ತವೆ:

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

ಇದರ ಗುರಿ ಕ್ಲೈಂಟ್ ಕಾಯುವಂತೆ ಮಾಡದೆ webhook ಅನ್ನು ಕಳುಹಿಸುವುದು. ಇದು ડેವಲಪ್‌ಮೆಂಟ್‌ನಲ್ಲಿ (development) ಕೆಲಸ ಮಾಡುತ್ತದೆ. ಆದರೆ ಪ್ರೊಡಕ್ಷನ್‌ನಲ್ಲಿ (production), ಇದು ಯಾದೃಚ್ಛಿಕವಾಗಿ (randomly) ವಿಫಲವಾಗುತ್ತದೆ.

ಸಮಸ್ಯೆ ಎಂದರೆ garbage collection.

ನೀವು asyncio.create_task ಅನ್ನು ಕರೆ ಮಾಡಿ ಅದರ ಫಲಿತಾಂಶವನ್ನು ಉಳಿಸಿಕೊಳ್ಳದಿದ್ದರೆ, ಆ task garbage collection ಗೆ ಅರ್ಹವಾಗುತ್ತದೆ. Event loop ಕೇವಲ task ಗಳಿಗಾಗಿ weak references ಅನ್ನು ಮಾತ್ರ ಇರಿಸಿಕೊಳ್ಳುತ್ತದೆ. ಬೇರೆ ಯಾವುದೂ ಆ task ಅನ್ನು ಸೂಚಿಸದಿದ್ದರೆ, ಅದು ಇನ್ನೂ ಚಾಲನೆಯಲ್ಲಿದ್ದರೂ Python ಅದನ್ನು ಮರುಪಡೆಯಬಹುದು (reclaim).

ನಿಮ್ಮ ಕೆಲಸವು ಇದ್ದಕ್ಕಿದ್ದಂತೆ ನಿಂತುಹೋಗುತ್ತದೆ. ನಿಮ್ಮ ಟ್ರ್ಯಾಕರ್‌ನಲ್ಲಿ ಯಾವುದೇ ದೋಷಗಳು (errors) ಕಂಡುಬರುವುದಿಲ್ಲ. ಕೋಡ್ ಪರಿಪೂರ್ಣವಾಗಿರುತ್ತದೆ. ಆದರೆ task ಸುಮ್ಮನೆ ಮಾಯವಾಗುತ್ತದೆ.

ನಿಮ್ಮ 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 task ಅನ್ನು ಜೀವಂತವಾಗಿರಿಸುತ್ತದೆ. ಕೆಲಸ ಮುಗಿದ ನಂತರ callback ಅದನ್ನು ತೆಗೆದುಹಾಕುತ್ತದೆ. ಇದು memory leaks ಅನ್ನು ತಡೆಯುತ್ತದೆ ಮತ್ತು garbage collector ನಿಮ್ಮ task ಗಳನ್ನು ಕೊಲ್ಲದಂತೆ ತಡೆಯುತ್ತದೆ.

Python 3.11 ಮತ್ತು ಅದಕ್ಕಿಂತ ಹೊಸದಿದ್ದಲ್ಲಿ, ನೀವು TaskGroups ಅನ್ನು ಬಳಸಬಹುದು. TaskGroup ತನ್ನ ಒಳಗಿರುವ ಪ್ರತಿಯೊಂದು task ಗೂ strong references ಅನ್ನು ಹೊಂದಿರುತ್ತದೆ. ಗಮನಿಸಿ, TaskGroup ಬ್ಲಾಕ್‌ನಿಂದ ಹೊರಬರುವ ಮೊದಲು ಎಲ್ಲಾ task ಗಳು ಮುಗಿಯುವವರೆಗೆ ಕಾಯುತ್ತದೆ. ನೀವು ಬಳಕೆದಾರರಿಗೆ ಪ್ರತಿಕ್ರಿಯಿಸುವ ಮೊದಲು ಕೆಲಸವು ಪೂರ್ಣಗೊಳ್ಳಬೇಕೆಂದಿದ್ದರೆ ಇದನ್ನು ಬಳಸಿ.

ಭಾರೀ ಹಿನ್ನೆಲೆ ಕೆಲಸಗಳಿಗಾಗಿ (heavy background work), supervisor pattern ಬಳಸಿ:

  • ಪ್ರಾರಂಭದ ಸಮಯದಲ್ಲಿ (startup) ಒಂದು ದೀರ್ಘಕಾಲ ಬಾಳಿಕೆ ಬರುವ task ಅನ್ನು ರಚಿಸಿ.
  • ಅದಕ್ಕೆ ಕೆಲಸವನ್ನು ಕಳುಹಿಸಲು queue ಅನ್ನು ಬಳಸಿ.
  • request handler ಕೇವಲ queue ಗೆ ಐಟಂಗಳನ್ನು ಸೇರಿಸುತ್ತದೆ.

ಇದು ನಿಮ್ಮ ಕೆಲಸವು ಮುಂದುವರಿಯುವುದನ್ನು ಖಚಿತಪಡಿಸುತ್ತದೆ ಮತ್ತು ನಂತರ retries ಅನ್ನು ಸೇರಿಸಲು ಅನುವು ಮಾಡಿಕೊಡುತ್ತದೆ.

Python ನಲ್ಲಿ, ನೀವು ನಿಮ್ಮ task ಗಳನ್ನು ಹೊಂದಿರಬೇಕು. ನೀವು ರೆಫರೆನ್ಸ್ ಅನ್ನು ಹಿಡಿದಿಟ್ಟುಕೊಳ್ಳದಿದ್ದರೆ, runtime ಅವುಗಳನ್ನು ರಕ್ಷಿಸುವುದಿಲ್ಲ.

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