DATABASE_CURSOR_FIX.md•4.4 kB
# 数据库游标管理修复文档
## 问题描述
在FastMCP服务器异步启动过程中,系统报错:`Cursor already closed`。这表明在异步函数中尝试使用已关闭的数据库游标进行数据库操作。
## 错误日志
```
2025-07-22 00:03:01,736 18830 ERROR kaikong17 odoo.addons.mcp_server.services.fast_mcp_service: 异步启动FastMCP服务器失败: Cursor already closed
2025-07-22 00:03:01,749 18830 ERROR kaikong17 odoo.addons.mcp_server.services.fast_mcp_service: 异常详情: Traceback (most recent call last):
File "/odoo17/odoo/sql_db.py", line 332, in execute
res = self._obj.execute(query, params)
File "/odoo17/odoo/sql_db.py", line 493, in __getattr__
raise psycopg2.InterfaceError("Cursor already closed")
psycopg2.InterfaceError: Cursor already closed
```
## 问题原因
在`mcp_server/services/fast_mcp_service.py`文件的`_start_server_async`方法中,我们在异步函数内直接使用
`server_record.sudo().write()`来更新数据库记录。但是,由于异步函数运行在不同的上下文中,原始的数据库事务已经结束,相关的数据库游标已经被关闭。
问题代码:
```python
# 在异步函数中直接使用server_record
server_record.sudo().write({
'server_url': f'http://127.0.0.1:{port}'
})
```
## 解决方案
使用我们已经实现的安全数据库管理器(`SafeDatabaseManager`)来处理异步环境中的数据库操作。这个管理器会创建新的数据库连接和游标,确保在异步环境中安全地执行数据库操作。
## 修改内容
在`mcp_server/services/fast_mcp_service.py`文件中进行了以下修改:
### 1. 更新服务器URL的修复
```python
# 修改前
server_record.sudo().write({
'server_url': f'http://127.0.0.1:{port}'
})
# 修改后
def update_server_url(env):
server = env['mcp.server'].sudo().browse(server_id)
if server.exists():
server.write({
'server_url': f'http://127.0.0.1:{port}'
})
return True
return False
await self._safe_execute_with_env(update_server_url)
```
### 2. 更新服务器状态的修复
```python
# 修改前
server_record.sudo().write({
'state': 'inactive'
})
# 修改后
def update_server_state(env):
server = env['mcp.server'].sudo().browse(server_id)
if server.exists():
server.write({'state': 'inactive'})
return True
return False
try:
await self._safe_execute_with_env(update_server_state)
except Exception as update_error:
_logger.error("更新服务器状态失败: %s", str(update_error))
```
### 3. 停止服务器状态更新的修复
```python
# 修改前
server_record.sudo().write({
'state': 'inactive'
})
# 修改后
def update_server_state(env):
server = env['mcp.server'].sudo().browse(server_record.id)
if server.exists():
server.write({'state': 'inactive'})
return True
return False
await self._safe_execute_with_env(update_server_state)
```
## 技术原理
### SafeDatabaseManager的工作原理
1. **连接池管理**: 为每个数据库维护独立的连接池
2. **线程安全**: 使用锁机制确保多线程环境下的安全访问
3. **自动资源管理**: 自动处理连接的获取、释放和清理
4. **健康检查**: 在使用连接前检查其健康状态
5. **异常处理**: 完善的异常处理和资源清理机制
### 异步环境中的数据库操作
在异步环境中,我们不能直接使用同步的Odoo记录对象,因为:
1. 原始的数据库事务可能已经结束
2. 数据库游标可能已经被关闭
3. 异步函数运行在不同的线程或事件循环中
通过`_safe_execute_with_env`方法,我们:
1. 创建新的数据库连接
2. 在新的环境中执行数据库操作
3. 自动处理事务提交和回滚
4. 确保资源正确清理
## 验证方法
重新启动Odoo服务器并尝试激活MCP服务器。检查日志确认:
1. FastMCP服务器成功启动
2. 不再出现"Cursor already closed"错误
3. 服务器状态正确更新
## 注意事项
- 在异步函数中避免直接使用Odoo记录对象进行数据库操作
- 使用安全数据库管理器或类似的机制来处理异步环境中的数据库操作
- 确保在异步操作中正确处理异常和资源清理
- 在多线程环境中要特别注意数据库连接的生命周期管理
## 修复日期
2025-07-22