Подтвердить что ты не робот

Как отлаживать предварительный компилятор Rails, который невыносимо медленный

Я работаю над проектом Rails 3.2, и в последние месяцы активы немного увеличились, хотя я бы не стал считать проект большим. Активы состоят из JS (без кофе - script) и файлов SASS; у нас довольно много изображений, но с ранних дней они были там меньше, поэтому я не думаю, что они являются существенным фактором. У нас может быть около десятка библиотек, и большинство из них небольшие, самым большим является JQuery UI JS. Развертывание осуществляется через Capistrano, и стало очевидно, что развертывание на этапе было значительно быстрее, чем на производство. Чтобы проиллюстрировать это, избегая факторов, связанных с различными серверами и сетевыми эффектами, я просто выполнил следующие три команды на моем ноутбуке следующим образом:

$ time RAILS_ENV=production bundle exec rake assets:precompile
^Crake aborted!
[Note I aborted this run as I felt it was getting stupidly long...]
real    52m33.656s
user    50m48.993s
sys 1m42.165s

$ time RAILS_ENV=staging bundle exec rake assets:precompile
real    0m41.685s
user    0m38.808s
sys 0m2.803s

$ time RAILS_ENV=development bundle exec rake assets:precompile
real    0m12.157s
user    0m10.567s
sys 0m1.531s

Итак, я остаюсь царапать себе голову. Почему существуют огромные различия между различными средами? Я понимаю разницу между развитием и постановкой, но наши конфигурации для постановки и производства идентичны. (Я должен указать, что компиляция будет завершена примерно через 2 часа!)

В то время как конечный результат ускоряет мой прекомпилятор, я хочу сделать это, поняв, где все время идет и почему существуют такие большие различия между средами Rails. Я видел другие сообщения об использовании разных компрессоров и т.д., Но я не могу найти какую-либо информацию о том, как отлаживать эти задачи рейка, чтобы определить, где расходуется время, и определить, какие настройки могут вызывать такие значительные различия.

Я не знаю, какая дополнительная информация может понадобиться людям, поэтому будет обновляться, если и когда спросят комментарии. ТИА

Обновление: дополнительная информация приведена ниже

config/environments/production.rb и config/environments/staging.rb (они точно такие же):

MyRailsApp::Application.configure do
  # Code is not reloaded between requests
  config.cache_classes = true

  # Full error reports are disabled and caching is turned on
  config.consider_all_requests_local       = false
  config.action_controller.perform_caching = true

  # Disable Rails static asset server (Apache or nginx will already do this)
  config.serve_static_assets = true
  config.static_cache_control = "public, max-age=31536000"
  config.action_controller.asset_host = "//#{MyRailsApp::CONFIG[:cdn]}"

  # Compress JavaScripts and CSS
  config.assets.compress = true

  # Don't fallback to assets pipeline if a precompiled asset is missed
  config.assets.compile = false

  # Generate digests for assets URLs
  config.assets.digest = true

  # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
  # the I18n.default_locale when a translation can not be found)
  config.i18n.fallbacks = true

  # Send deprecation notices to registered listeners
  config.active_support.deprecation = :notify
end

Базовая конфигурация /application.rb:

require File.expand_path('../boot', __FILE__)

require 'rails/all'

if defined?(Bundler)
  # If you precompile assets before deploying to production, use this line
  Bundler.require(*Rails.groups(:assets => %w(development test)))
  # If you want your assets lazily compiled in production, use this line
  # Bundler.require(:default, :assets, Rails.env)
end
module MyRailsApp
  CONFIG = YAML.load_file(File.join(File.dirname(__FILE__), 'config.yml'))[Rails.env]

  class Application < Rails::Application

    # Custom directories with classes and modules you want to be autoloadable.
    config.autoload_paths += %W(#{config.root}/lib)
    config.autoload_paths += %W(#{config.root}/app/workers)

    # Configure the default encoding used in templates for Ruby 1.9.
    config.encoding = "utf-8"

    # Configure sensitive parameters which will be filtered from the log file.
    config.filter_parameters += [:password]

    # Enable the asset pipeline
    config.assets.enabled = true

    # Stop precompile from looking for the database
    config.assets.initialize_on_precompile = false

    # Version of your assets, change this if you want to expire all your assets
    config.assets.version = '1.0'

    # Fix fonts in assets pipeline
    # http://stackoverflow.com/info/6510006/add-a-new-asset-path-in-rails-3-1
    config.assets.paths << Rails.root.join('app','assets','fonts')

    config.middleware.insert 0, 'Rack::Cache', {
      :verbose     => true,
      :metastore   => URI.encode("file:#{Rails.root}/tmp/dragonfly/cache/meta"),
      :entitystore => URI.encode("file:#{Rails.root}/tmp/dragonfly/cache/body")
    } # unless Rails.env.production?  ## uncomment this 'unless' in Rails 3.1,
                                      ## because it already inserts Rack::Cache in production

    config.middleware.insert_after 'Rack::Cache', 'Dragonfly::Middleware', :images

    config.action_mailer.default_url_options = { :host => CONFIG[:email][:host] }
    config.action_mailer.asset_host = 'http://' + CONFIG[:email][:host]
  end
end

Файл Gem:

source 'http://rubygems.org'

gem 'rails', '3.2.13'   
gem 'mysql2'
gem 'dragonfly', '>= 0.9.14'
gem 'rack-cache', :require => 'rack/cache'
gem 'will_paginate'
gem 'dynamic_form'
gem 'amazon_product' # for looking up Amazon ASIN codes of books
gem 'geoip'
gem 'mobile-fu'
gem 'airbrake'
gem 'newrelic_rpm'
gem 'bartt-ssl_requirement', '~>1.4.0', :require => 'ssl_requirement'
gem 'dalli' # memcache for api_cache
gem 'api_cache'
gem 'daemons'
gem 'delayed_job_active_record'
gem 'attr_encrypted'
gem 'rest-client'
gem 'json', '>= 1.7.7'
gem 'carrierwave' # simplify file uploads
gem 'net-scp'

# Gems used only for assets and not required
# in production environments by default.
group :assets do
  gem 'therubyracer'
  gem 'sass-rails',   '~> 3.2.3'
  gem 'compass', '~> 0.12.alpha'
  gem 'uglifier', '>= 1.0.3'
  gem 'jquery-fileupload-rails'
end

gem 'jquery-rails'
gem 'api_bee', :git => 'git://github.com/ismasan/ApiBee.git', :ref => '3cff959fea5963cf46b3d5730d68927cebcc59a8'
gem 'httparty', '>= 0.10.2'
gem 'twitter'

# Auth providers
gem 'omniauth-facebook'
gem 'omniauth-twitter'
gem 'omniauth-google-oauth2'
gem 'omniauth-identity'
gem 'omniauth-readmill'
gem 'bcrypt-ruby', "~> 3.0.0" # required for omniauth-identity
gem 'mail_view'

# To use ActiveModel has_secure_password
# gem 'bcrypt-ruby', '~> 3.0.0'

# Deploy with Capistrano
group :development do
  gem 'capistrano'
  gem 'capistrano-ext'
  gem 'capistrano_colors'
  gem 'rvm-capistrano'

  # requirement for Hoof, Linux equivalent of Pow
  gem 'unicorn'
end

group :test, :development do  
  gem 'rspec-rails'
  gem 'pry'
  gem 'pry-rails'
end

group :test do
  gem 'factory_girl_rails'
  gem 'capybara'
  gem 'cucumber-rails'
  gem 'database_cleaner'
  gem 'launchy'
  gem 'ruby-debug19'
  # Pretty printed test output
  gem 'shoulda-matchers'
  gem 'simplecov', :require => false
  gem 'email_spec'
  gem 'show_me_the_cookies'
  gem 'vcr'
  gem 'webmock', '1.6'
end
4b9b3361

Ответ 1

Это может не полностью ответить на ваш вопрос, но я считаю, что это достаточно приличный старт. Как вы увидите, точный ответ будет зависеть от конкретного приложения, версии gem и т.д.

Итак. Для работы с ресурсами, как вы знаете, Rails использует библиотеку под названием Sprockets, которая, как мне кажется, в новых версиях Rails подключается к Rails как к Railtie. Он инициализирует "среду" Sprockets, которая может делать такие вещи, как просмотр манифеста вашего ресурса, загрузка этих файлов, сжатие их, предоставление скомпилированных активов разумных имен и т.д.

По умолчанию этот Sprockets::Environment записывает свою активность в STDERR с уровнем журнала FATAL, что не очень полезно в этих ситуациях. К счастью, Sprockets::Environment (как 2.2.2) имеет записываемый атрибут logger, который вы можете встраивать через Rails с помощью инициализатора.


Итак, вот что я предлагаю, чтобы начать:

В config/initializers создайте файл, что-то вроде asset_logging.rb. В нем поставьте:

Rails.application.assets.logger = Logger.new($stdout)

Это перезаписывает регистратор по умолчанию с тем, который выплевывает больше информации на STDOUT. После того, как вы настроили эту настройку, запустите задачу предварительной компиляции вашего ресурса:

rake RAILS_ENV=production assets:precompile

И вы должны увидеть чуть более интересный вывод, например:

...
Compiled jquery.ui.core.js  (0ms)  (pid 66524)
Compiled jquery.ui.widget.js  (0ms)  (pid 66524)
Compiled jquery.ui.accordion.js  (10ms)  (pid 66524)
...

Но, в конце концов, окончательный ответ будет зависеть от:

  • как "глубоко" вы хотите пойти с записью этого актива.
  • какая конкретная версия Rails, звездочек и т.д. вы используете
  • и то, что вы найдете на этом пути

Как вы уже узнали, обработка журналов на уровне задач Rake или даже на уровне Rails не дает много информации. И даже создание Sprockets само по себе (см. Выше) не говорит вам слишком сильно.

Если вы хотите глубже, чем Sprockets, вы, вероятно, можете обезвредить различные двигатели и процессоры, которые Sprockets покорно объединяются, чтобы обеспечить работу конвейера. Например, вы можете изучить возможности ведения журнала этих компонентов:

  • Sass::Engine (преобразует SASS в CSS)
  • Uglifier (обертка компрессора JavaScript)
  • ExecJS (запускает JavaScript в Ruby, зависимость как Sprockets, так и Uglifier) ​​
  • therubyracer (V8, встроенный в Ruby, используемый ExecJS)
  • и др.

Но я оставлю все это "упражнением для читателя". Если есть серебряная пуля, я бы, конечно, хотел бы узнать об этом!

Ответ 2

существует куча возможных причин этой проблемы.

для возможной причины, я хотел бы знать, как время компиляции активов увеличилось в нескольких средах для ваших последних развертываний. это может указывать на то, что проблема только в средах или в самой компиляции активов. вы можете использовать git bisect для этого. Обычно я использую приложения для развертывания через jenkins или другую систему ci, чтобы я мог видеть любые изменения времени развертывания и когда они были введены.

он может сводиться к широкому использованию ресурсов CPU, MEMORY (любой обмен?), IO. если вы скомпилируете активы в производственных системах, они могут быть заняты службой запроса приложений. перейдите в свою систему, сделайте top для ресурсов, возможно, слишком много дескрипторов файлов одновременно (lsof подходит для этого).

возможно, что вы загружаете или кешируете некоторые данные для своего приложения. базы данных, как правило, намного больше в промежуточных и производственных средах, тогда они находятся в dev-блоках. вы можете просто поместить несколько вызовов Rails.logger в свои инициализаторы или whaterver.

Ответ 3

Я думаю, вам нужно увидеть параметры использования процессора на вашем сервере Prod.

Кроме того, может быть вероятность того, что активы будут прекомпилироваться несколько раз. Я бы предложил создать каталог ресурсов в общем каталоге, созданный capistrano, скопировать ваши изменения в том же и связать его со своими приложениями при развертывании.

Вот как я это делаю,

  after "deploy:update_code" do
    run "export RAILS_ENV=production"
    run "ln -nfs #{shared_path}/public/assets #{release_path}/public/assets"
    # Also for logs and temp section.
    # run "ln -nfs #{shared_path}/log #{release_path}/log"
    # run "ln -nfs #{shared_path}/tmp #{release_path}/tmp"
    #sudo "chmod -R 0777 #{release_path}/tmp/"
    #sudo "chmod -R 0777 #{release_path}/log/"
  end