"""fire-and-forget 태스크 공용 추적 헬퍼 (하드닝 R3 2026-10-09). 이벤트 루프는 태스크를 약참조로만 들고 있어 참조를 버리면 실행 중 GC 될 수 있다. 또 종료 drain 은 main._background_tasks 만 기다리므로, 엔드포인트가 만든 태스크도 같은 집합에 등록해 drain 대상에 포함시킨다(app.main 이 로드된 경우). """ import asyncio import sys _tracked: set = set() def _main_task_set(): mod = sys.modules.get("app.main") return getattr(mod, "_background_tasks", None) if mod else None def spawn_tracked_v2(coro) -> asyncio.Task: task = asyncio.create_task(coro) sets = [_tracked] main_set = _main_task_set() if main_set is not None: sets.append(main_set) for s in sets: s.add(task) task.add_done_callback(s.discard) return task