Ruby Rails 中的 Heisenbug 错误
Heisenbug Error in Ruby Rails
我称此为 Heisenbug 错误,因为该错误仅在未观察到时才存在。这是电话:
@selected_members = Member.where(id: params[:member_ids])
@existing_members = list.members
@new_members = @selected_members.where('id not in (?)', @existing_members.map(&:id))
@members_to_lose = @existing_members.where('enterprise_members.id not in (?)', @selected_members.map(&:id))
Rails.logger.info @members_to_lose.length # Remove this line and it no longer works
list.members = @selected_members
render json: {members_to_add: @new_members, members_to_lose: @members_to_lose}
没有任何异常。
如果我删除了 Rails.logger.info
调用,那么 @members_to_lose
return 就会出错(如 return 中的空白)。
如果我将 Logger 或 Debugger 放在 list.members = @selected_members
行之后,然后 @members_to_lose
是清空,并且 returns 不正确(如 returns 中的空白)。
如果我只是在 list.members
行之前放置一个 debugger
而不是 Rails.logger.info
,那么它将正确地 return。
这里发生了什么?这是 ruby 竞争条件吗?我的代码中 没有任何东西 会影响此代码。 List.members
是一个简单的 has_many
关系。
Rails 查询是延迟评估的 - 它们会在您真正需要它们时执行。
Rails.logger.info @members_to_lose.length
这会触发查询,因为您调用了长度方法。
当您跳过此行时,查询将在以下位置执行:
render json: {members_to_add: @new_members, members_to_lose: @members_to_lose}
此时您已保存新成员。
要执行查询添加负载:
@members_to_lose = @existing_members.where('enterprise_members.id not in (?)', @selected_members.map(&:id)).load
我还建议使用 select 而不是 map 以获得更好的性能:
@members_to_lose = @existing_members.where('enterprise_members.id not in (?)', @selected_members.select('enterprise_members.id')).load
这是因为 where
正在延迟加载数据。
当您这样做时,Rails.logger.info @members_to_lose.length
它基本上会加载数据。
如果你不这样做并触发最后一行,它基本上是在复制对象而不实际评估它。因此你没有得到任何结果。
我称此为 Heisenbug 错误,因为该错误仅在未观察到时才存在。这是电话:
@selected_members = Member.where(id: params[:member_ids])
@existing_members = list.members
@new_members = @selected_members.where('id not in (?)', @existing_members.map(&:id))
@members_to_lose = @existing_members.where('enterprise_members.id not in (?)', @selected_members.map(&:id))
Rails.logger.info @members_to_lose.length # Remove this line and it no longer works
list.members = @selected_members
render json: {members_to_add: @new_members, members_to_lose: @members_to_lose}
没有任何异常。
如果我删除了 Rails.logger.info
调用,那么 @members_to_lose
return 就会出错(如 return 中的空白)。
如果我将 Logger 或 Debugger 放在 list.members = @selected_members
行之后,然后 @members_to_lose
是清空,并且 returns 不正确(如 returns 中的空白)。
如果我只是在 list.members
行之前放置一个 debugger
而不是 Rails.logger.info
,那么它将正确地 return。
这里发生了什么?这是 ruby 竞争条件吗?我的代码中 没有任何东西 会影响此代码。 List.members
是一个简单的 has_many
关系。
Rails 查询是延迟评估的 - 它们会在您真正需要它们时执行。
Rails.logger.info @members_to_lose.length
这会触发查询,因为您调用了长度方法。 当您跳过此行时,查询将在以下位置执行:
render json: {members_to_add: @new_members, members_to_lose: @members_to_lose}
此时您已保存新成员。
要执行查询添加负载:
@members_to_lose = @existing_members.where('enterprise_members.id not in (?)', @selected_members.map(&:id)).load
我还建议使用 select 而不是 map 以获得更好的性能:
@members_to_lose = @existing_members.where('enterprise_members.id not in (?)', @selected_members.select('enterprise_members.id')).load
这是因为 where
正在延迟加载数据。
当您这样做时,Rails.logger.info @members_to_lose.length
它基本上会加载数据。
如果你不这样做并触发最后一行,它基本上是在复制对象而不实际评估它。因此你没有得到任何结果。