【发布时间】:2014-06-11 16:34:05
【问题描述】:
总结
我们在生产中的一个线程遇到了一个错误,现在在每个带有它所服务的查询的请求上都产生InvalidRequestError: This session is in 'prepared' state; no further SQL can be emitted within this transaction. 错误,直到它的余生!它已经这样做了天了,现在!这怎么可能,我们如何防止它继续发展?
背景
我们在 uWSGI(4 个进程,2 个线程)上使用 Flask 应用程序,Flask-SQLAlchemy 为我们提供到 SQL Server 的数据库连接。
问题似乎开始于我们生产中的一个线程在这个 Flask-SQLAlchemy 方法中拆除它的请求时:
@teardown
def shutdown_session(response_or_exc):
if app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN']:
if response_or_exc is None:
self.session.commit()
self.session.remove()
return response_or_exc
...当交易无效时,以某种方式设法调用self.session.commit()。这导致sqlalchemy.exc.InvalidRequestError: Can't reconnect until invalid transaction is rolled back 将输出输出到标准输出,无视我们的日志配置,这是有道理的,因为它发生在应用程序上下文崩溃期间,这绝不应该引发异常。我不确定在没有设置response_or_exec 的情况下如何交易无效,但这实际上是 AFAIK 的较小问题。
更大的问题是,“'准备好的'状态”错误开始了,并且从那以后就没有停止过。每次这个线程处理一个命中数据库的请求时,它都会持续 500 秒。每个其他线程似乎都很好:据我所知,即使是同一进程中的线程也运行良好。
胡乱猜测
SQLAlchemy 邮件列表有一个关于“'prepared' state”错误的条目,指出如果会话开始提交但尚未完成,并且其他东西试图使用它,就会发生这种情况。我的猜测是这个线程中的会话从未到达self.session.remove() 步骤,现在它永远不会。
我仍然觉得这并不能解释此会话如何在跨请求中持续存在。我们没有修改 Flask-SQLAlchemy 对请求范围会话的使用,所以会话应该返回到 SQLAlchemy 的池并在请求结束时回滚,即使是那些出错的(尽管承认,可能不是第一个,因为在应用程序上下文拆除期间提出)。为什么回滚没有发生?如果我们每次都在标准输出(在 uwsgi 的日志中)上看到“无效事务”错误,我可以理解,但我们不是:我只看到过一次,第一次。但是每次发生 500 次时,我都会看到“'prepared' state”错误(在我们的应用程序日志中)。
配置详情
我们已经关闭了session_options 中的expire_on_commit,并且我们已经打开了SQLALCHEMY_COMMIT_ON_TEARDOWN。我们只是从数据库中读取,还没有写入。我们还对所有查询使用 Dogpile-Cache(使用 memcached 锁,因为我们有多个进程,实际上是 2 个负载平衡服务器)。对于我们的主要查询,缓存每分钟都会过期。
2014 年 4 月 28 日更新:解决步骤
重新启动服务器似乎已经解决了问题,这并不完全令人惊讶。也就是说,我希望再次看到它,直到我们弄清楚如何阻止它。 benselme(下)建议编写我们自己的拆解回调,并在提交时处理异常,但我觉得更大的问题是线程在其余生中都被搞砸了。这个没有在一两个请求后消失的事实真的让我很紧张!
【问题讨论】:
-
线程被搞砸了因为
Session不是removed。就个人而言,我建议不要使用 Flask-SQLAlchemy:例如,这个 bug 将很难解决,如果你查看 github repo,你会发现它不再真正被维护了。此外,与普通的 SQLAlchemy 相比,它并没有为您提供更多功能。 -
@benselme:Flask-SQLAlchemy 确实 提供的一件重要的事情是请求范围的会话。当新请求到来时,无论旧会话是否被删除,都应该生成一个新会话。这就是这很奇怪的部分原因:如果有什么我们应该让数据库连接保持打开太久,而不是线程处于永久错误状态。我同意 Flask-SQLAlchemy 的安静程度很奇怪,但它是由 Flask 作者编写的,所以我认为它实际上相当稳定。
-
@benselme,我对 Flask-SQLAlchemy 也有同样的感觉。并编写了我自己的
SQLAlchemy to Flask集成代码。最终,我遇到了与此问题中描述的相同的奇怪行为。
标签: python flask sqlalchemy uwsgi flask-sqlalchemy