fix: resource leaks and connection safety

- Use context managers for all SQLite connections
- Add temp file cleanup in report download endpoint
- Add finally blocks for file handle cleanup in simulation runner
This commit is contained in:
Nyk 2026-03-17 21:19:27 +07:00
parent 985f89f49a
commit 7d76f663dc
6 changed files with 237 additions and 238 deletions

View File

@ -6,7 +6,7 @@ Report API路由
import os
import traceback
import threading
from flask import request, jsonify, send_file
from flask import request, jsonify, send_file, after_this_request
from . import report_bp
from ..config import Config
@ -415,6 +415,15 @@ def download_report(report_id: str):
f.write(report.markdown_content)
temp_path = f.name
@after_this_request
def cleanup(response):
try:
if temp_path and os.path.exists(temp_path):
os.unlink(temp_path)
except OSError:
pass
return response
return send_file(
temp_path,
as_attachment=True,

View File

@ -2016,7 +2016,7 @@ def get_simulation_posts(simulation_id: str):
})
import sqlite3
conn = sqlite3.connect(db_path)
with sqlite3.connect(db_path) as conn:
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
@ -2036,8 +2036,6 @@ def get_simulation_posts(simulation_id: str):
posts = []
total = 0
conn.close()
return jsonify({
"success": True,
"data": {
@ -2089,7 +2087,7 @@ def get_simulation_comments(simulation_id: str):
})
import sqlite3
conn = sqlite3.connect(db_path)
with sqlite3.connect(db_path) as conn:
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
@ -2113,8 +2111,6 @@ def get_simulation_comments(simulation_id: str):
except sqlite3.OperationalError:
comments = []
conn.close()
return jsonify({
"success": True,
"data": {

View File

@ -426,6 +426,7 @@ class SimulationRunner:
main_log_path = os.path.join(sim_dir, "simulation.log")
main_log_file = open(main_log_path, 'w', encoding='utf-8')
try:
# 设置子进程环境变量,确保 Windows 上使用 UTF-8 编码
# 这可以修复第三方库(如 OASIS读取文件时未指定编码的问题
env = os.environ.copy()
@ -445,6 +446,9 @@ class SimulationRunner:
env=env, # 传递带有 UTF-8 设置的环境变量
start_new_session=True, # 创建新进程组,确保服务器关闭时能终止所有相关进程
)
except Exception:
main_log_file.close()
raise
# 保存文件句柄以便后续关闭
cls._stdout_files[simulation_id] = main_log_file
@ -1667,7 +1671,7 @@ class SimulationRunner:
results = []
try:
conn = sqlite3.connect(db_path)
with sqlite3.connect(db_path) as conn:
cursor = conn.cursor()
if agent_id is not None:
@ -1701,8 +1705,6 @@ class SimulationRunner:
"platform": platform_name
})
conn.close()
except Exception as e:
logger.error(f"读取Interview历史失败 ({platform_name}): {e}")

View File

@ -528,7 +528,7 @@ class ParallelIPCHandler:
return result
try:
conn = sqlite3.connect(db_path)
with sqlite3.connect(db_path) as conn:
cursor = conn.cursor()
# 查询最新的Interview记录
@ -550,8 +550,6 @@ class ParallelIPCHandler:
except json.JSONDecodeError:
result["response"] = info_json
conn.close()
except Exception as e:
print(f" 读取Interview结果失败: {e}")
@ -679,7 +677,7 @@ def fetch_new_actions_from_db(
return actions, new_last_rowid
try:
conn = sqlite3.connect(db_path)
with sqlite3.connect(db_path) as conn:
cursor = conn.cursor()
# 使用 rowid 来追踪已处理的记录rowid 是 SQLite 的内置自增字段)
@ -738,8 +736,6 @@ def fetch_new_actions_from_db(
'action_type': action_type,
'action_args': simplified_args,
})
conn.close()
except Exception as e:
print(f"读取数据库动作失败: {e}")

View File

@ -311,7 +311,7 @@ class IPCHandler:
return result
try:
conn = sqlite3.connect(db_path)
with sqlite3.connect(db_path) as conn:
cursor = conn.cursor()
# 查询最新的Interview记录
@ -333,8 +333,6 @@ class IPCHandler:
except json.JSONDecodeError:
result["response"] = info_json
conn.close()
except Exception as e:
print(f" 读取Interview结果失败: {e}")

View File

@ -311,7 +311,7 @@ class IPCHandler:
return result
try:
conn = sqlite3.connect(db_path)
with sqlite3.connect(db_path) as conn:
cursor = conn.cursor()
# 查询最新的Interview记录
@ -333,8 +333,6 @@ class IPCHandler:
except json.JSONDecodeError:
result["response"] = info_json
conn.close()
except Exception as e:
print(f" 读取Interview结果失败: {e}")