-
-
Notifications
You must be signed in to change notification settings - Fork 706
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
watchdog runs too many times / repeatedly when many files are changed #542
Comments
I think the |
I confirm this finding, although I'm not sure which exact way to use Watchdog you're referring to. Assuming $ watchmedo shell-command --command='echo start; sleep 0.1; echo finish' &
$ touch {1..3}.md
start
start
start
start
start
start
finish
finish
finish
finish
finish
finish It runs the command not just 3 times (once per file), but 6! It's easy to guess that this regressed when Linux emitter was improved to emit both FileModified and FileClosed events. Then let's try $ watchmedo shell-command --command='echo start; sleep 0.1; echo finish' --drop &
$ touch {1..3}.md
start
finish The events do get disintegrated. The $ watchmedo shell-command --command='echo start; sleep 0.1; echo finish' --wait &
$ touch {1..3}.md
start
finish
start
finish
start
finish
start
finish
start
finish
start
finish So indeed no solution currently. |
But let me propose an even better behavior (and a complete example with it): import time
import threading
from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer
class RebuildLoop(FileSystemEventHandler):
def __init__(self, func, debounce=0.1):
self.func = func
self.debounce = debounce
self.want_rebuild = False
self.condition = threading.Condition()
def on_any_event(self, event):
with self.condition:
self.want_rebuild = True
self.condition.notify_all()
def run(self):
while True:
with self.condition:
self.condition.wait_for(lambda: self.want_rebuild)
self.want_rebuild = False
print("Detected file changes")
while self.condition.wait(timeout=self.debounce):
print("Waiting for file changes to stop happening")
self.func()
def do_rebuild():
print("start")
time.sleep(1)
print("finish")
rebuild_loop = RebuildLoop(do_rebuild)
observer = Observer()
observer.schedule(rebuild_loop, ".")
observer.start()
try:
rebuild_loop.run()
finally:
observer.stop()
observer.join()
Consider a shell session with two bursts of events: $ python this_example.py &
$ for i in {1..3}; do (set -x; touch $i.md); sleep 0.05; done; \
sleep 0.5; \
for i in {1..3}; do (set -x; touch $i.md); sleep 0.05; done; \
fg
I already implemented this into a project in a more advanced shape here: mkdocs/mkdocs@a444c43#diff-1d9feb257a726960a5811814b912c90718f0935d46a7504c4d47268ccd67bb50R98 |
For my own case of avoiding the
|
@oprypin, @MareoRaft, @libcthorne: would you mind trying the patch from #940 to see if it fits your use cases 🙏? |
@BoboTiG, what a timely patch! I just started using watchdog and face the same issue as the people above. This seems like a patch to watchmedo, but not to the observers themselves, correct? It does not fit my use case, unfortunately. My static site generator has a --watch argument that uses watchdog. Saving multiple files at once triggers a long chain of rebuilds that keep my computer busy for a while. My use case would be to wait for changes to finish (as above), to prevent multiple successive builds. |
Ah, yes, that patch is only about watchmedo. Although you could just use the Anyway as I had commented, I already made my own debouncer that's better integrated into my server: the server also knows to pause serving any files while the rebuild is ongoing. |
You're right. After spending a few I'll try to extend FileSystemEventHandler and see where it takes me. The goal is to group multiple file change events to avoid multiple lengthy rebuilds. |
I am using watchdog to watch a git repository and rerun a build process every time the repo changes.
I have observed that performing a git pull, for example, can cause watchdog to run repeatedly. I believe that the git pull is causing a very large number of files to change, and hence watchdog runs many times because watchdog runs once per file modification.
For me, this is not the desired behavior. If 5 files are modified at the same time, for example, I only need watchdog to run the build process once. I am hoping we can add a configuration option to watchdog to allow somebody to opt-in to the following proposed behavior "lazy run":
Proposed behavior "lazy run":
Example of "lazy run" in action:
Suppose that the process to run takes a long time, and 5 files are modified in rapid succession.
As you can see, in this example, even though 5 files were modified, the process only runs 2 times.
The text was updated successfully, but these errors were encountered: