Flask-SQLAlchemy会话对象无法看到对数据库的更改?

3
我在PythonAnywhere上有一个网站,使用Flask和Flask-SQLAlchemy连接到MySQL数据库。用户可以使用该网站将任务保存到数据库中的记录表中,然后单独的定时任务(Python程序)检查数据库并处理每个未处理的记录。
我遇到的问题是,定时任务的数据库查询似乎只在第一次运行时找到新记录,但如果我使用网站添加新任务,则仍在运行的定时任务的定期数据库查询(每5秒钟)似乎无法检测到新记录。
您有什么想法关于这里可能发生了什么?
以下是由bash文件运行的代码:
def generate_any_pending_videos():
    unfinished_videos = db.session.query(Video)\
                                  .filter(~Video.status.has(VideoStatus.status.in_(['Error', 'Finished', 'Video deleted'])))\
                                  .order_by(Video.datetime_created)\
                                  .all()
    for video in unfinished_videos:
        try:
            logging.info("Attempting to create video for video %d" % video.id)
            generate_video(video)
        except Exception as e:
            logging.error(str(e))


if __name__ == '__main__':
    while True:
        generate_any_pending_videos()
        time.sleep(5)

你确定服务器的数据库连接已经持久化了更改(即事务已提交),而且你确定定时任务的数据库会话会刷新,而不是操作缓存对象吗? - Jens
@Jens 感谢您的建议。在发布这个问题之前,我确实看到了那个SO问题,但是这里的区别在于这不是单个对象值不刷新的情况;我正在尝试主动查询数据库以获取对象。因此,对于这个问题,我不清楚如何使用session.refresh(object)方法。 - Nathan Wailes
正如@Jens所指出的那样,这似乎是一个缓存会话的问题。您可以尝试在generate_any_pending_videos结束时发出session.expire_all()。这可能不是最优雅的解决方案,但如果它起作用,至少您知道问题所在。 - jorgeh
@jorgeh,我确实尝试过那个方法,但它没有起作用。 - Nathan Wailes
@NathanWailes,请查看 db.session.new (以及 dirtydeleted)属性,以检查对 会话的任何更改。在您的代码中,db.session 是一个崭新的会话,还是它在哪里被创建? - Jens
显示剩余7条评论
4个回答

8

找到解决方法:不知道为什么在查询之前运行db.session.commit()会使新记录出现。

if __name__ == '__main__':
    while True:
        generate_any_pending_videos()
        time.sleep(5)
        db.session.commit()  # For some reason this is needed to be able to detect newly-created videos

嗯... Flask 应该将 SQLAlchemy 会话绑定到请求事务上,当生成响应时,事务应该提交并随之提交 db 会话。如果不是这种情况,我建议检查请求处理和 db 会话处理,以确保数据被正确持久化。 - Jens
如果在非锁定SELECT返回结果时,InnoDB提交了事务,则(默认)隔离级别可重复读将没有任何意义。@MiguelGarcia 在他的回答中是正确的。只要您不在REPEATABLE READ中提交(或回滚)事务,其中的所有查询都将从第一个查询创建的快照中读取,并且对数据库的后续更改将对您的事务/会话保持透明。 - shmee
1
这对我有用!!!应该是正确的答案。 另一种方法是在server.cnf中设置 [mysqld] transaction-isolation=READ-COMMITTED - r3v3r53
1
天啊,你不知道我搜索和尝试解决这个问题的次数有多少。谢谢你的发布! - C. Cooney

6
默认情况下,所有的SqlAlchemy查询都在事务内运行。这就是为什么您在脚本的第一次调用中仅获取到新数据的原因。因为在事务中看到的数据不会改变,这就是ACID中的I,事务是隔离的。 在提交或回滚之后,下一个查询将启动一个新的事务,因此您将从数据库中获得新鲜的数据。 http://docs.sqlalchemy.org/en/latest/orm/session_transaction.html

1
你可以在SQLAlchemy引擎中更改隔离级别:
engine = create_engine(db_path, execution_options={"isolation_level": "READ COMMITTED"})

这是MySQL默认配置的问题。

0
我实现这个的方法是在MySQL配置中设置事务隔离级别。
使用全局范围,事务隔离级别在重新启动MySQL后会被重置。您可以通过终端登录MySql DB来执行此操作。
sudo mysql -u root -p

SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;

或者:

sudo mysql -u root -p

SET GLOBAL transaction_isolation = 'READ-COMMITTED';

您可以使用以下4个范围设置4个事务隔离级别。 您可以在此处检查更多详细信息 https://dev.mysql.com/doc/refman/8.0/en/set-transaction.html


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