You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决Minitest中Postgres死锁及相关数据库错误?

解决Rails Minitest + Postgres测试中的死锁、唯一约束及连接问题

一、处理PostgreSQL死锁

死锁本质是两个测试进程互相持有对方需要的锁,测试量上升后触发概率增高,可从以下方向修复:

  • 统一数据清理顺序:死锁日志显示videos表删除操作与另一表锁冲突,确保所有测试清理数据时遵循相同表顺序(比如按表名字母排序),避免交叉锁等待。可修改测试的teardown逻辑,或调整数据库清理工具的表处理顺序。
  • 先关闭测试并行化:如果开启了Minitest并行测试(parallelize(workers: :number_of_processors)),先禁用并行,确认死锁是否消失。若必须使用并行,需保证每个测试进程用独立连接池,且数据清理操作互不干扰。
  • 避免测试内手动事务操作:Rails测试默认用事务包裹每个测试,不要手动提交测试内的事务,也不要嵌套事务,防止锁被长时间持有。
  • 查看Postgres日志定位根源:按照错误提示的HINT,去Postgres服务器日志里查看两个死锁进程的具体SQL,找到触发冲突的测试代码后针对性修改。

二、解决唯一约束冲突(duplicate key)

该问题是因为Postgres序列不会随事务回滚,测试多次回滚后,序列值与表中实际最大ID不一致导致的:

  • 手动重置表序列:写一个辅助方法在测试前重置所有表的ID序列,放在test_helper.rb里全局调用:
def reset_all_id_sequences
  ActiveRecord::Base.connection.tables.each do |table|
    next if table == 'schema_migrations'
    ActiveRecord::Base.connection.execute(
      "SELECT setval('#{table}_id_seq', COALESCE((SELECT MAX(id) FROM #{table}), 0) + 1);"
    )
  end
end

# 在全局setup里调用
class ActiveSupport::TestCase
  setup do
    reset_all_id_sequences
  end
end
  • 改用truncation清理策略:如果使用database_cleaner,把测试环境的清理策略从:transaction改成:truncation,每次测试后截断表,序列会自动重置:
# test_helper.rb
DatabaseCleaner.strategy = :truncation

注意:truncation比事务清理速度慢,可根据测试规模权衡使用。

三、修复连接关闭错误

该问题多因连接池耗尽或连接超时导致:

  • 增大测试环境连接池:在config/database.yml的test配置里,调高pool值,比如:
test:
  adapter: postgresql
  database: your_app_test
  pool: 20 # 根据并行worker数调整,一般比worker数多3-5个
  • 确保连接正确回收:如果测试中手动获取过数据库连接,记得在测试结束后释放:
ActiveRecord::Base.connection.release_connection
  • 调整Postgres连接超时参数:如果测试运行时间较长,可修改Postgres的idle_in_transaction_session_timeout参数,延长连接超时时间,避免连接被主动关闭。

内容的提问来源于stack exchange,提问作者tolia k

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 11:05:13