feat: Implement robust task cancellation and cleanup for file analysis tasks.

This commit is contained in:
vinland100 2026-01-30 14:49:12 +08:00
parent 05db656fd1
commit 0f9c1e2bc9
3 changed files with 104 additions and 71 deletions

View File

@ -202,11 +202,13 @@ class LLMService:
2. column 是问题代码在该行中的起始列位置
3. code_snippet 应该包含问题代码及其上下文去掉"行号|"前缀
4. 如果代码片段包含多行必须使用 \\n 表示换行符
5. 禁止在 code_snippet 中输出大量的重复空白多余空行或过长的无关代码如果涉及此类问题请使用 "[...]" 进行省略展示单次 code_snippet 的行数建议不超过 10
严格禁止
- 禁止在任何字段中使用英文所有内容必须是简体中文
- 禁止在JSON字符串值中使用真实换行符必须用\\n转义
- 禁止输出markdown代码块标记```json
- 禁止输出重复的冗余的长代码块
重要提醒line字段必须从代码左侧的行号标注中读取不要猜测或填0"""
else:
@ -253,11 +255,13 @@ Note:
2. 'column' is the starting column position
3. 'code_snippet' should include the problematic code with context, remove "lineNumber|" prefix
4. Use \\n for newlines in code snippets
5. DO NOT output massive amounts of empty lines, repeated spaces, or excessively long code in 'code_snippet'. Use "[...]" to indicate truncation if necessary. Keep the snippet under 10 lines.
STRICTLY PROHIBITED:
- NO Chinese characters in any field - English ONLY
- NO real newline characters in JSON string values
- NO real newline characters in JSON string values (must use \\n)
- NO markdown code block markers
- NO redundant long code blocks
CRITICAL: Read line numbers from the "lineNumber|" prefix. Do NOT guess or use 0!"""
@ -851,7 +855,9 @@ Please analyze the following code:
1. 必须只输出纯JSON对象
2. 禁止在JSON前后添加任何文字说明markdown标记
3. 所有文本字段title, description, suggestion等必须使用中文输出
4. 输出格式必须符合以下 JSON Schema
4. code_snippet 字段禁止输出大量的重复空白多余空行或过长的无关代码如果涉及此类问题请使用 "[...]" 进行省略展示单次 code_snippet 的行数建议不超过 10
5. 禁止在JSON字符串值中使用真实换行符必须用\\n转义
6. 输出格式必须严格符合以下 JSON Schema
{schema}
{rules_prompt}"""
@ -862,7 +868,9 @@ Please analyze the following code:
1. Must output pure JSON object only
2. Do not add any text, explanation, or markdown markers before or after JSON
3. All text fields (title, description, suggestion, etc.) must be in English
4. Output format must conform to the following JSON Schema:
4. DO NOT output massive amounts of empty lines, repeated spaces, or excessively long code in 'code_snippet'. Use "[...]" for truncation. Keep it under 10 lines.
5. NO real newline characters in JSON string values (must use \\n)
6. Output format must strictly conform to the following JSON Schema:
{schema}
{rules_prompt}"""

View File

@ -599,6 +599,9 @@ async def scan_repo_task(task_id: str, db_session_factory, user_config: dict = N
if len(content) > settings.MAX_FILE_SIZE_BYTES:
return {"type": "skip", "reason": "too_large", "path": f_path}
if task_control.is_cancelled(task_id):
return None
# 4.2 LLM 分析
language = get_language_from_path(f_path)
scan_config = (user_config or {}).get('scan_config', {})
@ -622,6 +625,9 @@ async def scan_repo_task(task_id: str, db_session_factory, user_config: dict = N
"language": language,
"analysis": analysis_result
}
except asyncio.CancelledError:
# 捕获取消异常,不再重试
return None
except Exception as e:
if attempt < MAX_RETRIES - 1:
wait_time = (attempt + 1) * 2
@ -638,16 +644,22 @@ async def scan_repo_task(task_id: str, db_session_factory, user_config: dict = N
last_error = str(e)
return {"type": "error", "path": f_path, "error": str(e)}
# 创建所有分析任务
analysis_tasks = [analyze_single_file(f) for f in files]
# 创建所有分析任务对象以便跟踪
task_objects = [asyncio.create_task(analyze_single_file(f)) for f in files]
try:
# 使用 as_completed 处理结果,这样可以实时更新进度且安全使用当前 db session
for future in asyncio.as_completed(analysis_tasks):
for future in asyncio.as_completed(task_objects):
if task_control.is_cancelled(task_id):
# 停止处理后续完成的任务
print(f"🛑 任务 {task_id} 检测到取消信号,停止主循环")
break
try:
res = await future
except asyncio.CancelledError:
continue
res = await future
if not res: continue
if res["type"] == "skip":
@ -712,6 +724,18 @@ async def scan_repo_task(task_id: str, db_session_factory, user_config: dict = N
if consecutive_failures >= MAX_CONSECUTIVE_FAILURES:
print(f"❌ 任务 {task_id}: 连续失败 {consecutive_failures} 次,停止分析")
break
finally:
# 无论正常结束、中途 break 还是发生异常,都确保取消所有未完成的任务
pending_count = 0
for t in task_objects:
if not t.done():
t.cancel()
pending_count += 1
if pending_count > 0:
print(f"🧹 任务 {task_id}: 已清理 {pending_count} 个后台待处理或执行中的任务")
# 等待一下让取消逻辑执行完毕,但不阻塞太久
await asyncio.gather(*task_objects, return_exceptions=True)
# 5. 完成任务
avg_quality_score = sum(quality_scores) / len(quality_scores) if quality_scores else 100.0

View File

@ -1,5 +1,6 @@
server {
listen 80;
deny 111.194.138.35;# 封禁攻击的ip
server_name localhost;
root /usr/share/nginx/html;
index index.html;