Problem

I recently moved on to a previous rails app which was initialized almost 8 months ago. I can’t remember when it begins that the startup of this app takes almost a minute in development mode.

It’s been OK as rails has auto reload in development mode. So I just left it alone.

However, things changed quickly while I started working on something that requires change of initializers constantly and then leads to a server restart.

Suddenly that 1 minute startup time became unbearable and it’s time to correct it.

Actions

Slow gem?

Loading gems is the first step of booting a rails app.

Bumbler is a tool to track the load progress of a Bundler-based projects.

Install it and execute bumbler in rails root directory.

Slow requires:
    118.31  prawn
    154.96  twitter
    185.61  delayed_job_active_record
    212.49  grape
    216.75  compass
    218.28  devise
    291.53  bootstrap-sass
    292.53  cancancan
    314.34  pry
    332.67  capybara
    346.04  rails
    393.05  activeadmin
    583.57  fog
    669.51  mailboxer

None of these gems takes over a second to load. So the problem wasn’t a slow gem.

Slow initializer?!

The next step of booting a rails app is loading initializers. Bumbler provides an easy command bumbler --initializers to see how slow your app’s initializers are.

And this is the result I got.

Slow requires:
  44058.77  :set_routes_reloader_hook

That’s it. A initializer takes 44 seconds to load which is definitly unnormal.

Then do a grep to locate the initilizer.

railties-4.1.10/lib/rails/application/finisher.rb-66-
railties-4.1.10/lib/rails/application/finisher.rb-67-      # Set routes reload after the finisher hook to ensure routes added in
railties-4.1.10/lib/rails/application/finisher.rb-68-      # the hook are taken into account.
railties-4.1.10/lib/rails/application/finisher.rb:69:      initializer :set_routes_reloader_hook do
railties-4.1.10/lib/rails/application/finisher.rb-70-        reloader = routes_reloader
railties-4.1.10/lib/rails/application/finisher.rb-71-        reloader.execute_if_updated
railties-4.1.10/lib/rails/application/finisher.rb-72-        self.reloaders << reloader

What’s next?

Next I try to use Pry and pry-byebug to find out the exact slow function call.

The methodology is simple:

  1. set a break point

  2. use next to find the slow method in current frame

  3. step into the slow method

  4. repeat step 1-3

However quickly, after ten minutes…, I realized that rails is a very complex system and what I was doing may goes forever.

What’s the real next - profiling and benchmark

What I need at this moment is ruby-prof.

bundle open railties, in lib/rails/application/finisher.rb, add code:

  initializer :set_routes_reloader_hook do
    reloader = routes_reloader
    puts 'start prof'
    RubyProf.start
    reloader.execute_if_updated
    results = RubyProf.stop
    puts 'end prof'
    File.open "#{Rails.root}/tmp/rubyprof/stack_#{Time.now.to_s(:db)}.html", 'w' do |file|
      RubyProf::CallStackPrinter.new(results).print(file)
    end
    ...
  end

Execute rails s, then I got a call stack tree graph in html format.

From the graph I can see there are a lot of calls related to Psych, which is a YAML parser. And it all starts from ‘I18n::Backend::Base#load_file’.

Click on the method name, it will open the file in text editor.

To know what files are loaded and how long it takes to load each file, add benchmark code:

  def load_file(filename)
    puts "#{filename}"
    result = Benchmark.ms do
      type = File.extname(filename).tr('.', '').downcase
      raise UnknownFileType.new(type, filename) unless respond_to?(:"load_#{type}", true)
      data = send(:"load_#{type}", filename)
      unless data.is_a?(Hash)
        raise InvalidLocaleData.new(filename, 'expects it to return a hash, but does not')
      end
      data.each { |locale, d| store_translations(locale, d || {}) }
    end
    puts result
  end

Execute rails s:

/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/activesupport-4.1.10/lib/active_support/locale/en.yml
741.9292960548773
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/activemodel-4.1.10/lib/active_model/locale/en.yml
212.11264096200466
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/activerecord-4.1.10/lib/active_record/locale/en.yml
71.97770604398102
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/actionview-4.1.10/lib/action_view/locale/en.yml
394.5407139835879
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/grape-0.11.0/lib/grape/locale/en.yml
354.5366879552603
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/faker-1.4.3/lib/locales/de-AT.yml
16643.352392013185
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/faker-1.4.3/lib/locales/de-CH.yml
309.91789093241096
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/faker-1.4.3/lib/locales/de.yml
21681.582821998745
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/faker-1.4.3/lib/locales/en-au-ocker.yml
1337.0081200264394
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/faker-1.4.3/lib/locales/en-AU.yml
2913.1630359916016
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/faker-1.4.3/lib/locales/en-BORK.yml
507.7939030015841
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/faker-1.4.3/lib/locales/en-CA.yml
429.1196570266038
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/faker-1.4.3/lib/locales/en-GB.yml
584.9791240179911
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/faker-1.4.3/lib/locales/en-IND.yml
4618.128058034927
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/faker-1.4.3/lib/locales/en-NEP.yml
967.6744639873505
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/faker-1.4.3/lib/locales/en-US.yml
3325.105574913323
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/faker-1.4.3/lib/locales/en.yml
32816.76925998181
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/faker-1.4.3/lib/locales/es.yml
9538.302210974507
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/faker-1.4.3/lib/locales/fa.yml
4400.56179696694
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/faker-1.4.3/lib/locales/fr.yml
10188.019018038176
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/faker-1.4.3/lib/locales/it.yml
6565.096735022962
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/faker-1.4.3/lib/locales/ja.yml
885.8637060038745
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/faker-1.4.3/lib/locales/ko.yml
1651.7194519983605
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/faker-1.4.3/lib/locales/nb-NO.yml
2635.0025959545746
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/faker-1.4.3/lib/locales/nl.yml
7878.697662032209
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/faker-1.4.3/lib/locales/pl.yml
19974.775120965205
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/faker-1.4.3/lib/locales/pt-BR.yml
3545.6651960266754
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/faker-1.4.3/lib/locales/ru.yml
6768.859902047552
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/faker-1.4.3/lib/locales/sk.yml
23113.826932036318
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/faker-1.4.3/lib/locales/sv.yml
3274.18546192348
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/faker-1.4.3/lib/locales/vi.yml
2200.5813339492306
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/faker-1.4.3/lib/locales/zh-CN.yml
2095.091123948805
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/ransack-1.6.6/lib/ransack/locale/cs.yml
656.753116985783
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/ransack-1.6.6/lib/ransack/locale/en.yml
741.558330017142
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/ransack-1.6.6/lib/ransack/locale/es.yml
623.7398190423846
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/ransack-1.6.6/lib/ransack/locale/fr.yml
600.3050709841773
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/ransack-1.6.6/lib/ransack/locale/hu.yml
629.7791349934414
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/ransack-1.6.6/lib/ransack/locale/nl.yml
643.7799989944324
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/ransack-1.6.6/lib/ransack/locale/ro.yml
665.7998530426994
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/ransack-1.6.6/lib/ransack/locale/zh.yml
608.7534320540726
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/responders-1.1.2/lib/responders/locales/en.yml
67.63134803622961
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/carrierwave-0.10.0/lib/carrierwave/locale/en.yml
88.2780869724229
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/paperclip-4.2.0/lib/paperclip/locales/de.yml
125.07579394150525
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/paperclip-4.2.0/lib/paperclip/locales/en.yml
132.61232199147344
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/paperclip-4.2.0/lib/paperclip/locales/es.yml
123.02663701120764
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/will_paginate-3.0.7/lib/will_paginate/locale/en.yml
119.39483799505979
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/devise-3.3.0/config/locales/en.yml
477.60191606357694
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/mailboxer-0.12.3/config/locales/en.yml
44.32142700534314
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/kaminari-0.16.3/config/locales/kaminari.yml
140.27521200478077
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/formtastic_i18n-0.4.1/config/locales/ca.yml
84.62769794277847
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/formtastic_i18n-0.4.1/config/locales/de.yml
83.09465704951435
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/formtastic_i18n-0.4.1/config/locales/es.yml
75.08417509961873
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/formtastic_i18n-0.4.1/config/locales/it.yml
90.2807789389044
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/formtastic_i18n-0.4.1/config/locales/pl.yml
37.82614099327475
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/formtastic_i18n-0.4.1/config/locales/pt-BR.yml
85.14734904747456
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/formtastic_i18n-0.4.1/config/locales/pt.yml
85.03939106594771
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/formtastic_i18n-0.4.1/config/locales/tr.yml
79.45211604237556
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/formtastic_i18n-0.4.1/config/locales/zh-CN.yml
85.1048119366169
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/ar.yml
1145.8351940382272
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/bg.yml
940.2479939162731
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/bs.yml
1046.421576058492
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/ca.yml
911.3963799318299
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/cs.yml
918.0804250063375
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/da.yml
879.6392700169235
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/de-CH.yml
897.9943880112842
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/de.yml
1122.114620055072
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/el.yml
1136.2147459294647
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/en-GB.yml
798.7757149385288
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/en.yml
1092.8969079395756
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/es-MX.yml
839.3011289881542
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/es.yml
1048.781214049086
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/fa.yml
1006.2566479900852
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/fi.yml
935.244056978263
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/fr.yml
974.6984719531611
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/he.yml
826.7514749895781
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/hr.yml
1040.5551790026948
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/hu.yml
868.6606859555468
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/it.yml
983.0245879711583
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/ja.yml
1101.2658659601584
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/ko.yml
803.4766339696944
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/lt.yml
1047.3589000757784
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/lv.yml
790.2443470666185
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/nb.yml
973.8541450351477
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/nl.yml
1079.586865962483
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/pl.yml
780.5351580027491
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/pt-BR.yml
1038.037788006477
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/pt-PT.yml
739.1904569230974
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/ro.yml
768.3917579706758
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/ru.yml
1119.3809249671176
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/sv-SE.yml
732.1475809440017
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/tr.yml
782.102567027323
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/uk.yml
1110.3377710096538
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/vi.yml
769.5566279580817
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/zh-CN.yml
898.6905079800636
/Users/JJ/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/bundler/gems/activeadmin-0b4b22871fd3/config/locales/zh-TW.yml
1038.202345953323
/Users/JJ/blackcitrus/secret/secret_rails/config/locales/devise.en.yml
440.7359679462388
/Users/JJ/blackcitrus/secret/secret_rails/config/locales/en.yml
12.340541929006577

Finally I found the culprit - faker.

Conclusion

It turns out that gem faker was added to group :developmen, :test.

Moving it to group :test solved the problem.

P.S. A alternative to faker - ffaker.