我已经让Django设置为在自己的线程中运行一些重复的任务,我注意到他们总是留下未完成的数据库连接进程(pgsql“Idle In Transaction”)。
我查看了Postgres日志,发现交易没有完成(没有ROLLBACK)。我尝试在我的函数上使用各种事务装饰器,没有运气。
我切换到手动事务管理并手动完成了回滚,虽然有效,但仍将进程保留为“空闲”。
然后我调用了connection.close(),一切都很好。
但我想知道,为什么Django的典型事务和连接管理不适用于从主Django线程生成的这些线程任务?
答案 0 :(得分:100)
经过数周的测试和阅读Django源代码,我找到了自己问题的答案:
<强>交易强>
对于我的线程函数,Django的默认自动提交行为仍然适用。但是,它在Django文档中声明:
只要执行需要写入数据库的操作,Django就会生成INSERT / UPDATE / DELETE语句,然后执行COMMIT。没有隐含的ROLLBACK。
最后一句非常字面。除非Django中的某些内容设置了脏标志,否则它不会发出ROLLBACK命令。由于我的函数只执行SELECT语句,因此它永远不会设置脏标志,也不会触发COMMIT。
这与PostgreSQL认为事务需要ROLLBACK的事实相反,因为Django为时区发出了SET命令。在查看日志时,我全神贯注,因为我一直看到这些ROLLBACK语句,并假设Django的事务管理是源。事实证明它不是,那没关系。
<强>连接强>
连接管理是事情变得棘手的地方。事实证明Django使用signals.request_finished.connect(close_connection)
来关闭它通常使用的数据库连接。由于Django中通常不会发生任何涉及请求的内容,因此您将此行为视为理所当然。
在我的情况下,没有请求,因为工作已安排。没有请求意味着没有信号。没有信号意味着数据库连接从未关闭。
回到事务,事实证明,在没有对事务管理进行任何更改的情况下简单地发出对connection.close()
的调用会在PostgreSQL日志中发出我一直在寻找的ROLLBACK语句。
<强>解决方案强>
解决方案是允许正常的Django事务管理正常进行,并简单地通过以下三种方式之一关闭连接:
这三个中的任何一个都会(并且确实)起作用。
这让我疯狂了几个星期。我希望将来可以帮助别人!