我有一个使用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
答案 0 :(得分:2)
我在Debug mode中运行Flask时发现了这种情况。如果您的代码抛出异常并且调试器启动,则事务将永远不会“回滚”或“删除”。因此,在失败的请求上使用的会话永远不会返回到池中。
解决方案是禁用调试模式。
编辑:
还有另一种情况我已经看到过这种情况。如果你有自动运行的代码(即不是HTTP事务的一部分 - 就像在启动Flask应用程序时启动和生成的独立线程一样),它通常会涉及睡眠。如果你在睡眠之前访问会话,那么你将在睡眠期间以这样的挂起事务结束。
另一种可能性是您从创建应用程序功能访问会话。如果你这样做,请确保.remove()它。否则,该会话可能会在gevent应用程序的主线程上保持挂起状态。
答案 1 :(得分:-1)
from sqlalchemy.pool import NullPool
使用NullPoll作为poolclass为我解决了这个问题。不知道为什么。