1

在我的 Rails 3.0.11 应用程序中,我们在控制器中有非常简单的代码:

def index
  @record = Record.valid # scope around 80,000 records
  asdfasdfsa # consider this is a typo to raise NameError Exception
end

有趣的是,当遇到拼写错误时,应用程序似乎在引发异常之前首先查询/执行 @record 实例变量。查询花费将近 1 分钟来获取记录。所以在浏览器中,页面在进入异常模板之前会挂起很长时间。

如果我将@record 替换为局部变量“record”,则根本不会发生查询。有谁知道这是怎么回事?

4

3 回答 3

4

正如@Khronos 指出的那样,这是由于错误消息和评估变量造成的,但不是 to_s,而是#inspect.

actiondispatch/middleware/templates/rescues/diagnostic.erb它调用<%=h @exception.message %>以显示错误。快速进入 irb 提供了这个花絮:

class Object ; def inspect; "foo" ; end ; end
 => nil 
a=Exception.new(Object)
 => #<Exception: #<Exception:0x10d8a4108>> 
a.message
 => foo 

所以我认为@exception.message 会在异常上调用inspect,这反过来可能会在控制器上调用inspect。虽然它在检查期间枚举整个对象,但它运行查询,但是当它运行 to_s 时,我认为它只为对象 id 丢弃所有这些。

我仍然有点模糊,但它至少与异常和检查有关。

于 2012-07-18T00:32:41.730 回答
3

有关此问题的详细信息,请参阅我的博客文章Ruby's Inspect Considered Harmful。简而言之:

  1. NameErrorinspect在格式化其错误消息时调用
  2. 递归inspect调用inspect所有实例变量的默认实现
  3. NameError丢弃超过 65 个字符inspect的结果
  4. 对我们来说,这意味着 View 中的拼写错误导致 Rails 挂起20 分钟,而 Ruby 构建了一个巨大的 20MB 字符串,然后将其丢弃
  5. 我们花了7 个月的时间才在 Rails 核心中对此进行了简单的修复

简而言之,我认为 NameError 的行为是 Ruby 解释器中的一个令人发指的错误。我想不出这种实施的合理理由。

于 2012-07-18T17:22:56.227 回答
1

这是异常处理代码的副作用。

想想你在两种情况下看到的行为。

  1. 实例变量 - 您已将查询分配给控制器的实例变量。然后你抛出一个异常,作为异常处理的一部分,rails 将在控制器上调用 to_s,然后强制执行查询,因为它默认显示所有实例变量。

  2. 局部变量 - 您已将查询分配给控制器的局部变量。当在这种情况下抛出异常时,局部变量就会被丢弃。

我发现总是在对象上覆盖 to_s 是一种很好的做法,因为在 ruby​​ 控制台中创建结构的字符串表示可能很昂贵和/或垃圾邮件。

于 2012-07-17T14:32:28.743 回答