Django单元测试发往Postgres的查询随机活跃未完成问题排查咨询
问题排查解决方案
核心根因推测
你遇到的偶发查询挂起问题90%是单元测试场景下的事务锁等待,核心触发条件如下:
- Django
TestCase默认仅会为default数据库开启事务包裹、测试结束自动回滚,你当前测试类未声明操作的非默认数据库别名,导致操作其他库的事务测试结束后未正常回滚,长期持有表/行锁 - Jenkins/本地并发运行测试时,多个测试事务同时申请同几张表的锁,出现循环等待触发死锁
- 单独跑SQL、生产ETL无问题的原因是这两个场景都不存在未提交的残留事务持有锁,查询可以直接拿到资源执行
排查验证步骤
- 先在查询挂起时登录PostgreSQL执行以下命令,确认是否为锁等待:
SELECT pid, query, state, wait_event_type, wait_event FROM pg_stat_activity WHERE state = 'active' AND query LIKE '%disease_target%';
如果返回结果中wait_event_type为Lock,则可确认是锁等待导致的挂起,再执行以下语句找到阻塞会话:
SELECT blocked_locks.pid AS blocked_pid, blocking_locks.pid AS blocking_pid, a.query AS blocking_query FROM pg_locks blocked_locks JOIN pg_locks blocking_locks ON blocked_locks.locktype = blocking_locks.locktype AND blocked_locks.database = blocking_locks.database AND blocked_locks.relation = blocking_locks.relation AND blocked_locks.pid != blocking_locks.pid JOIN pg_stat_activity a ON a.pid = blocking_locks.pid WHERE NOT blocked_locks.granted;
执行SELECT pg_terminate_backend(阻塞pid);即可临时解除挂起。
修复方案
1. 优先修复测试类的多数据库声明
在你的测试类中显式声明操作的所有数据库别名,让TestCase为所有涉及的数据库开启事务管理,测试结束自动回滚释放锁:
class NoneGroundTruthTest(TestCase): # 新增此行,填入你代码中self.database对应的别名,包含default databases = {'default', '你的自定义数据库别名'} def test_disease_target(self): # 原有测试逻辑不变 ...
2. 显式设置数据库隔离级别
在settings.py的数据库配置中新增隔离级别配置,避免可重复读隔离级别下的长事务持有锁时间过长:
import psycopg2.extensions DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': os.getenv('db_name_ccm'), 'HOST': os.getenv('db_host_ccm'), 'PORT': os.getenv('POSTGRES_PORT'), 'USER': os.getenv('DB_USER'), 'PASSWORD': os.getenv('DB_SECRET_PASSWORD'), # 新增以下配置 'OPTIONS': { 'isolation_level': psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED, } }, }
3. 并发测试优化
如果Jenkins开启了多进程并发测试,可将操作核心表的测试用例单独分组串行执行,或者调低并发数,避免多个测试同时申请锁。
4. 兜底验证方案
如果以上修改后仍有问题,可将TestCase替换为TransactionTestCase验证:
from django.test import TransactionTestCase class NoneGroundTruthTest(TransactionTestCase): databases = {'default', '你的自定义数据库别名'} # 其余逻辑不变
TransactionTestCase不会用事务回滚来重置测试数据,而是跑完测试直接清空表,可完全排除事务残留导致的锁问题,若替换后问题消失则可100%确认根因为事务锁残留。
内容的提问来源于stack exchange,提问作者Alma
相关产品推荐
相关产品推荐

