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

时间:2014-12-03 12:13:07

标签: python postgresql flask sqlalchemy

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

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

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

一段时间后,某些资源上似乎出现了死锁,我必须重新启动应用程序才能让它再次运行。

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

@app.teardown_appcontext
def shotdown_session(exception=None):
    db.session.remove()

这会导致所有会话在每次请求后关闭,并有效避免“在事务中”连接的问题。

不幸的是,它似乎没有任何效果。

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

更新

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

@app.teardown_appcontext
def shotdown_session(exception=None):
    print "@app.teardown_appcontext: shotdown_session()"
    db.session.remove()

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

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

这会产生如下的日志行:

 * 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 -

我还浏览了代码,并在使用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))

整个申请表的结构可以在这里找到:http://paste.yt/p3219.html

2 个答案:

答案 0 :(得分:2)

我在Debug mode中运行Flask时发现了这种情况。如果您的代码抛出异常并且调试器启动,则事务将永远不会“回滚”或“删除”。因此,在失败的请求上使用的会话永远不会返回到池中。

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

编辑:

还有另一种情况我已经看到过这种情况。如果你有自动运行的代码(即不是HTTP事务的一部分 - 就像在启动Flask应用程序时启动和生成的独立线程一样),它通常会涉及睡眠。如果你在睡眠之前访问会话,那么你将在睡眠期间以这样的挂起事务结束。

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

答案 1 :(得分:-1)

from sqlalchemy.pool import NullPool

使用NullPoll作为poolclass为我解决了这个问题。不知道为什么。