任务队列执行超时自动更为状态为失败,默认超时60秒
This commit is contained in:
parent
3c2d21981d
commit
14cb5d68db
@ -1,8 +1,11 @@
|
|||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Dict, List
|
import time
|
||||||
|
import asyncio
|
||||||
import uuid
|
import uuid
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
import dramatiq
|
import dramatiq
|
||||||
from dramatiq.brokers.redis import RedisBroker
|
from dramatiq.brokers.redis import RedisBroker
|
||||||
@ -92,9 +95,8 @@ async def _execute_node_job(payload: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
job_id = payload.get("job_id")
|
job_id = payload.get("job_id")
|
||||||
started_iso = None
|
started_iso = None
|
||||||
try:
|
try:
|
||||||
import time
|
|
||||||
started = time.time()
|
started = time.time()
|
||||||
started_iso = __import__('datetime').datetime.now().isoformat()
|
started_iso = datetime.now().isoformat()
|
||||||
modifier = get_logged_user(session_cookie) or 'system'
|
modifier = get_logged_user(session_cookie) or 'system'
|
||||||
await _push_status_to_jingrow(job_id, {
|
await _push_status_to_jingrow(job_id, {
|
||||||
'status': 'started',
|
'status': 'started',
|
||||||
@ -103,7 +105,7 @@ async def _execute_node_job(payload: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
})
|
})
|
||||||
result = await executor.execute_node(node_type, flow_id, context, inputs, config, session_cookie)
|
result = await executor.execute_node(node_type, flow_id, context, inputs, config, session_cookie)
|
||||||
ended = time.time()
|
ended = time.time()
|
||||||
ended_iso = __import__('datetime').datetime.now().isoformat()
|
ended_iso = datetime.now().isoformat()
|
||||||
await _push_status_to_jingrow(job_id, {
|
await _push_status_to_jingrow(job_id, {
|
||||||
'status': 'finished' if result.get('success') else 'failed',
|
'status': 'finished' if result.get('success') else 'failed',
|
||||||
'exc_info': None if result.get('success') else result.get('error'),
|
'exc_info': None if result.get('success') else result.get('error'),
|
||||||
@ -114,7 +116,7 @@ async def _execute_node_job(payload: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
})
|
})
|
||||||
return result
|
return result
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
ended_iso = __import__('datetime').datetime.now().isoformat()
|
ended_iso = datetime.now().isoformat()
|
||||||
modifier = get_logged_user(session_cookie) or 'system'
|
modifier = get_logged_user(session_cookie) or 'system'
|
||||||
await _push_status_to_jingrow(job_id, {
|
await _push_status_to_jingrow(job_id, {
|
||||||
'status': 'failed',
|
'status': 'failed',
|
||||||
@ -300,10 +302,11 @@ async def _execute_agent_job(payload: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
return {"success": False, "error": f"Failed to get agent detail: {str(e)}"}
|
return {"success": False, "error": f"Failed to get agent detail: {str(e)}"}
|
||||||
|
|
||||||
|
|
||||||
@dramatiq.actor(max_retries=3, time_limit=60_000)
|
@dramatiq.actor(max_retries=0, time_limit=60_000)
|
||||||
def execute_local_scheduled_job(job_json: str) -> None:
|
def execute_local_scheduled_job(job_json: str) -> None:
|
||||||
"""Worker 执行入口:job_json 为 JSON 字符串,包含 target_type 及其参数。
|
"""Worker 执行入口:job_json 为 JSON 字符串,包含 target_type 及其参数。
|
||||||
target_type: agent
|
target_type: agent
|
||||||
|
超时不重试(max_retries=0)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
init_hooks(clear_cache=False)
|
init_hooks(clear_cache=False)
|
||||||
@ -313,38 +316,79 @@ def execute_local_scheduled_job(job_json: str) -> None:
|
|||||||
try:
|
try:
|
||||||
payload = json.loads(job_json)
|
payload = json.loads(job_json)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
logger.error(f"解析任务 JSON 失败: {e}")
|
||||||
return
|
return
|
||||||
|
|
||||||
import asyncio
|
|
||||||
|
|
||||||
if not payload.get("job_id"):
|
if not payload.get("job_id"):
|
||||||
payload["job_id"] = str(uuid.uuid4())
|
payload["job_id"] = str(uuid.uuid4())
|
||||||
|
|
||||||
target_type = payload.get("target_type")
|
target_type = payload.get("target_type")
|
||||||
|
job_id = payload["job_id"]
|
||||||
|
sc = payload.get("session_cookie") or (payload.get("context") or {}).get("session_cookie")
|
||||||
|
modifier = get_logged_user(sc) or 'system'
|
||||||
|
started = None
|
||||||
|
started_iso = None
|
||||||
|
|
||||||
if target_type == "agent":
|
try:
|
||||||
import time
|
if target_type == "agent":
|
||||||
started = time.time()
|
started = time.time()
|
||||||
started_iso = __import__('datetime').datetime.now().isoformat()
|
started_iso = datetime.now().isoformat()
|
||||||
sc = payload.get("session_cookie") or (payload.get("context") or {}).get("session_cookie")
|
asyncio.run(_push_status_to_jingrow(job_id, {
|
||||||
modifier = get_logged_user(sc) or 'system'
|
"status": "started",
|
||||||
asyncio.run(_push_status_to_jingrow(payload["job_id"], {
|
"started_at": started_iso,
|
||||||
"status": "started",
|
"session_cookie": sc,
|
||||||
"started_at": started_iso,
|
"modified_by": modifier
|
||||||
"session_cookie": sc,
|
}))
|
||||||
"modified_by": modifier
|
result = asyncio.run(_execute_agent_job(payload))
|
||||||
}))
|
ended = time.time()
|
||||||
result = asyncio.run(_execute_agent_job(payload))
|
ended_iso = datetime.now().isoformat()
|
||||||
ended = time.time()
|
asyncio.run(_push_status_to_jingrow(job_id, {
|
||||||
ended_iso = __import__('datetime').datetime.now().isoformat()
|
"status": "finished" if result.get("success") else "failed",
|
||||||
asyncio.run(_push_status_to_jingrow(payload["job_id"], {
|
"ended_at": ended_iso,
|
||||||
"status": "finished" if result.get("success") else "failed",
|
"time_taken": round(max(0.0, ended - started), 3),
|
||||||
"ended_at": ended_iso,
|
"session_cookie": sc,
|
||||||
"time_taken": round(max(0.0, ended - started), 3),
|
"modified_by": modifier,
|
||||||
"session_cookie": sc,
|
}))
|
||||||
"modified_by": modifier,
|
else:
|
||||||
}))
|
logger.warning(f"Unsupported target_type: {target_type}")
|
||||||
else:
|
except BaseException as e:
|
||||||
logger.warning(f"Unsupported target_type: {target_type}")
|
# 捕获所有异常(包括 TimeLimit 等系统异常),确保任务状态正确更新
|
||||||
|
ended_iso = datetime.now().isoformat()
|
||||||
|
time_taken = round(max(0.0, (time.time() - started) if started else 0.0), 3)
|
||||||
|
error_msg = str(e)
|
||||||
|
# 判断是否为超时异常
|
||||||
|
is_timeout = "timeout" in error_msg.lower() or "time limit" in error_msg.lower()
|
||||||
|
if is_timeout:
|
||||||
|
error_msg = f"任务执行超时: {error_msg}"
|
||||||
|
# 状态更新失败不应影响异常传播,使用新的事件循环避免与已中断的循环冲突
|
||||||
|
status_updated = False
|
||||||
|
try:
|
||||||
|
loop = asyncio.new_event_loop()
|
||||||
|
asyncio.set_event_loop(loop)
|
||||||
|
try:
|
||||||
|
loop.run_until_complete(_push_status_to_jingrow(job_id, {
|
||||||
|
"status": "failed",
|
||||||
|
"ended_at": ended_iso,
|
||||||
|
"time_taken": time_taken,
|
||||||
|
"exc_info": error_msg,
|
||||||
|
"session_cookie": sc,
|
||||||
|
"modified_by": modifier,
|
||||||
|
}))
|
||||||
|
status_updated = True
|
||||||
|
finally:
|
||||||
|
loop.close()
|
||||||
|
except Exception as update_error:
|
||||||
|
logger.error(f"更新任务 {job_id} 状态失败: {update_error}")
|
||||||
|
|
||||||
|
# 状态更新成功则只记录警告,不抛出异常;更新失败才抛出异常
|
||||||
|
if status_updated:
|
||||||
|
if is_timeout:
|
||||||
|
logger.warning(f"任务 {job_id} 执行超时,已更新状态为失败")
|
||||||
|
else:
|
||||||
|
logger.warning(f"任务 {job_id} 执行失败: {error_msg}")
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
logger.error(f"任务 {job_id} 执行失败且状态更新失败: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user