如何在Sqlalchemy orm会话中正确禁用缓存?

5

我有一个在守护进程中的线程,它循环执行以下查询:

    try:
        newsletter = self.session.query(models.Newsletter).\
               filter(models.Newsletter.status == 'PROCESSING').\
               limit(1).one()
    except sa.orm.exc.NoResultFound:
        self.logger.debug('No PROCESSING newsletters found. Sleeping...')
        self.sleep()
        return
    # (...) more code to do with found newsletter

在IT技术中,sleep方法仅仅是停止当前线程的执行,等待预设时间后返回到主循环。但是我发现,如果我在守护进程运行时将任何简报的状态更改为“处理中”,则什么也不会发生,即查询仍然会引发NoResultFound异常。但是如果我重新启动守护进程,它将找到简报。因此,我认为这个查询的结果必须被缓存了。那么我应该怎么做才能使缓存失效呢?session.expire_all()并不能解决问题。我也可以在每次迭代时创建新的Session()对象,但不知道这样做是否会影响系统资源。


2
你的假设有误。你知道你的数据库是如何序列化这两个事务的吗?你知道这两个事务涉及哪些数据库锁吗?你怎么知道这是SQLAlchemy缓存?同样可能是一个数据库锁,阻止更新直到查询完成,因为查询获取了错误的锁。你有缓存的证据吗?还是猜测的? - S.Lott
我没有任何证据,只是猜测。也许缓存是在其他地方完成的。在这里使用rollback()似乎是有效的。 - zefciu
更可能的是没有缓存,而是锁定。 - S.Lott
我这里也遇到了完全相同的问题,我的结果出现了更新问题,输出结果一直没有变化。奇怪的是,甚至发生在我还没有获取的那些记录上 :/ 而且这还是使用原生的SQLAlchemy的情况下。 - Not Available
这个链接https://dev59.com/SGkv5IYBdhLWcg3w_lvE#15788051可能会有用。 - Jakub M.
4个回答

6
你的代码问题是由于数据库默认使用 可重复读隔离级别,所以查询会返回相同的结果,除非你调用 commit()rollback()(或像 Xeross 建议的那样使用 autocommit=True)或手动更改隔离级别。
是的,SQLAlchemy 确实缓存映射对象(而不是查询结果!),因为 ORM 模式要求每个标识都有单个对象。默认情况下,SQLAlchemy 使用弱标识映射作为缓存,因此当没有引用指向它时,对象会自动从会话中删除。请注意,后续查询将使用新数据更新缓存对象的状态,因此无需担心此缓存。

5

传递echo=True不起作用。我还尝试从sqlalchemy.engine记录器设置日志,但也没有起作用。 - zefciu
你可能需要在整个应用程序中启用日志记录:https://gist.github.com/aphillipo/25ae5456cf1fe7fed1a5 - pip

1
嗯,我已经找到了答案,显然你需要明确地执行session.commit()来更新它,或者你需要通过例如sessionmaker设置autocommit=True来设置会话。
sessionmaker(bind=self.engine, autocommit=True)

然而我还没有测试过session.commit()的方式

所以这似乎不是一个缓存问题,而是事务工作的方式


1

不要使用autocommit=True和expire_on_commit=True

for state in self.identity_map.all_states():
    state.expire(state.dict, self.identity_map._modified)

你可以在查询后加上:db.session.commit()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接