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

Posted

技术标签:

【中文标题】线程化的 Django 任务不会自动处理事务或数据库连接?【英文标题】:Threaded Django task doesn't automatically handle transactions or db connections? 【发布时间】:2010-11-21 04:17:23 【问题描述】:

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

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

我切换到手动事务管理并手动进行回滚,这很有效,但仍然让进程处于“空闲”状态。

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

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

【问题讨论】:

【参考方案1】:

经过数周的测试和阅读 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 事务管理正常进行,并通过以下三种方式之一简单地关闭连接:

    编写一个装饰器来关闭连接并在其中包装必要的功能。 挂钩现有的请求信号以让 Django 关闭连接。 在函数结束时手动关闭连接。

这三个中的任何一个都会(并且确实)起作用。

这让我发疯了好几个星期。我希望这对将来的其他人有所帮助!

【讨论】:

很高兴你终于解决了这个问题。这并不明显。我想知道在某个地方的文档中添加注释是否值得一张票。 这张票描述了一个非常相似的问题:code.djangoproject.com/ticket/9964 很好的解释,感谢您的时间和精力,Gabriel!

以上是关于线程化的 Django 任务不会自动处理事务或数据库连接?的主要内容,如果未能解决你的问题,请参考以下文章

跨线程共享 django 事务

Java实现通用线程池

django事务处理

python多线程

python多线程

Django异步任务线程池