DEBUG.WIKI
IPPeak:从目标地区访问网页,住宅代理、SEO 监测、广告验证
腾讯云;让你的想法跑起来;轻量服务器 · 建站 / Docker / API;查看产品 →
晚安云;香港 CN2 ¥15/月

SQLAlchemy 关闭连接被取消,旧句柄又弄坏了新连接

异步 close 在连接池重置中被取消时,旧连接对象可能仍指向已归还记录。SQLAlchemy 2.1.6 已补清理;使用上下文管理资源并区分句柄关闭与池中连接复用。

await c1.close() 被取消,连接池似乎已经归还了连接,但 c1.closed 仍为假。随后借出的 c2 开始工作,再对旧 c1 调用 invalidate(),新连接也突然报 no active connection。应检查关闭取消后的句柄清理,不要把 c2 的错误直接归因于数据库随机断开。

aiosqlite 与 Python 3.14 的这条关闭路径存在上述问题,影响 SQLAlchemy 2.1.2 起及 2.0.55 起的相关清理路径。关键条件是直接关闭时,连接池 rollback/reset 被取消:记录已经可以再次借出,旧句柄却没能完成解绑,之后还可能操作那条被复用的记录。

先保护连接的所有权边界

同一条连接只应由一个明确的资源拥有者关闭。不要让请求任务、超时任务和后台回收任务各持有一个引用,任意对旧对象执行 close 或 invalidate。尤其不能因为 c1 看起来没关闭,就在 c2 已借出后反复操作 c1 来“补一次清理”。

能使用 engine 的异步上下文时,让正常资源管理路径负责退出:

async with engine.connect() as connection:
    result = await connection.exec_driver_sql("SELECT 1")
    value = result.scalar_one()

engine 是项目已有的 AsyncEngine,示例是只读查询,不替代业务事务提交。显式写入应继续遵守原来的 begin、commit 与 rollback 契约。上下文管理的关闭路径对取消有自己的完成处理,不能据此承诺任何驱动在无限网络故障中都立刻退出。

确实需要直接管理 close 时,把关闭责任和取消清理安排在同一个生命周期内,按驱动与框架既有的有界收尾方式处理。不要吞掉所有取消异常,让外层误以为业务成功;也不要给所有数据库调用统一套无限 shield。

2.1.6 已补齐取消分支清理

SQLAlchemy 2.1.6 对 Connection 关闭增加了嵌套 finally,即使底层关闭被打断,也会清除本地连接引用并禁止重连。池重置的取消分支还会先把旧 fairy 与记录解绑,再处理归还,避免它影响下一次借用。

使用 2.0 分支时,应单独确认目标正式版本包含相同修正,不能只因另一个分支修好就推定自己的版本安全。升级沿原锁文件和环境构建流程进行;同版本号但来源不同的本地修改包也需明确记录。

不要把 engine.dispose() 当成每次取消的常规补救,它会影响池资源范围,未必符合其他并发任务的所有权。已经出现不明连接状态时,按应用既有恢复流程结束相应任务,核对实际事务结果后再重试,不能假定取消等于数据库没有执行。

验收在隔离环境覆盖正常关闭、reset 期间取消、紧接着再次借出,以及旧句柄退出后的行为。确认新借用不再被旧句柄影响,资源计数也正确;只看池中连接数量恢复,并不能证明旧对象已经失去操作权。