Threaded Django任务不会自动处理事务或数据库连接?
我已经设置Django在自己的线程中运行一些重复的任务,我注意到他们总是留下未完成的数据库连接进程(pgsql“空闲事务”)。
我查看了Postgres日志,发现交易没有完成(没有ROLLBACK)。 我尝试在我的函数上使用各种事务装饰器,没有运气。
我切换到手动事务pipe理,手动进行回滚,工作,但仍然离开进程“空闲”。
那么我打电话connection.close(),一切都很好。
但是我仍然想知道,为什么Django的典型事务和连接pipe理对于正在从主Django线程产生的这些线程化任务起作用呢?
经过数周的testing和阅读Django源代码,我已经find了我自己的问题的答案:
交易
Django的默认自动提交行为仍然适用于我的线程函数。 但是,它在Django文档中声明:
只要您执行需要写入数据库的操作,Django就会生成INSERT / UPDATE / DELETE语句,然后执行COMMIT。 没有隐式的ROLLBACK。
最后一句话是非常真实的。 它不会发出ROLLBACK命令,除非在Django中设置了脏标志。 由于我的function只做SELECT语句,它从来没有设置脏标志,并没有触发COMMIT。
这违背了PostgreSQL认为事务需要ROLLBACK的事实,因为Django为时区发出了SET命令。 在审查日志时,我抛弃了自己,因为我一直看到这些ROLLBACK语句,并认为Django的事务pipe理是源代码。 事实certificate,这不是,这没关系。
连接
连接pipe理是事情变得棘手的地方。 事实certificate,Django使用signals.request_finished.connect(close_connection)
来closures它通常使用的数据库连接。 由于在Django中通常不会发生任何请求,因此您将此行为视为理所当然。
就我而言,虽然没有要求,因为这项工作是按计划进行的。 没有请求意味着没有信号。 没有信号意味着数据库连接从未closures。
回到事务处理,事实certificate,只要在没有对事务pipe理进行任何更改的情况下发出对connection.close()
的调用,就会发出我一直在寻找的PostgreSQL日志中的ROLLBACK语句。
解
解决的办法是让正常的Django事务pipe理正常进行,并简单地closures连接三种方式之一:
- 编写一个装饰器,closures连接并包装必要的function。
- 挂钩到现有的请求信号让Djangoclosures连接。
- 在function结束时手动closures连接。
这三个中的任何一个都会(并且确实)工作。
这让我疯狂了好几个星期。 我希望这可以帮助别人在未来!