在使用LiveServerTestCase和Selenium测试Django/Postgres应用时,我发现了间歇性的死锁问题。LiveServerTestCase继承自TransactionTestCase,所以每次测试运行后,所有DB表都会被截断。但有时候,截断会导致死锁,因为其中一个表被未解决的Postgres事务锁定。
select * from pg_stat_activity
where datname='test' and current_query='<IDLE> in transaction';
因此,我的应用程序中的某些活动一定是在创建一个未解决的事务,我已经仔细检查了测试,以确保它们在退出之前等待任何更新完成,并确信不是这样。
查看Postgres日志,我经常看到这两行,但没有对应的COMMIT
或ROLLBACK
:
SHOW default_transaction_isolation
BEGIN
我怀疑这些是造成死锁的原因。你知道是什么发出了这个SQL或者如何禁用它吗?这是Django1.5。
2条答案
按热度按时间uwopmtnx1#
这个死锁的根本原因是Django1.5的自动提交行为。默认情况下Django1.5运行时有一个打开的事务,只有在执行
UPDATE
或INSERT
时,它才会被COMMIT
关闭。“读取”操作(SELECT
)会导致我上面提到的不匹配的BEGIN
语句。如果SELECT
恰好出现在测试TRUNCATE
。为了避免死锁,测试必须在所有请求完成后退出,即使请求没有导致DB写入。如果 AJAX 调用在更新后异步更新页面的部分内容,这可能会很棘手。一个更好的解决方案是使用Django 1.6,其中
atomic()
是唯一的(未被弃用的)事务创建原语,它不会为读操作打开事务,也不会留下悬空的BEGIN
语句,测试可以遵循常识性的方法,即在“写”请求挂起时不退出。e5nqia272#
对于未来的旅行者:我们在Django 3.2 + Postgres 12上遇到了同样的问题。当构建服务器处于高负载下进行多个并行构建时,实时服务器不断接收来自Selenium容器 AJAX 调用,这中断了测试后TRUNCATE,导致死锁。
我们的解决方案是在每个测试用例结束时添加1秒睡眠:
这给了实时服务器足够的时间在测试完成后处理任何延迟 AJAX 调用,从而消除了死锁。