【问题标题】:ActiveRecord find_or_initialize_by race conditionsActiveRecord find_or_initialize_by 竞争条件
【发布时间】:2016-02-26 17:03:32
【问题描述】:

我有一个场景,其中 2 个 db 连接可能同时运行 Model.find_or_initialize_by(params) 并引发错误:PG::UniqueViolation: ERROR: duplicate key value against unique constraint

我想更新我的代码,以便它可以正常地从中恢复。比如:

record = nil

begin
  record = Model.find_or_initialize_by(params)
rescue ActiveRecord::RecordNotUnique
  record = Model.where(params).first
end

return record

问题是在我的本地机器上没有一个很好/简单的方法来重现它,所以我不确定我的修复是否真的有效。

所以我想我会有点创意,并尝试连续调用 create 2 次(本地)这应该引发然后 PG::UniqueViolation: ERROR,然后我可以从中解救并确保一切都得到妥善处理。

但是我得到这个错误:PG::InFailedSqlTransaction: ERROR: current transaction is aborted, commands ignored until end of transaction block

即使我将所有内容包装在单独的事务块中,我也会收到此错误

record = nil

Model.transaction do
  record = Model.create(params)
end

begin
  Model.transaction do
    record = Model.create(params)
  end
rescue ActiveRecord::RecordNotUnique
end

Model.transaction do
  record = Model.where(params).first
end

return record

我的问题:

  • 优雅地处理我在本文开头提到的竞争条件的正确方法是什么?
  • 如何在本地进行测试?

我想我可能在这里遗漏了一些简单的东西,但是已经晚了,也许我没有想得太清楚。

我正在运行 postgres 9.3 和 rails 4。

EDIT 结果发现 find_or_initialize_by 应该是 find_or_create_by ,而我得到的错误来自后来在执行过程中发生的实际保存调用。 #VeryTiredWhenIWroteThis

【问题讨论】:

标签: postgresql activerecord


【解决方案1】:

这真的发生了吗?

Model.find_or_initialize_by(params)

不应引发“ActiveRecord::RecordNotUnique”错误,因为它不会将任何内容保存到 db。它只是创建一个新的 ActiveRecord。

但是,在第二个 sn-p 中,您正在创建记录。 create(没有 bang)不会抛出由验证引起的异常,但是 ActiveRecord::RecordNotUnique 在createcreate! 重复的情况下总是抛出

如果您正在创建记录,则根本不需要交易。由于 Postgres 符合 ACID,因此这两个操作中只有一个成功,并且如果它响应,那么它的更改将是持久的。 (针对 postgres 的单个语句查询也是一个事务)。所以如果你用find_or_create_by替换你上面的代码几乎没问题

begin
 record = Model.find_or_create_by(params)
rescue ActiveRecord::RecordNotUnique
 record = Model.where(params).first
end

您可以通过简单地尝试连续两次创建相同的记录来测试代码是否正确运行。但是,这不会测试 ActiveRecord::RecordNotUnique 在竞争条件下实际上是否正确抛出。

测试也不是您的应用程序的责任,而且测试它并不容易。您必须在机器上以多线程模式启动 rails,或者针对多进程暂存 rails 实例进行测试。例如,Webrick 一次只处理一个请求。您可以使用 puma 应用程序服务器,但是在 MRI 上没有真正的并发 (GIL)。线程仅在 IO 阻塞时共享 GIL。因为与 Postgres 交谈是 IO,所以我希望有一些并发请求,但可以 100% 确定,最好的测试方案是部署在具有多个工作人员的乘客上,然后使用 jmeter 对服务器运行并发请求。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    • 2018-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多