线程Django任务不会自动处理事务或数据库连接?

时间:2009-08-20 02:26:49

标签: python database django multithreading transactions

我已经让Django设置为在自己的线程中运行一些重复的任务,我注意到他们总是留下未完成的数据库连接进程(pgsql“Idle In Transaction”)。

我查看了Postgres日志,发现交易没有完成(没有ROLLBACK)。我尝试在我的函数上使用各种事务装饰器,没有运气。

我切换到手动事务管理并手动完成了回滚,虽然有效,但仍将进程保留为“空闲”。

然后我调用了connection.close(),一切都很好。

但我想知道,为什么Django的典型事务和连接管理不适用于从主Django线程生成的这些线程任务?

1 个答案:

答案 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事务管理正常进行,并简单地通过以下三种方式之一关闭连接:

  1. 编写一个装饰器,关闭连接并将必要的功能包装在其中。
  2. 挂钩现有的请求信号让Django关闭连接。
  3. 在功能结束时手动关闭连接。
  4. 这三个中的任何一个都会(并且确实)起作用。

    这让我疯狂了几个星期。我希望将来可以帮助别人!