Python SQLAlchemy + PostgreSQL 程序卡死
我遇到了一个奇怪的情况。我正在为我的程序写一些测试用例。这个程序可以根据用户的选择在sqllite或postgresql上运行。现在我正在使用unittest来编写我的测试代码。简单来说,我的做法是这样的:
def setUp(self):
"""
Reset the database before each test.
"""
if os.path.exists(root_storage):
shutil.rmtree(root_storage)
reset_database()
initialize_startup()
self.project_service = ProjectService()
self.structure_helper = FilesHelper()
user = model.User("test_user", "test_pass", "test_mail@tvb.org",
True, "user")
self.test_user = dao.store_entity(user)
在设置环境的部分,我会删除任何已经存在的文件夹(这些是之前测试创建的),然后重置我的数据库(基本上是删除所有表),接着重新初始化数据库,并创建一些测试中会用到的服务。
def tearDown(self):
"""
Remove project folders and clean up database.
"""
created_projects = dao.get_projects_for_user(self.test_user.id)
for project in created_projects:
self.structure_helper.remove_project_structure(project.name)
reset_database()
而在清理环境的部分,我做的事情和设置环境的部分差不多,只是不会再创建服务,因为这个测试模块和其他模块在同一个测试套件里,我不想让某些测试留下的东西影响到其他测试。
现在,我的所有测试在sqllite上运行得很好。但是在postgresql上,我遇到了一个非常奇怪的情况:在执行的某个时刻(这个时刻在每次运行时会有一点不同,比如多一两次调用),程序就会停止。我是说没有产生错误,也没有抛出异常,程序就是停在那里了。
我能想到的唯一原因是,我可能在某个地方忘记关闭一个连接,过了一段时间后它超时了,导致了某些事情发生。但我有很多连接,所以在我开始检查所有代码之前,我希望能得到一些建议或意见。
造成这种行为的原因可能是什么?我该从哪里开始查找呢?
祝好,
Bogdan
2 个回答
我找到的一个解决这个问题的方法是,在尝试调用 db.drop_all()
之前,先调用 db.session.close()
。这样可以在删除表之前关闭连接,防止Postgres锁住这些表。
想了解这个问题的更详细讨论,可以点击 这里。
基于PostgreSQL的应用程序可能会出现卡死的情况,因为PG会比较严格地锁定表,特别是如果有任何连接在进行中的事务中访问了该表(包括SELECT查询),它就不会允许DROP命令继续执行。
如果你使用的是unix系统,可以通过命令“ps -ef | grep 'post'”查看所有PostgreSQL的进程,这样你就能看到当前命令的状态,包括你卡住的“DROP TABLE”命令或其他导致卡死的操作。你也可以通过查看pg_stat_activity视图来看到这些信息。
所以关键是要确保没有未完成的事务。这意味着在数据库API层面,任何结果游标都要关闭,任何当前打开的连接都需要调用rollback()
,或者明确关闭它。在SQLAlchemy中,这意味着任何有未处理行的结果集(也就是ResultProxy
)都要完全处理完,并且任何Connection
对象都要被close()
,这样它们就会返回到连接池,并在底层的数据库API连接上调用rollback()
。你需要确保在发出任何DROP TABLE命令之前,有一些无条件的清理代码来确保这些操作完成。
至于“我有很多连接”,你应该控制好这个问题。当SQLAlchemy的测试套件运行3000多个测试时,我们确保对连接进行严格控制,通常一次只打开一个连接(不过,在Pypy上运行时,有些行为仍然会导致与PG的卡死,这很棘手)。你可以使用一个叫AssertionPool
的连接池类,它确保一次只检查出一个连接,否则会抛出一个有用的错误,显示连接是在哪里被检查出的。