【问题标题】:How to test PG::QueryCanceled (due to timeout) error in a Rails app?如何在 Rails 应用程序中测试 PG::QueryCanceled(由于超时)错误?
【发布时间】:2023-03-09 18:02:02
【问题描述】:

在生产服务器上出现此错误

ActiveRecord::StatementInvalid: PG::QueryCanceled: ERROR: canceling statement due to statement timeout <SQL query here>

在这一行:

Contact.where(id: contact_ids_to_delete).delete_all

SQL 查询是带有大量 id 的 DELETE 命令。它超时了。 我想出了一个解决方案,就是批量删除联系人:

Contact.where(id: contact_ids_to_delete).in_batches.delete_all

问题是,如何测试我的解决方案?或者测试它的常用方法是什么?或者是否有任何可以方便测试的宝石?
我看到了两种可能的测试方法:
1.(动态)将测试数据库中的超时设置为少量秒并创建一个测试,在该测试中我生成大量联系人,然后尝试运行我的代码以删除它们。
这似乎是正确的方法,但它可能会减慢测试的执行速度,并且动态设置超时(这将是理想的方法)可能会很棘手。
2. 测试是否批量删除。
这可能很棘手,因为这样我就必须监控查询。

【问题讨论】:

  • 我认为你只是在使用 Rails 的方法,因此不应该测试它们。特别是因为没有一个方法真正修复或避免异常。 in_batches 只会降低异常发生的可能性。
  • 很公平。然而,不通过测试覆盖生产错误是一种不好的做法。现在我认为测试它是做一个胖测试(它只在所有其他测试成功后运行),它在数据大小的意义上模仿生产错误(在 CI 期间,理想情况下应该在与 prod 机器具有相似特征的机器上运行),如果可能的话。

标签: ruby-on-rails ruby postgresql testing rails-activerecord


【解决方案1】:

这不是我要测试的边缘情况,因为它需要构建和运行超过数据库内置超时的查询;这个单一测试的最短运行时间至少是那个时间。

即使这样,您也可以为此编写一个测试,该测试在您的 test 环境中 100% 通过,但在 production 中 100% 失败,因为您永远无法完全复制的两个环境之间的差异;一方面,您的测试数据库被单个并发用户使用,而您的生产数据库将有多个并发用户、不同的可用资源和不同的活动锁。这不是您编写测试的问题类型,因为测试不能确保它不会在生产中发生。最佳实践将做到这一点。

我建议您follow the best practices for Rails 使用find_in_batchesfind_each 方法,期望数据库服务器一次可以成功处理1000 条记录:

Contact.where(id: contact_ids_to_delete).find_in_batches do |contacts|
  contacts.delete_all
end

或者,如果您愿意:

Contact.where(id: contact_ids_to_delete).find_in_batches(&:delete_all)

如果您对自己的生产数据库服务器无法一次处理 1000 条记录感到偏执,可以使用 batch_size 调整批量大小:

Contact.where(id: contact_ids_to_delete).find_in_batches(batch_size: 500) { |contacts| contacts.delete_all }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-21
    相关资源
    最近更新 更多