Flask和SQLAlchemy在PostgreSQL中的事务连接中导致大量IDLE

Tho*_*mas 12 python postgresql sqlalchemy flask

我有一个使用SQLAlchemy访问PostgreSQL数据库的Flask Web应用程序.

当我启动应用程序时,会立即在PostgreSQL中创建一个"in transaction"连接.

当应用程序使用一段时间后,其中一些连接出现在pg_stat_activity中.

一段时间后,似乎在某些资源上发生了死锁,我必须重新启动应用程序以使其再次运行.

我已经读过,如果我在关闭数据库会话之前从使用数据库的视图函数返回,则会发生这种情况.所以为了避免这个问题,我创建了以下装饰器:

@app.teardown_appcontext
def shotdown_session(exception=None):
    db.session.remove()
Run Code Online (Sandbox Code Playgroud)

这应该导致在每次请求之后关闭所有会话,并有效地避免出现"在事务中"连接的问题.

不幸的是,它似乎没有任何影响.

那么,我该如何解决这个问题呢?

更新:

我应该补充一点,我已经验证我的装饰器功能实际上已经运行了.我通过添加打印来验证这一点:

@app.teardown_appcontext
def shotdown_session(exception=None):
    print "@app.teardown_appcontext: shotdown_session()"
    db.session.remove()
Run Code Online (Sandbox Code Playgroud)

我还通过向视图函数添加打印来验证它确实在返回视图函数后运行:

[...]
products = db.session.query(...).all()
print "BEFORE RETURN"
return render_template("show_products.html", products=products)
Run Code Online (Sandbox Code Playgroud)

这会产生如下日志行:

 * Running on http://0.0.0.0:5000/
 * Restarting with reloader
BEFORE RETURN
@app.teardown_appcontext: shotdown_session()
10.0.0.100 - - [03/Dec/2014 13:41:30] "GET /product/51 HTTP/1.1" 200 -
Run Code Online (Sandbox Code Playgroud)

我还浏览了代码,并在使用db.session在每个函数中返回之前添加了一个db.session.remove()调用.这确实摆脱了in事务,但是,它也会导致问题.我将SQLAlchemy模型对象从数据库传递给模板.然后,一些模板对这些模型对象执行某些操作,导致应用程序失败,因为该对象不再附加到会话.

编辑2014-12-08:

可以在此处看到连接设置:

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, scoped_session
from flask_sqlalchemy import SQLAlchemy

from config import cfg

engine = create_engine(cfg["db_uri"], echo=False, pool_size=10)
db = SQLAlchemy()
Base = db.Model
Session = scoped_session(sessionmaker(bind=engine))
Run Code Online (Sandbox Code Playgroud)

整个应用程序的结构可以在这里找到:http://paste.yt/p3219.html

Dav*_*ess 5

我见过当你在调试模式下运行 Flask 时会发生这种情况。如果您的代码引发异常并且调试器启动,则事务将永远不会“回滚”或“删除”。因此,失败的请求所使用的会话永远不会返回到池中。

\n\n

解决方案是禁用调试模式。

\n\n

编辑:

\n\n

我还曾在另一种情况下见过这种情况。如果您有自主运行的代码(即不是 HTTP 事务 \xe2\x80\x93\xc2\xa0 的一部分,就像在 Flask 应用程序启动时启动并生成的独立线程),它通常会涉及睡眠。如果您在睡眠之前访问会话,那么您将在睡眠期间遇到这样的挂起事务。

\n\n

另一种可能性是您正在从创建应用程序功能访问会话。如果这样做,请确保 .remove() 它。否则,该会话可能会在 gevent 应用程序的主线程上保持挂起状态。

\n