Как отлаживать предварительный компилятор 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
Ответы
Ответ 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