Django LiveServer测试用例、Selenium和Postgres的间歇性死锁

2guxujil  于 2023-01-14  发布在  Go
关注(0)|答案(2)|浏览(143)

在使用LiveServerTestCase和Selenium测试Django/Postgres应用时,我发现了间歇性的死锁问题。LiveServerTestCase继承自TransactionTestCase,所以每次测试运行后,所有DB表都会被截断。但有时候,截断会导致死锁,因为其中一个表被未解决的Postgres事务锁定。

select * from pg_stat_activity 
         where datname='test' and current_query='<IDLE> in transaction';

因此,我的应用程序中的某些活动一定是在创建一个未解决的事务,我已经仔细检查了测试,以确保它们在退出之前等待任何更新完成,并确信不是这样。
查看Postgres日志,我经常看到这两行,但没有对应的COMMITROLLBACK

SHOW default_transaction_isolation
BEGIN

我怀疑这些是造成死锁的原因。你知道是什么发出了这个SQL或者如何禁用它吗?这是Django1.5。

uwopmtnx

uwopmtnx1#

这个死锁的根本原因是Django1.5的自动提交行为。默认情况下Django1.5运行时有一个打开的事务,只有在执行UPDATEINSERT时,它才会被COMMIT关闭。“读取”操作(SELECT)会导致我上面提到的不匹配的BEGIN语句。如果SELECT恰好出现在测试TRUNCATE。为了避免死锁,测试必须在所有请求完成后退出,即使请求没有导致DB写入。如果 AJAX 调用在更新后异步更新页面的部分内容,这可能会很棘手。
一个更好的解决方案是使用Django 1.6,其中atomic()是唯一的(未被弃用的)事务创建原语,它不会为读操作打开事务,也不会留下悬空的BEGIN语句,测试可以遵循常识性的方法,即在“写”请求挂起时不退出。

e5nqia27

e5nqia272#

对于未来的旅行者:我们在Django 3.2 + Postgres 12上遇到了同样的问题。当构建服务器处于高负载下进行多个并行构建时,实时服务器不断接收来自Selenium容器 AJAX 调用,这中断了测试后TRUNCATE,导致死锁。
我们的解决方案是在每个测试用例结束时添加1秒睡眠:

class CustomLiveTestCase(StaticLiveServerTestCase):
    def tearDown(self):
        time.sleep(1)

这给了实时服务器足够的时间在测试完成后处理任何延迟 AJAX 调用,从而消除了死锁。

相关问题