从Rails 3.2.12升级到3.2.13时出现极慢的原因是什么?

20

今天早上,我将Rails从3.2.12更新到3.2.13,结果导致我的视图加载出现了较大的延迟。 下面是加载我的主页时的情况:

Rails 3.2.12:
Completed 200 OK in 387ms (Views: 339.0ms | ActiveRecord: 27.1ms)

Rails 3.2.13:
Completed 200 OK in 4416ms (Views: 4361.2ms | ActiveRecord: 28.7ms)

这两个提交之间唯一的区别是Rails版本的不同,当然也导致了许多其他gem的更新...... 这就是Gemfile.lock中的区别:

GEM
   remote: https://rubygems.org/
   specs:
-    actionmailer (3.2.12)
-      actionpack (= 3.2.12)
-      mail (~> 2.4.4)
-    actionpack (3.2.12)
-      activemodel (= 3.2.12)
-      activesupport (= 3.2.12)
+    actionmailer (3.2.13)
+      actionpack (= 3.2.13)
+      mail (~> 2.5.3)
+    actionpack (3.2.13)
+      activemodel (= 3.2.13)
+      activesupport (= 3.2.13)
       builder (~> 3.0.0)
       erubis (~> 2.7.0)
       journey (~> 1.0.4)
@@ -14,19 +14,19 @@ GEM
       rack-cache (~> 1.2)
       rack-test (~> 0.6.1)
       sprockets (~> 2.2.1)
-    activemodel (3.2.12)
-      activesupport (= 3.2.12)
+    activemodel (3.2.13)
+      activesupport (= 3.2.13)
       builder (~> 3.0.0)
-    activerecord (3.2.12)
-      activemodel (= 3.2.12)
-      activesupport (= 3.2.12)
+    activerecord (3.2.13)
+      activemodel (= 3.2.13)
+      activesupport (= 3.2.13)
       arel (~> 3.0.2)
       tzinfo (~> 0.3.29)
-    activeresource (3.2.12)
-      activemodel (= 3.2.12)
-      activesupport (= 3.2.12)
-    activesupport (3.2.12)
-      i18n (~> 0.6)
+    activeresource (3.2.13)
+      activemodel (= 3.2.13)
+      activesupport (= 3.2.13)
+    activesupport (3.2.13)
+      i18n (= 0.6.1)
       multi_json (~> 1.0)
     airbrake (3.1.7)
       activesupport
@@ -129,7 +129,7 @@ GEM
     hashr (0.0.22)
     hike (1.2.1)
     honeypot-captcha (0.0.2)
-    i18n (0.6.4)
+    i18n (0.6.1)
     journey (1.0.4)
     jquery-rails (2.2.0)
       railties (>= 3.0, < 5.0)
@@ -146,7 +146,7 @@ GEM
     kgio (2.8.0)
     listen (0.7.2)
     lumberjack (1.0.2)
-    mail (2.4.4)
+    mail (2.5.3)
       i18n (>= 0.4.0)
       mime-types (~> 1.16)
       treetop (~> 1.4.8)
@@ -155,7 +155,7 @@ GEM
     mime-types (1.21)
     mocha (0.10.5)
       metaclass (~> 0.0.1)
-    multi_json (1.6.1)
+    multi_json (1.7.1)
     mysql2 (0.3.11)
     nested_form (0.3.1)
     net-scp (1.0.4)
@@ -180,14 +180,14 @@ GEM
       rack
     rack-test (0.6.2)
       rack (>= 1.0)
-    rails (3.2.12)
-      actionmailer (= 3.2.12)
-      actionpack (= 3.2.12)
-      activerecord (= 3.2.12)
-      activeresource (= 3.2.12)
-      activesupport (= 3.2.12)
+    rails (3.2.13)
+      actionmailer (= 3.2.13)
+      actionpack (= 3.2.13)
+      activerecord (= 3.2.13)
+      activeresource (= 3.2.13)
+      activesupport (= 3.2.13)
       bundler (~> 1.0)
-      railties (= 3.2.12)
+      railties (= 3.2.13)
     rails_admin (0.4.3)
       bootstrap-sass (~> 2.2)
       builder (~> 3.0)
@@ -202,9 +202,9 @@ GEM
       rails (~> 3.1)
       remotipart (~> 1.0)
       sass-rails (~> 3.1)
-    railties (3.2.12)
-      actionpack (= 3.2.12)
-      activesupport (= 3.2.12)
+    railties (3.2.13)
+      actionpack (= 3.2.13)
+      activesupport (= 3.2.13)
       rack-ssl (~> 1.3.2)
       rake (>= 0.8.7)
       rdoc (~> 3.4)
@@ -212,7 +212,7 @@ GEM
     raindrops (0.10.0)
     rake (10.0.3)
     rb-fsevent (0.9.1)
-    rdoc (3.12.1)
+    rdoc (3.12.2)
       json (~> 1.4)
     remotipart (1.0.2)
     rest-client (1.6.7)
@@ -266,7 +266,7 @@ GEM
       eventmachine (>= 0.12.6)
       rack (>= 1.0.0)
     thor (0.17.0)
-    tilt (1.3.4)
+    tilt (1.3.6)
     tire (0.5.4)
       activemodel (>= 3.0)
       hashr (~> 0.0.19)
@@ -280,7 +280,7 @@ GEM
       actionpack (>= 3.1)
       execjs
       railties (>= 3.1)
-    tzinfo (0.3.35)
+    tzinfo (0.3.37)
     uglifier (1.3.0)
       execjs (>= 0.3.0)
       multi_json (~> 1.0, >= 1.0.2)
@@ -325,7 +325,7 @@ DEPENDENCIES
   nested_form
   newrelic_rpm (~> 3.5.5.38)
   pry
-  rails (= 3.2.12)
+  rails (= 3.2.13)
   rails_admin
   rb-fsevent (= 0.9.1)
   rmagick

除了这两个文件外,没有发生任何变化。

从阅读诊断视图渲染慢的原因我了解到资产管道中的内容可能会使其速度变慢,但是当我在development.rb中更改"config.assets.debug = false"的值时,我没有看到任何区别。

我想我确实有很多需要清理的资产管道,我将在部署到生产环境之前完成这些工作,但我想知道为什么更新Rails后现在突然导致滞后。问题是:是什么导致了这种情况,我能做些什么吗?

如果您需要更多信息,请告诉我。非常感谢任何帮助。

编辑:我还在github.com/rails/rails上创建了一个问题,似乎被视为回归:https://github.com/rails/rails/issues/9803


你尝试过使用 RACK_ENV=PRODUCTION 运行你的服务器吗? - Maz
我已经完成了,但没有什么区别(完成200 OK,耗时4566毫秒(Views:4510.4毫秒 | ActiveRecord:28.8毫秒)),不过谢谢建议。 - Fritzz
更新后我的生产应用程序没有任何差异。 - Intrepidd
有点难说哪些宝石/配置参数可以做到这一点。最好是逐步更新开始。 - sanny Sin
这是针对每个请求还是仅针对第一个请求? - Klaus
每个请求都发生了这件事。 - Fritzz
2个回答

10

我们在使用Discourse时也遇到了同样的问题。我将相关的安全修复内容提取出来,制作了一个猴子补丁可以应用到Rails 3.2应用程序中:

module HTML
  class WhiteListSanitizer
      # Sanitizes a block of css code. Used by #sanitize when it comes across a style attribute
    def sanitize_css(style)
      # disallow urls
      style = style.to_s.gsub(/url\s*\(\s*[^\s)]+?\s*\)\s*/, ' ')

      # gauntlet
      if style !~ /\A([:,;#%.\sa-zA-Z0-9!]|\w-\w|\'[\s\w]+\'|\"[\s\w]+\"|\([\d,\s]+\))*\z/ ||
          style !~ /\A(\s*[-\w]+\s*:\s*[^:;]*(;|$)\s*)*\z/
        return ''
      end

      clean = []
      style.scan(/([-\w]+)\s*:\s*([^:;]*)/) do |prop,val|
        if allowed_css_properties.include?(prop.downcase)
          clean <<  prop + ': ' + val + ';'
        elsif shorthand_css_properties.include?(prop.split('-')[0].downcase)
          unless val.split().any? do |keyword|
            !allowed_css_keywords.include?(keyword) &&
              keyword !~ /\A(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)\z/
          end
            clean << prop + ': ' + val + ';'
          end
        end
      end
      clean.join(' ')
    end
  end
end

module HTML
  class WhiteListSanitizer
    self.protocol_separator = /:|(&#0*58)|(&#x70)|(&#x0*3a)|(%|&#37;)3A/i

    def contains_bad_protocols?(attr_name, value)
      uri_attributes.include?(attr_name) &&
      (value =~ /(^[^\/:]*):|(&#0*58)|(&#x70)|(&#x0*3a)|(%|&#37;)3A/i && !allowed_protocols.include?(value.split(protocol_separator).first.downcase.strip))
    end
  end
end

module ActiveRecord
  class Relation

    def where_values_hash
      equalities = with_default_scope.where_values.grep(Arel::Nodes::Equality).find_all { |node|
        node.left.relation.name == table_name
      }

      Hash[equalities.map { |where| [where.left.name, where.right] }].with_indifferent_access
    end

  end
end

module ActiveRecord
  class PredicateBuilder # :nodoc:
    def self.build_from_hash(engine, attributes, default_table, allow_table_name = true)
      predicates = attributes.map do |column, value|
        table = default_table

        if allow_table_name && value.is_a?(Hash)
          table = Arel::Table.new(column, engine)

          if value.empty?
            '1 = 2'
          else
            build_from_hash(engine, value, table, false)
          end
        else
          column = column.to_s

          if allow_table_name && column.include?('.')
            table_name, column = column.split('.', 2)
            table = Arel::Table.new(table_name, engine)
          end

          attribute = table[column]

          case value
          when ActiveRecord::Relation
            value = value.select(value.klass.arel_table[value.klass.primary_key]) if value.select_values.empty?
            attribute.in(value.arel.ast)
          when Array, ActiveRecord::Associations::CollectionProxy
            values = value.to_a.map {|x| x.is_a?(ActiveRecord::Base) ? x.id : x}
            ranges, values = values.partition {|v| v.is_a?(Range) || v.is_a?(Arel::Relation)}

            array_predicates = ranges.map {|range| attribute.in(range)}

            if values.include?(nil)
              values = values.compact
              if values.empty?
                array_predicates << attribute.eq(nil)
              else
                array_predicates << attribute.in(values.compact).or(attribute.eq(nil))
              end
            else
              array_predicates << attribute.in(values)
            end

            array_predicates.inject {|composite, predicate| composite.or(predicate)}
          when Range, Arel::Relation
            attribute.in(value)
          when ActiveRecord::Base
            attribute.eq(value.id)
          when Class
            # FIXME: I think we need to deprecate this behavior
            attribute.eq(value.name)
          when Integer, ActiveSupport::Duration
            # Arel treats integers as literals, but they should be quoted when compared with strings
            column = engine.connection.schema_cache.columns_hash[table.name][attribute.name.to_s]
            attribute.eq(Arel::Nodes::SqlLiteral.new(engine.connection.quote(value, column)))
          else
            attribute.eq(value)
          end
        end
      end

      predicates.flatten
    end
  end
end

应用了安全补丁并回退到Rails 3.2.13之前,性能恢复到正常水平。我们在预编译资源时也遇到了UTF-8错误,但现在不再出现。似乎在3.2.13补丁中有一系列与安全无关的东西正在破坏程序:(


1
哇,太棒了。我也看到了你在我发布的Rails Github问题上的回复,谢谢。 由于我的应用程序尚未上线,现在我将恢复到3.2.12,并等待最新版本的修复。现在似乎是最明智的做法。谢谢! - Fritzz
我感激得想哭!非常感谢你!从大约5秒的时间缩短到大约500毫秒。 - hrdwdmrbl
我有点担心直接从 Stack Overflow 应用一个 Rails 补丁,但检查过了,非常感谢你,兄弟,你是我的救星! - Mohamed Hafez

8

@fredwu在一个拉请求中修复了这个问题:https://github.com/rails/rails/pull/9820

如果你想立即使用这个修复(而不必等待新版本的Rails发布),你可以通过使用以下代码对AssetsPaths类进行猴子补丁来解决:

module Sprockets
  module Helpers
    module RailsHelper
      class AssetPaths < ::ActionView::AssetPaths
        private
        def rewrite_extension(source, dir, ext)
          source_ext = File.extname(source)[1..-1]
          if !ext || ext == source_ext
            source
          elsif source_ext.blank?
            "#{source}.#{ext}"
          elsif File.exists?(source) || exact_match_present?(source)
            source
          else
            "#{source}.#{ext}"
          end
        end
      end
    end
  end
end

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接