我似乎无法让我的Flask应用程序关闭或重用数据库连接。我正在使用PostgreSQL 9.1.3和
Flask==0.8
Flask-SQLAlchemy==0.16
psycopg2==2.4.5
当我的测试套件运行时,打开的连接数达到20(max_connections
中的postgresql.conf
设置),然后我看到:
OperationalError: (OperationalError) FATAL: sorry, too many clients already
None None
我已经将代码简化为只调用create_all
和drop_all
(但没有发布任何sql,因为没有模型)。
我在日志中看到正在检入的连接:
DEBUG:sqlalchemy.pool.QueuePool:Connection <connection object at 0x101c1dff0; dsn: 'dbname=cx_test host=localhost', closed: 0> checked out from pool
DEBUG:sqlalchemy.pool.QueuePool:Connection <connection object at 0x101c1dff0; dsn: 'dbname=cx_test host=localhost', closed: 0> being returned to pool
WARNING:root:impl <-------- That's the test running
DEBUG:sqlalchemy.pool.QueuePool:Connection <connection object at 0x101c1dff0; dsn: 'dbname=cx_test host=localhost', closed: 0> checked out from pool
DEBUG:sqlalchemy.pool.QueuePool:Connection <connection object at 0x101c1dff0; dsn: 'dbname=cx_test host=localhost', closed: 0> being returned to pool
对于每次测试运行,连接的地址(“xyz”部分的“连接对象”)是不同的。我怀疑这与问题有关,但我不确定如何进一步调查。
下面的代码在新的venv中重现了这个问题:
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from unittest import TestCase
import logging
logging.basicConfig(level=logging.DEBUG)
logging.getLogger('sqlalchemy.pool').setLevel(logging.DEBUG)
logging.getLogger('sqlalchemy.engine').setLevel(logging.DEBUG)
logging.getLogger('sqlalchemy.dialects').setLevel(logging.DEBUG)
logging.getLogger('sqlalchemy.orm').setLevel(logging.DEBUG)
db = SQLAlchemy()
def create_app(config=None):
app = Flask(__name__)
app.config.from_object(config)
db.init_app(app)
return app
class AppTestCase(TestCase):
SQLALCHEMY_DATABASE_URI = "postgresql://localhost/cx_test"
TESTING = True
def create_app(self):
return create_app(self)
def setUp(self):
self.app = self.create_app()
self.client = self.app.test_client()
self._ctx = self.app.test_request_context()
self._ctx.push()
db.create_all()
def tearDown(self):
db.session.remove()
db.drop_all()
self._ctx.pop()
class TestModel(AppTestCase):
def impl(self):
logging.warn("impl")
pass
def test_01(self):
self.impl()
def test_02(self):
self.impl()
def test_03(self):
self.impl()
def test_04(self):
self.impl()
def test_05(self):
self.impl()
def test_06(self):
self.impl()
def test_07(self):
self.impl()
def test_08(self):
self.impl()
def test_09(self):
self.impl()
def test_10(self):
self.impl()
def test_11(self):
self.impl()
def test_12(self):
self.impl()
def test_13(self):
self.impl()
def test_14(self):
self.impl()
def test_15(self):
self.impl()
def test_16(self):
self.impl()
def test_17(self):
self.impl()
def test_18(self):
self.impl()
def test_19(self):
self.impl()
if __name__ == "__main__":
import unittest
unittest.main()
这是我第一次在烧瓶中使用app工厂,我从Flask-SQLAlchemy docs部分复制了这段代码。 Elseware那些文档提到在错误的上下文中使用db会导致连接泄漏 - 也许我在做错了init?
答案 0 :(得分:10)
在阅读了SQLAlchemy文档和一些摆弄db实例之后,我终于得到了解决方案。在db.get_engine(self.app).dispose()
中添加tearDown()
,使其如下所示:
def tearDown(self):
db.session.remove()
db.drop_all()
db.get_engine(self.app).dispose()
self._ctx.pop()
答案 1 :(得分:8)
自从大约一年前提出问题以来,我认为OP必须解决他的问题。但对于那些徘徊在这里的人(像我一样)试图弄清楚到底发生了什么,这是我最好的解释:
正如van所说,问题确实是每个测试调用setUp
和tearDown
的测试用例。虽然连接并没有完全从SQLAlchemy中泄漏,但是由于每个测试都有自己的setUp
,所以创建了应用程序的多个实例:每个应用程序都有自己的数据库连接池,可能不是'在测试结束时重复使用或回收。
换句话说,正在检出连接并将其正确返回到池中,但是此连接将作为空闲连接继续使用,以便在相同中进行将来的交易app(连接池)。
在上面的测试用例中,创建了大约20个连接池(每个连接池由于create / drop_all而具有空闲连接)并占用postgres连接限制。
答案 2 :(得分:1)
您知道在test method
之前和之后都会调用setUp and tearDown
。从您的代码看起来您需要它们以确保空数据库
但是,还有setUpClass and tearDownClass
,每个测试类都调用一次
我相信您可以拆分您当前拥有的代码,并将db-connection
相关部分移至Class
级别,同时将test-method
相关部分保留在需要的位置。
答案 3 :(得分:1)
在最新版本的Flask-SQLAlchemy中,session.remove()
会自动调用app.after_request
。
另请参阅此处的SQLALCHEMY_COMMIT_ON_TEARDOWN
设置:
https://pythonhosted.org/Flask-SQLAlchemy/config.html?highlight=sqlalchemy_commit_on_teardown
这也将自动提交交易。