Explorar el Código

修复一些问题

xueyiming hace 4 días
padre
commit
c727a92203
Se han modificado 4 ficheros con 33 adiciones y 1 borrados
  1. 3 0
      .env.example
  2. 7 1
      supply_agent/tools/base.py
  3. 15 0
      supply_infra/config.py
  4. 8 0
      supply_infra/db/session.py

+ 3 - 0
.env.example

@@ -35,6 +35,9 @@ MYSQL_POOL_SIZE_CONTROL=1
 MYSQL_MAX_OVERFLOW=0
 MYSQL_POOL_TIMEOUT_SECONDS=10
 MYSQL_POOL_RECYCLE_SECONDS=1800
+MYSQL_CONNECT_TIMEOUT_SECONDS=10
+MYSQL_READ_TIMEOUT_SECONDS=30
+MYSQL_WRITE_TIMEOUT_SECONDS=30
 MYSQL_OPERATIONAL_RESERVE=4
 MYSQL_ECHO=false
 

+ 7 - 1
supply_agent/tools/base.py

@@ -1,5 +1,6 @@
 from __future__ import annotations
 
+import asyncio
 import inspect
 import json
 import types
@@ -146,7 +147,12 @@ class Tool:
         return self.func(**kwargs)
 
     async def acall(self, **kwargs: Any) -> Any:
-        result = self.func(**kwargs)
+        if asyncio.iscoroutinefunction(self.func):
+            return await self.func(**kwargs)
+        # 同步函数(如阻塞式 DB 调用)丢到线程池执行,避免整体卡住事件循环——
+        # 否则一旦某次同步调用阻塞,async 调用方设置的超时(如 asyncio.wait_for)
+        # 完全无法取消/中断它,会导致 Agent 无限期卡死。
+        result = await asyncio.to_thread(self.func, **kwargs)
         if inspect.isawaitable(result):
             return await result
         return result

+ 15 - 0
supply_infra/config.py

@@ -51,6 +51,21 @@ class InfraSettings(BaseSettings):
         ge=60,
         alias="MYSQL_POOL_RECYCLE_SECONDS",
     )
+    mysql_connect_timeout_seconds: int = Field(
+        default=10,
+        ge=1,
+        alias="MYSQL_CONNECT_TIMEOUT_SECONDS",
+    )
+    mysql_read_timeout_seconds: int = Field(
+        default=30,
+        ge=1,
+        alias="MYSQL_READ_TIMEOUT_SECONDS",
+    )
+    mysql_write_timeout_seconds: int = Field(
+        default=30,
+        ge=1,
+        alias="MYSQL_WRITE_TIMEOUT_SECONDS",
+    )
     mysql_operational_reserve: int = Field(
         default=4,
         ge=2,

+ 8 - 0
supply_infra/db/session.py

@@ -39,6 +39,14 @@ def get_engine():
             pool_recycle=settings.mysql_pool_recycle_seconds,
             pool_pre_ping=True,
             echo=settings.mysql_echo,
+            # pymysql 默认 read/write timeout 为 None(永不超时):一旦锁等待或网络抖动,
+            # 查询会在 socket.recv() 上永久阻塞,且这类同步阻塞无法被 asyncio 超时取消。
+            # 这里显式加 socket 级超时,确保任何一次查询最多阻塞有限时间就会抛异常。
+            connect_args={
+                "connect_timeout": settings.mysql_connect_timeout_seconds,
+                "read_timeout": settings.mysql_read_timeout_seconds,
+                "write_timeout": settings.mysql_write_timeout_seconds,
+            },
         )
         if _engine.dialect.name == "mysql":
             event.listen(_engine, "connect", _set_mysql_session_china_time)