-
Notifications
You must be signed in to change notification settings - Fork 247
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
Guidance on using listen with multiple processes #398
Comments
I can't reply much right now (exhausted and falling asleep as I type), so I'll check this out tomorrow. Listen does keep a list of global threads and I'm suspecting that's the problem - they should be process-specific. To get a clearer picture of what's going on, run it with I'll see if I can patch this tomorrow and release a fix. Thanks for the report and example! Much appreciated! |
$stdout.sync = true
require 'fileutils'
require 'listen'
FileUtils.mkdir_p("/tmp/listen")
def change_callback(modified, added, removed)
puts " Changed registered in: #{ Process.pid }"
end
def boot!
puts "Listening on process: #{ Process.pid }"
Listen.to("/tmp/listen", &method(:change_callback)).start
end
boot!
Thread.new do
sleep 0.5
FileUtils.touch("/tmp/listen/foo")
end
pids = [] # collect pids to kill and wait on
pids << fork do
boot!
sleep # sleep to prevent boot! returning from fork
end
pids << fork do
boot!
sleep # sleep to prevent boot! returning from fork
end
sleep 2
pids.each { |pid| Process.kill("TERM", pid) }
pids.each { |pid| Process.wait(pid) } |
For a clean solution, it's best to trap a special signal (INT ?), wake from the sleep and stop Listen before exiting. Killing process will still obviously free the system resources, but in the future gracefully exiting may help find/prevent other bugs. |
Thanks for your responses here and for clarifying behavior, I saw earlier but forgot to comment. I plan on adding a small section in the documentation to remind people using forks to re-run listen code on multiple processes. About that suggestion to trap a signal, I would recommend to anyone doing this to be careful. You want to make sure that you re-signal it as other systems may be depending on getting the event like Puma or Sidekiq. Original article: https://devcenter.heroku.com/articles/what-happens-to-ruby-apps-when-they-are-restarted#why-some-programs-won-t-die Here's an okay-ish way to re-signal if you have to trap one: http://stackoverflow.com/questions/29568298/run-code-when-signal-is-sent-but-do-not-trap-the-signal-in-ruby |
A follow up from guard#398 to mention behavior across multiple processes.
Rails 5 is now using an evented file system listener. We were seeing a bug where controller code would not be reloaded in development. I eventually tracked it down rails/rails#24990 (comment)
What I think is happening is that the listen code is initialized in a process, let's call it PID 1. Then our two puma workers boot and fork to make PID 2 and PID 3. When a file is changed the callback gets triggered inside of PID 1 but since variable changes aren't persisted across other processes PID 2 and PID 3 never know that the file was updated and they need to reload code.
Initially I thought we could re-invoke the listen code on each process to get a notification on that process. However this doesn't work:
I would expect to see something like:
However this is the output that I get
Only the parent process is notified even though we've "booted" a Listen instance on each fork.
So my question is this: are there any good practices with using Listen with multiple processes?
The text was updated successfully, but these errors were encountered: