guard / rb-fsevent

FSEvents API with signals handled (without RubyCocoa)

Home Page:https://rubygems.org/gems/rb-fsevent

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

rb-fsevent taking up over 100% CPU

Bodacious opened this issue · comments

I'm running guard in my Rails app and the test suite has recently stopped running smoothly.
If I'm lucky, it'll run all of the tests once, maybe twice. After that, it takes so long to respond to even one small test file being changed that
using the gem becomes futile.

When following top while the tests run, I can see there's a ruby process that's taking up over 100% of the CPU constantly. Even once all tests are run and I haven't made any changes to the file.

screen shot 2013-12-03 at 17 52 51

The ruby process is:

/Users/Bodacious/.rvm/gems/ruby-2.0.0-p247@MyApp/gems/rb-fsevent-0.9.3/bin/fsevent_watch --latency 0.1 /Users/Bodaiou/Clients/MyApp

(process 31332) in the screenshot attached.

Here's my setup:

Gemfile

group :test do
  gem 'launchy'
  gem "mocha", require: false
  gem 'database_cleaner'
  gem 'selenium-webdriver'
  gem 'capybara-webkit' # for headless javascript tests
  gem 'timecop'
  gem "minitest-focus"
end

group :development, :test do
  gem "minitest-rails"
  gem "minitest-rails-capybara"
  gem 'zeus'
  gem 'guard'
  gem 'guard-minitest'
  gem 'factory_girl_rails'
end

Guardfile

guard :minitest, all_on_start: false do
  # Rails 4
  watch(%r{^app/(.+)\.rb})                               { |m| "test/#{m[1]}_test.rb" }
  watch(%r{^app/controllers/application_controller\.rb}) { 'test/controllers' }
  watch(%r{^app/controllers/(.+)_controller\.rb})        { |m| "test/integration/#{m[1]}_test.rb" }
  watch(%r{^app/views/(.+)_mailer/.+})                   { |m| "test/mailers/#{m[1]}_mailer_test.rb" }
  watch(%r{^lib/(.+)\.rb})                               { |m| "test/lib/#{m[1]}_test.rb" }
  watch(%r{^test/.+_test\.rb})
  watch(%r{^test/test_helper\.rb})                       { 'test' }

  ignore(%r{^tmp/})

end

test_helper

ENV["RAILS_ENV"] = "test"
require File.expand_path("../../config/environment", __FILE__)

require 'rails/test_help'
require 'minitest/autorun'
require 'minitest/pride'
require "minitest/rails/capybara"

require "mocha/setup"

# Sidekiq https://github.com/mperham/sidekiq/wiki/Testing
require 'sidekiq/testing'
Sidekiq::Testing.fake!

Dir[Rails.root.join('test', 'support', '*.rb')].each do |file|
  require file
end


class MiniTest::Spec
  include FactoryGirl::Syntax::Methods
  include AllTestHelper

end


class ActiveSupport::TestCase
  include FactoryGirl::Syntax::Methods
  include AllTestHelper
end

class Capybara::Rails::TestCase
  include Rails.application.routes.url_helpers 
  include Capybara::DSL
  include Capybara::Assertions
  include IntegrationTestHelper

  # Called before each Feature spec
  before :each do
  end

  # Called after each Feature spec
  after :each do
    Capybara.reset_sessions!
  end
end

Gem Versions:

  • minitest (4.7.5)
  • minitest-capybara (0.5.0)
  • minitest-focus (1.1.0)
  • minitest-metadata (0.4.0)
  • minitest-rails (0.9.2)
  • minitest-rails-capybara (0.10.0)
  • mobvious-rails (0.1.2)
  • mocha (0.14.0)
  • guard (2.2.4)
  • guard-minitest (2.1.2)
  • rb-fsevent (0.9.3)

Any advise on where to look to start fixing this?

Do you have a lot of files in your app? You could try to ignore some of them with ignore method: https://github.com/guard/guard#ignore

How many files is a lot?

It's not a particularly large app:

+----------------------+-------+-------+---------+---------+-----+-------+
| Name                 | Lines |   LOC | Classes | Methods | M/C | LOC/M |
+----------------------+-------+-------+---------+---------+-----+-------+
| Controllers          |  2162 |  1526 |      67 |     282 |   4 |     3 |
| Helpers              |  1184 |   894 |       8 |     127 |  15 |     5 |
| Models               |  1729 |  1017 |      43 |     120 |   2 |     6 |
| Mailers              |   197 |   103 |       2 |      11 |   5 |     7 |
| Javascripts          |   360 |   225 |       6 |      72 |  12 |     1 |
| Libraries            |    40 |    29 |       1 |       6 |   6 |     2 |
| Controller tests     |     7 |     3 |       0 |       0 |   0 |     0 |
| Helper tests         |     9 |     6 |       0 |       0 |   0 |     0 |
| Model tests          |   860 |   622 |       0 |       0 |   0 |     0 |
| Mailer tests         |    55 |     3 |       0 |       0 |   0 |     0 |
+----------------------+-------+-------+---------+---------+-----+-------+
| Total                |  6603 |  4428 |     127 |     618 |   4 |     5 |
+----------------------+-------+-------+---------+---------+-----+-------+
  Code LOC: 3794     Test LOC: 634     Code to Test Ratio: 1:0.2

Comp stats:
Processor: 2.7 GHz Intel Core i7
Memory: 8GB

Even stripping the Guardfile back down to this shows the CPU usage at 90%+:

guard :minitest, all_on_start: false do
  # Rails 4
  watch(%r{^app/(.+)\.rb})                               { |m| "test/#{m[1]}_test.rb" }
  watch(%r{^test/.+_test\.rb})
  watch(%r{^test/test_helper\.rb})                       { 'test' }
end

I was more thinking about files like logs/tmp, not your app code.

I thought only files defined in watch() were watched?

i.e. The others are implicitly ignored

No, all files not ignored by default are watched.

find . -type f | wc -l is useful to count all files in a dir.

Ah - I didn't realise that. 7216 is probably a lot of files to be watching.

Yeah, try to ignore some.

That's working a lot better now - thanks:

guard :minitest, all_on_start: false, zeus: true, bundler: false do
  # Rails 4 watch:
  watch(%r{^app/(.+)\.rb})                               { |m| "test/#{m[1]}_test.rb" }
  watch(%r{^test/.+_test\.rb})
  watch(%r{^test/test_helper\.rb})                       { 'test' }

  # Rails 4 ignores:
  ignore(%r{^bin/*})
  ignore(%r{^config/*})
  ignore(%r{^db/*})
  ignore(%r{^lib/*})
  ignore(%r{^log/*})
  ignore(%r{^public/*})
  ignore(%r{^tmp/*})
end

Great, you can do it like that:

 # Rails 4 ignores:
ignore([%r{^bin/*}, %r{^config/*}, %r{^db/*}, %r{^lib/*}, %r{^log/*}, %r{^public/*}, %r{^tmp/*}])

guard :minitest, all_on_start: false, zeus: true, bundler: false do
  # Rails 4 watch:
  watch(%r{^app/(.+)\.rb})                               { |m| "test/#{m[1]}_test.rb" }
  watch(%r{^test/.+_test\.rb})
  watch(%r{^test/test_helper\.rb})                       { 'test' }
end

Thanks!