1

私のRails3.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 の検査は有害であると見なされる」を参照してください。要するに、しかし:

  1. inspectエラーメッセージをフォーマットするときにNameError が呼び出されます
  2. すべてのインスタンス変数を再帰的inspectに呼び出すデフォルトの実装inspect
  3. NameError65 文字を超えるinspect場合の結果を破棄します
  4. 私たちにとって、これは、View のタイプミスが原因で Rails が20 分間ハングアップし、Ruby が 20MB の巨大な文字列を作成してから破棄したことを意味します。
  5. これをRailsコアに簡単に修正するのに7か月かかりました

要するに、NameError の動作は Ruby インタープリター内の凶悪なバグだと思います。この実装の正当な理由は思いつきません。

于 2012-07-18T17:22:56.227 に答える
1

これは、例外処理コードの副作用です。

2 つのケースで見られる動作を考えてみてください。

  1. インスタンス変数 - コントローラーのインスタンス変数にクエリを割り当てました。次に例外をスローし、その例外処理の一環として、レールはコントローラーで to_s を呼び出します。これにより、デフォルトですべてのインスタンス変数が表示されるため、クエリが強制的に実行されます。

  2. ローカル変数 - コントローラーのローカル変数にクエリを割り当てました。この場合、例外がスローされると、ローカル変数は単に破棄されます。

Ruby コンソールで構造の文字列表現を作成するとコストがかかる、および/またはスパムになる可能性があるオブジェクトでは、常に to_s をオーバーライドすることをお勧めします。

于 2012-07-17T14:32:28.743 に答える