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:
parent
985f89f49a
commit
7d76f663dc
|
|
@ -6,7 +6,7 @@ Report API路由
|
||||||
import os
|
import os
|
||||||
import traceback
|
import traceback
|
||||||
import threading
|
import threading
|
||||||
from flask import request, jsonify, send_file
|
from flask import request, jsonify, send_file, after_this_request
|
||||||
|
|
||||||
from . import report_bp
|
from . import report_bp
|
||||||
from ..config import Config
|
from ..config import Config
|
||||||
|
|
@ -415,6 +415,15 @@ def download_report(report_id: str):
|
||||||
f.write(report.markdown_content)
|
f.write(report.markdown_content)
|
||||||
temp_path = f.name
|
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(
|
return send_file(
|
||||||
temp_path,
|
temp_path,
|
||||||
as_attachment=True,
|
as_attachment=True,
|
||||||
|
|
|
||||||
|
|
@ -2016,7 +2016,7 @@ def get_simulation_posts(simulation_id: str):
|
||||||
})
|
})
|
||||||
|
|
||||||
import sqlite3
|
import sqlite3
|
||||||
conn = sqlite3.connect(db_path)
|
with sqlite3.connect(db_path) as conn:
|
||||||
conn.row_factory = sqlite3.Row
|
conn.row_factory = sqlite3.Row
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
|
@ -2036,8 +2036,6 @@ def get_simulation_posts(simulation_id: str):
|
||||||
posts = []
|
posts = []
|
||||||
total = 0
|
total = 0
|
||||||
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"success": True,
|
"success": True,
|
||||||
"data": {
|
"data": {
|
||||||
|
|
@ -2089,7 +2087,7 @@ def get_simulation_comments(simulation_id: str):
|
||||||
})
|
})
|
||||||
|
|
||||||
import sqlite3
|
import sqlite3
|
||||||
conn = sqlite3.connect(db_path)
|
with sqlite3.connect(db_path) as conn:
|
||||||
conn.row_factory = sqlite3.Row
|
conn.row_factory = sqlite3.Row
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
|
@ -2113,8 +2111,6 @@ def get_simulation_comments(simulation_id: str):
|
||||||
except sqlite3.OperationalError:
|
except sqlite3.OperationalError:
|
||||||
comments = []
|
comments = []
|
||||||
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"success": True,
|
"success": True,
|
||||||
"data": {
|
"data": {
|
||||||
|
|
|
||||||
|
|
@ -426,6 +426,7 @@ class SimulationRunner:
|
||||||
main_log_path = os.path.join(sim_dir, "simulation.log")
|
main_log_path = os.path.join(sim_dir, "simulation.log")
|
||||||
main_log_file = open(main_log_path, 'w', encoding='utf-8')
|
main_log_file = open(main_log_path, 'w', encoding='utf-8')
|
||||||
|
|
||||||
|
try:
|
||||||
# 设置子进程环境变量,确保 Windows 上使用 UTF-8 编码
|
# 设置子进程环境变量,确保 Windows 上使用 UTF-8 编码
|
||||||
# 这可以修复第三方库(如 OASIS)读取文件时未指定编码的问题
|
# 这可以修复第三方库(如 OASIS)读取文件时未指定编码的问题
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
|
|
@ -445,6 +446,9 @@ class SimulationRunner:
|
||||||
env=env, # 传递带有 UTF-8 设置的环境变量
|
env=env, # 传递带有 UTF-8 设置的环境变量
|
||||||
start_new_session=True, # 创建新进程组,确保服务器关闭时能终止所有相关进程
|
start_new_session=True, # 创建新进程组,确保服务器关闭时能终止所有相关进程
|
||||||
)
|
)
|
||||||
|
except Exception:
|
||||||
|
main_log_file.close()
|
||||||
|
raise
|
||||||
|
|
||||||
# 保存文件句柄以便后续关闭
|
# 保存文件句柄以便后续关闭
|
||||||
cls._stdout_files[simulation_id] = main_log_file
|
cls._stdout_files[simulation_id] = main_log_file
|
||||||
|
|
@ -1667,7 +1671,7 @@ class SimulationRunner:
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
conn = sqlite3.connect(db_path)
|
with sqlite3.connect(db_path) as conn:
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
||||||
if agent_id is not None:
|
if agent_id is not None:
|
||||||
|
|
@ -1701,8 +1705,6 @@ class SimulationRunner:
|
||||||
"platform": platform_name
|
"platform": platform_name
|
||||||
})
|
})
|
||||||
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"读取Interview历史失败 ({platform_name}): {e}")
|
logger.error(f"读取Interview历史失败 ({platform_name}): {e}")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -528,7 +528,7 @@ class ParallelIPCHandler:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
try:
|
try:
|
||||||
conn = sqlite3.connect(db_path)
|
with sqlite3.connect(db_path) as conn:
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
||||||
# 查询最新的Interview记录
|
# 查询最新的Interview记录
|
||||||
|
|
@ -550,8 +550,6 @@ class ParallelIPCHandler:
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
result["response"] = info_json
|
result["response"] = info_json
|
||||||
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f" 读取Interview结果失败: {e}")
|
print(f" 读取Interview结果失败: {e}")
|
||||||
|
|
||||||
|
|
@ -679,7 +677,7 @@ def fetch_new_actions_from_db(
|
||||||
return actions, new_last_rowid
|
return actions, new_last_rowid
|
||||||
|
|
||||||
try:
|
try:
|
||||||
conn = sqlite3.connect(db_path)
|
with sqlite3.connect(db_path) as conn:
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
||||||
# 使用 rowid 来追踪已处理的记录(rowid 是 SQLite 的内置自增字段)
|
# 使用 rowid 来追踪已处理的记录(rowid 是 SQLite 的内置自增字段)
|
||||||
|
|
@ -738,8 +736,6 @@ def fetch_new_actions_from_db(
|
||||||
'action_type': action_type,
|
'action_type': action_type,
|
||||||
'action_args': simplified_args,
|
'action_args': simplified_args,
|
||||||
})
|
})
|
||||||
|
|
||||||
conn.close()
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"读取数据库动作失败: {e}")
|
print(f"读取数据库动作失败: {e}")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -311,7 +311,7 @@ class IPCHandler:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
try:
|
try:
|
||||||
conn = sqlite3.connect(db_path)
|
with sqlite3.connect(db_path) as conn:
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
||||||
# 查询最新的Interview记录
|
# 查询最新的Interview记录
|
||||||
|
|
@ -333,8 +333,6 @@ class IPCHandler:
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
result["response"] = info_json
|
result["response"] = info_json
|
||||||
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f" 读取Interview结果失败: {e}")
|
print(f" 读取Interview结果失败: {e}")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -311,7 +311,7 @@ class IPCHandler:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
try:
|
try:
|
||||||
conn = sqlite3.connect(db_path)
|
with sqlite3.connect(db_path) as conn:
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
||||||
# 查询最新的Interview记录
|
# 查询最新的Interview记录
|
||||||
|
|
@ -333,8 +333,6 @@ class IPCHandler:
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
result["response"] = info_json
|
result["response"] = info_json
|
||||||
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f" 读取Interview结果失败: {e}")
|
print(f" 读取Interview结果失败: {e}")
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue