SQLAlchemy Scoped Session修改在其他作用域不可见问题排查
问题
测试环境中,db.session与db.read_replica_session是两个独立的SQLAlchemy Scoped Session,使用相同连接字符串连接同一数据库。执行如下代码:
card = db.session.query(Card).first() card.display_name = "RANDOM" db.session.add(card) db.session.commit() card_from_rr = ( db.read_replica_session.query(Card).filter(Card.id == card.id).one() ) assert card_from_rr.display_name == card.display_name assert card_from_rr.id == card.id
执行断言时出现错误:
assert card_from_rr.display_name == card.display_name E AssertionError: assert equals failed
两个会话的连接信息如下:
- db.read_replica_session:
<connection object at 0x11bba23b0; dsn: 'user=postgres password=xxx dbname=test host=localhost port=5444', closed: 0> - db.session:
<connection object at 0x10eba5000; dsn: 'user=postgres password=xxx dbname=test host=localhost port=5444', closed: 0>
请问导致该问题的原因是什么?
原因分析
问题出在SQLAlchemy会话的**本地对象缓存(identity map)**机制:
- 两个会话是完全独立的Scoped Session,各自维护自己的本地缓存,缓存之间完全隔离,不会同步数据。
- 如果
db.read_replica_session在此之前已经查询过这条Card记录,对应的旧对象会被存在它的缓存里。即使主会话db.session提交了修改,读会话不会主动更新缓存,查询时直接返回缓存里的旧对象,导致断言失败。 - 即使是读会话第一次查询,也可能因为数据库事务隔离级别(比如默认的
READ COMMITTED在极端情况下,读会话可能无法立即读取到刚提交的修改),但这种场景下最核心的原因还是独立会话的缓存不共享。
解决建议
- 在查询前调用
db.read_replica_session.expire_all(),清空读会话的本地缓存,强制从数据库重新读取最新数据; - 针对特定对象,查询后调用
db.read_replica_session.refresh(card_from_rr),刷新该对象的属性; - 确保读会话在主会话提交完成后再执行首次查询,避免缓存旧数据。
内容的提问来源于stack exchange,提问作者user3154925
相关产品推荐
相关产品推荐

