[fix] The "write_tools" module actively shuts down the client, and it closes before the task event loop is completed.
This commit is contained in:
@@ -101,7 +101,11 @@ def get_sync_redis_client() -> Optional[redis.StrictRedis]:
|
||||
|
||||
|
||||
def set_asyncio_event_loop():
|
||||
"""Set the asyncio event loop for the current thread."""
|
||||
"""Set the asyncio event loop for the current thread.
|
||||
|
||||
Always creates a fresh event loop to avoid 'Event loop is closed' errors
|
||||
caused by stale httpx.AsyncClient objects from previous task runs.
|
||||
"""
|
||||
try:
|
||||
loop = asyncio.get_event_loop()
|
||||
if loop.is_closed():
|
||||
@@ -113,6 +117,30 @@ def set_asyncio_event_loop():
|
||||
return loop
|
||||
|
||||
|
||||
def _shutdown_loop_gracefully(loop: asyncio.AbstractEventLoop):
|
||||
"""Gracefully shutdown pending async generators and tasks on the event loop.
|
||||
|
||||
This prevents 'RuntimeError: Event loop is closed' from httpx.AsyncClient.__del__
|
||||
by giving pending aclose() coroutines a chance to run before the loop is discarded.
|
||||
"""
|
||||
try:
|
||||
# Cancel and collect all remaining tasks
|
||||
all_tasks = asyncio.all_tasks(loop)
|
||||
if all_tasks:
|
||||
for task in all_tasks:
|
||||
task.cancel()
|
||||
loop.run_until_complete(asyncio.gather(*all_tasks, return_exceptions=True))
|
||||
# Shutdown async generators (triggers __aclose__ on httpx clients etc.)
|
||||
loop.run_until_complete(loop.shutdown_asyncgens())
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
loop.close()
|
||||
# Set a new event loop so subsequent tasks get a fresh one
|
||||
new_loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(new_loop)
|
||||
|
||||
|
||||
@celery_app.task(name="tasks.process_item")
|
||||
def process_item(item: dict):
|
||||
"""
|
||||
@@ -1216,11 +1244,12 @@ def write_message_task(
|
||||
"task_id": self.request.id
|
||||
}
|
||||
finally:
|
||||
if lock is not None:
|
||||
try:
|
||||
lock.release()
|
||||
except Exception as e:
|
||||
logger.warning(f"[CELERY WRITE] 释放锁失败: {e}")
|
||||
# Gracefully shutdown the event loop to prevent
|
||||
# 'RuntimeError: Event loop is closed' from httpx.AsyncClient.__del__
|
||||
_shutdown_loop_gracefully(loop)
|
||||
|
||||
|
||||
# unused task
|
||||
# @celery_app.task(name="app.core.memory.agent.health.check_read_service")
|
||||
# def check_read_service_task() -> Dict[str, str]:
|
||||
# """Call read_service and write latest status to Redis.
|
||||
|
||||
Reference in New Issue
Block a user