You need to enable JavaScript to run this app. Async SQLAlchemy中AsyncSession两种使用方式差异及相关报错如何解决
优惠活动
大模型
产品
解决方案
定价
更多

Async SQLAlchemy中AsyncSession两种使用方式差异及相关报错如何解决

两种AsyncSession写法差异及报错解决方案

核心前提

你代码中赋值的AsyncSession是async_scoped_session生成的会话代理对象,而非SQLAlchemy原生的异步会话类。它会根据你指定的current_task scopefunc,为每个异步任务绑定独立的会话实例,同个任务内多次调用AsyncSession()拿到的是同一个实例,不同任务之间会话完全隔离。


一、两种写法的核心区别

  • Foo类写法:混合了「类实例持有会话对象」和「全局代理操作会话」两种模式,操作同一个会话时同时用到了实例属性和全局代理,容易出现上下文不一致问题
  • Bar类写法:仅通过类实例持有的会话对象操作,没有用到代理的全局上下文隔离特性

二、报错原因分析

1. Bar类触发PendingRollbackError的原因

报错的根本原因是你持有的self.async_session之前执行操作时发生过异常(你给出的报错里明确是主键冲突的UniqueViolationError),会话的事务已经被自动回滚,但你没有主动调用rollback()方法重置会话状态,就继续用同一个会话执行新操作,就会触发这个报错。
Bar类写法容易触发该问题的常见诱因:

  • 类实例被跨多个异步任务复用,绑定的会话也被多个任务共享,而AsyncSession本身不是任务安全的,上一个任务执行出错没有处理,下一个任务拿到已失效的会话直接使用
  • 没有做异常捕获逻辑,操作出错后没有回滚会话就继续执行后续逻辑

2. Foo类触发remove() await报错的原因

SQLAlchemy 1.4版本中async_scoped_session.remove()是同步方法,没有返回值,你强行加await调用就会出现NoneType不能被await的报错。


三、对应解决方法

针对Bar类的PendingRollbackError

  • 不要跨异步任务长期持有AsyncSession实例,每个异步任务的独立逻辑块单独从async_scoped_session获取会话,不要存在类实例中全局复用
  • 所有数据库操作增加异常捕获逻辑,发生异常时主动回滚会话:
async def some_function(self, some_object):
    async with AsyncSession() as session:
        try:
            session.add(some_object)
            await session.commit()
        except Exception as e:
            await session.rollback()
            raise e
  • 推荐直接用异步上下文管理器获取会话,执行完逻辑后会自动关闭会话,不需要手动处理

针对Foo类的remove()报错

直接去掉await关键字,将await AsyncSession.remove()改为AsyncSession.remove()即可。同时不推荐混合使用实例持有会话和全局代理操作的写法,全程要么用全局代理操作,要么用上下文管理器拿到的实例操作,避免出现会话状态不一致。


四、推荐实践

  • 不要将AsyncSession作为类属性长期持有,每个异步任务的独立逻辑块单独获取会话
  • 所有数据库操作逻辑必须增加异常捕获,异常场景主动回滚事务
  • 不要混用代理对象和实例对象操作同一个会话,避免出现不可预期的状态问题

内容的提问来源于stack exchange,提问作者Nyxynyx

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.12 19:15:03