Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

listen should not listen on paths loaded from gems #26955

Closed
radiospiel opened this issue Nov 2, 2016 · 26 comments
Closed

listen should not listen on paths loaded from gems #26955

radiospiel opened this issue Nov 2, 2016 · 26 comments

Comments

@radiospiel
Copy link
Contributor

I stumbled across this when I tracked the reason why I have so many fsevent_watch processes running: after starting a rails server in development mode I see a lot of fsevent_watch processes watching directories that are in gems. Since these are not supposed to change anyways they should not be watched:

/Users/eno/projects/--redacted-- [master] > ps -xf | grep fs[e]vent_watch
  501  2309  2307   0  5:40PM ttys000    0:00.01 /Users/eno/.rvm/gems/ruby-2.3.1/gems/rb-fsevent-0.9.7/bin/fsevent_watch --latency 0.1 /Users/eno/.rvm/gems/ruby-2.3.1/gems/activesupport-5.0.0.1/lib/active_support/locale
  501  2310  2307   0  5:40PM ttys000    0:00.01 /Users/eno/.rvm/gems/ruby-2.3.1/gems/rb-fsevent-0.9.7/bin/fsevent_watch --latency 0.1 /Users/eno/.rvm/gems/ruby-2.3.1/gems/activemodel-5.0.0.1/lib/active_model/locale
  501  2311  2307   0  5:40PM ttys000    0:00.01 /Users/eno/.rvm/gems/ruby-2.3.1/gems/rb-fsevent-0.9.7/bin/fsevent_watch --latency 0.1 /Users/eno/.rvm/gems/ruby-2.3.1/gems/activerecord-5.0.0.1/lib/active_record/locale
  501  2312  2307   0  5:40PM ttys000    0:00.01 /Users/eno/.rvm/gems/ruby-2.3.1/gems/rb-fsevent-0.9.7/bin/fsevent_watch --latency 0.1 /Users/eno/.rvm/gems/ruby-2.3.1/gems/actionview-5.0.0.1/lib/action_view/locale
  501  2313  2307   0  5:40PM ttys000    0:00.01 /Users/eno/.rvm/gems/ruby-2.3.1/gems/rb-fsevent-0.9.7/bin/fsevent_watch --latency 0.1 /Users/eno/.rvm/gems/ruby-2.3.1/gems/responders-2.3.0/lib/responders/locales
  501  2314  2307   0  5:40PM ttys000    0:00.01 /Users/eno/.rvm/gems/ruby-2.3.1/gems/rb-fsevent-0.9.7/bin/fsevent_watch --latency 0.1 /Users/eno/.rvm/gems/ruby-2.3.1/gems/devise-4.2.0/config/locales
  501  2315  2307   0  5:40PM ttys000    0:00.01 /Users/eno/.rvm/gems/ruby-2.3.1/gems/rb-fsevent-0.9.7/bin/fsevent_watch --latency 0.1 /Users/eno/.rvm/gems/ruby-2.3.1/gems/kaminari-0.17.0/config/locales
  501  2316  2307   0  5:40PM ttys000    0:00.01 /Users/eno/.rvm/gems/ruby-2.3.1/gems/rb-fsevent-0.9.7/bin/fsevent_watch --latency 0.1 /Users/eno/projects/--redacted--/config/locales
  501  2317  2307   0  5:40PM ttys000    0:00.01 /Users/eno/.rvm/gems/ruby-2.3.1/gems/rb-fsevent-0.9.7/bin/fsevent_watch --latency 0.1 /Users/eno/.rvm/gems/ruby-2.3.1/gems/actioncable-5.0.0.1
  501  2318  2307   0  5:40PM ttys000    0:00.01 /Users/eno/.rvm/gems/ruby-2.3.1/gems/rb-fsevent-0.9.7/bin/fsevent_watch --latency 0.1 /Users/eno/.rvm/gems/ruby-2.3.1/gems/jquery-rails-4.2.1
  501  2319  2307   0  5:40PM ttys000    0:00.01 /Users/eno/.rvm/gems/ruby-2.3.1/gems/rb-fsevent-0.9.7/bin/fsevent_watch --latency 0.1 /Users/eno/.rvm/gems/ruby-2.3.1/gems/foundation-rails-6.2.3.0/config
  501  2320  2307   0  5:40PM ttys000    0:00.01 /Users/eno/.rvm/gems/ruby-2.3.1/gems/rb-fsevent-0.9.7/bin/fsevent_watch --latency 0.1 /Users/eno/.rvm/gems/ruby-2.3.1/gems/rails-timeago-2.15.0
  501  2321  2307   0  5:40PM ttys000    0:00.01 /Users/eno/.rvm/gems/ruby-2.3.1/gems/rb-fsevent-0.9.7/bin/fsevent_watch --latency 0.1 /Users/eno/.rvm/gems/ruby-2.3.1/gems/cloudinary-1.2.3
  501  2322  2307   0  5:40PM ttys000    0:00.01 /Users/eno/.rvm/gems/ruby-2.3.1/gems/rb-fsevent-0.9.7/bin/fsevent_watch --latency 0.1 /Users/eno/.rvm/gems/ruby-2.3.1/gems/foundation-rails-6.2.3.0/app/controllers
  501  2323  2307   0  5:40PM ttys000    0:00.01 /Users/eno/.rvm/gems/ruby-2.3.1/gems/rb-fsevent-0.9.7/bin/fsevent_watch --latency 0.1 /Users/eno/.rvm/gems/ruby-2.3.1/gems/devise-4.2.0/app/controllers
  501  2324  2307   0  5:40PM ttys000    0:00.01 /Users/eno/.rvm/gems/ruby-2.3.1/gems/rb-fsevent-0.9.7/bin/fsevent_watch --latency 0.1 /Users/eno/.rvm/gems/ruby-2.3.1/gems/devise-4.2.0/app/helpers
  501  2325  2307   0  5:40PM ttys000    0:00.01 /Users/eno/.rvm/gems/ruby-2.3.1/gems/rb-fsevent-0.9.7/bin/fsevent_watch --latency 0.1 /Users/eno/.rvm/gems/ruby-2.3.1/gems/devise-4.2.0/app/mailers
  501  2326  2307   0  5:40PM ttys000    0:00.01 /Users/eno/.rvm/gems/ruby-2.3.1/gems/rb-fsevent-0.9.7/bin/fsevent_watch --latency 0.1 /Users/eno/projects/--redacted--

The culprit here seems to be in railsties' lib/rails/application.rb file, which loads all the ActiveSupport::Dependencies.autoload_paths
as paths to watch. In my case config.watchable_files and config.watchable_dirs is empty.

# API.
def watchable_args #:nodoc:
  files, dirs = config.watchable_files.dup, config.watchable_dirs.dup

  ActiveSupport::Dependencies.autoload_paths.each do |path|
    dirs[path.to_s] = [:rb]
  end

  [files, dirs]
end

I guess for the reason that also autoload_paths should not include paths from inside gems, but maybe I am wrong there.

In any case fixing this could also help with #26158 and rails/spring-watcher-listen#13

Expected behavior

Watchers should only start for the app directories itself.

Actual behavior

Watchers start for a number of directories outside of the app directory itself.

System configuration

Rails version: 5.0.0.1
Ruby version: 2.3.1
OS: Mac OS 10.11

@maclover7
Copy link
Contributor

cc @fxn

@rafaelfranca
Copy link
Member

I guess for the reason that also autoload_paths should not include paths from inside gems, but maybe I am wrong there.

Yes, they should, otherwise you would have to require manually all controller, models and helpers defined in engines.

I guess to fix this we would have to remove all items in the autoload path that are not inside the application.

@radiospiel
Copy link
Contributor Author

radiospiel commented Nov 5, 2016

I can provide a fix along that path. Is there a better way to check whether or not a path is inside the app apart from path.starts_with?(Rails.root + <append fs dir separator>)

@rafaelfranca
Copy link
Member

I don't think so. That is the best API that I remember.

@matthewd
Copy link
Member

matthewd commented Nov 6, 2016

We should be able to get the paths for all the gems from rubygems, then exclude those... maybe with some consultation with bundler too.

@radiospiel
Copy link
Contributor Author

I have a patch here https://github.com/radiospiel/rails/pull/1 which I verified to work correctly. However, I have trouble building tests since the code detects Rails' root via defined?(::Rails) && Rails.root. Any ideas?

@fxn
Copy link
Member

fxn commented Nov 6, 2016

Let's think a bit about this.

If you put something in autoload_paths, it is expected to change. I mean, that is the normal use case. You have the ability to autoload the constant, and also the expectation to reload it when they are cleaned up.

When you want to spare the require but do not want to be monitoring changes, you put that in autoload_once_paths. Maybe this issue is saying that we should move more stuff to autoload_once_paths, and that we should ignore those when configuring the watchers?

@fxn
Copy link
Member

fxn commented Nov 6, 2016

Ah, I don't mean this approach is direct in the current dependencies.rb, maybe the implementation needs some adjustment (have not checked the code).

@radiospiel
Copy link
Contributor Author

radiospiel commented Nov 6, 2016

@fxn I agree that maybe things should be moved into autoload_once_paths. However, in the meantime I also found out that listen is called both on the auto_load path (for ruby code), but also on some paths that stem from i18n, and this is in a quite simple application. I have no idea if in other scenarios this would be even invoked from more places.

On the other hand you would not expect things to change outside of the current app regardless of how those were loaded (i18n or ruby autoloading), hence I would suggest to fix evented_file_update_checker.rb to only check for updates from inside the app directory in the first place - and this is what https://github.com/radiospiel/rails/pull/1/files is doing.

Moving things from auto_load to auto_load_once would be a change with a much broader surface and certainly out of scope of my original report. (Not that I would disagree with it)

@matthewd
Copy link
Member

matthewd commented Nov 7, 2016

you would not expect things to change outside of the current app

FWIW, I disagree, with the sole exception of gem files

@radiospiel
Copy link
Contributor Author

you would not expect things to change outside of the current app

FWIW, I disagree, with the sole exception of gem files

Mathew, I am afraid I don’t quite catch that. Can you explain further,
please?

me at github: https://github.com/radiospiel
me at linked.in: https://www.linkedin.com/in/radiospiel

On 7 Nov 2016, at 2:24, Matthew Draper wrote:

you would not expect things to change outside of the current app

FWIW, I disagree, with the sole exception of gem files

You are receiving this because you authored the thread.
Reply to this email directly or view it on GitHub:
#26955 (comment)

@matthewd
Copy link
Member

matthewd commented Nov 7, 2016

I do expect files outside of the application's root directory to change. The only files I think it is reasonable to expect not to change are files that belong to installed gems.

@radiospiel
Copy link
Contributor Author

@matthewd if that is the expectation then my original complaint is moot - and in that case feel free to close this issue. However, I can't envision a scenario in which a source file's change should affect an applications behaviour if it is not inside the application nor inside an installed gem.

@fxn
Copy link
Member

fxn commented Nov 7, 2016

I agree with @matthewd.

The key point here is which is a better reasonabe default for watchers? We all would agree that watching stuff in gems is not a good default. It is OK that the use case in which you edit a gem to debug something is not supported out of the box, for example.

But there are use cases for wanting to watch stuff outside of the application, like developing an engine that is declared as a dependency via path in the Gemfile of the parent application. There, you want to watch and reload as we do today.

And that also forces us to put our finger on defining what "stuff in gems" exactly means. Because that engine may be a gem, and it is being loaded by Bundler.

@fxn
Copy link
Member

fxn commented Nov 7, 2016

I would be open to reconsider this behaviour though. That is, if you want to watch stuff outside of Rails.root, then you need to opt-in somehow. That use case exists but it is less common, so if you need to do something to enable it... maybe that's not a big deal, and the the rule for what to watch is more straightforward to implement, and to understand by users.

@radiospiel
Copy link
Contributor Author

radiospiel commented Nov 7, 2016

Could this be a configuration setting via config/environments/development.rb maybe, which lets users define additional directories that should be watched? Coming back to @matthewd suggestion above we could also only include paths inside Rails.root or loaded from the Gemfile via a "path:" gem file location.

@matthewd
Copy link
Member

matthewd commented Nov 8, 2016

My suggestion is that we include all paths except those inside Gem.path.

That'll do what we want for installed gems (exclude them) and path gems (include them). It doesn't get git gems right -- we should really exclude those (if they're bundler-managed, and not using a local override): that's where bundler could give us more information.

@radiospiel
Copy link
Contributor Author

radiospiel commented Nov 8, 2016

Should we really care about git gems? When building a gem locally you would not include it via git but via path anyways, I guess... Ah, but then Bundler probably does not install git gems inside Gem.path, I see.

@radiospiel
Copy link
Contributor Author

Updated preview here: https://github.com/radiospiel/rails/pull/1

@radiospiel
Copy link
Contributor Author

Any comments on my PR suggestion?

@fxn
Copy link
Member

fxn commented Dec 8, 2016

The ones present in the thread at the moment.

@matthewd
Copy link
Member

matthewd commented Dec 8, 2016

@radiospiel that looks good to me. Submit it as an actual PR?

@marzdrel
Copy link

I've added the fix proposed by @radiospiel as a monkey patch and so far so good. There is no processes started outside the app directory. The number of active processes is down to ~15 from ~200 per app.

In case someone ends up reading thread from Google while looking for a solution - this worked for me:

config/initializers/fsevent_watch_fix.rb

module ActiveSupport
  class EventedFileUpdateChecker
    private

    def directories_to_watch
      dtw = (@files + @dirs.keys).map { |f| @ph.existing_parent(f) }
      dtw.compact!
      dtw.uniq!

      normalized_gem_paths = Gem.path.map { |path| File.join path, "" }
      dtw = dtw.reject do |path|
        normalized_gem_paths.any? { |gem_path| path.to_s.starts_with?(gem_path) }
      end

      @ph.filter_out_descendants(dtw)
    end
  end
end

@stale
Copy link

stale bot commented Mar 27, 2017

This issue has been automatically marked as stale because it has not been commented on for at least three months.
The resources of the Rails team are limited, and so we are asking for your help.
If you can still reproduce this error on the 5-1-stable branch or on master, please reply with all of the information you have about it in order to keep the issue open.
Thank you for all your contributions.

@stale stale bot added the wontfix label Mar 27, 2017
@rafaelfranca rafaelfranca added stale and removed stale labels Mar 27, 2017
@rafaelfranca
Copy link
Member

Fixed by 8d50b45

@stale stale bot removed the stale label Mar 28, 2017
@rafaelfranca
Copy link
Member

Backported in 5568a77 and 615baac

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants