babashka / pod-babashka-fswatcher

Babashka filewatcher pod.
BSD 3-Clause "New" or "Revised" License
22 stars 3 forks source link

Duplicate events on one file change in Windows 10 #11

Closed rgkirch closed 1 year ago

rgkirch commented 2 years ago
(pods/load-pod 'org.babashka/fswatcher "0.0.2")
(require '[pod.babashka.fswatcher :as fw])

(def count (atom 0))

(def watcher (fw/watch "some-file.txt"
                       (fn [event]
                         (do
                           (prn event)
                           (swap! count inc)))
                       {:delay-ms 250}))

When I modify the file then it prints twice and increments count from 0 to 2.

rgkirch commented 2 years ago

I'm on windows 10. bb version v0.7.4-snapshot

lispyclouds commented 2 years ago

hey, is it the same event you're seeing twice? or is one of them chmod and the other edit?

rgkirch commented 2 years ago
(def w1 (fw/watch "file.clj"
                  (fn [{:keys [type path] :as _event}]
                    (let [now (System/currentTimeMillis)]
                      (prn [:type type :path path :now now])))
                  {:recursive true
                   :delay-ms 250}))
[:type :write :path "file.clj" :now 1649072989448]
[:type :write :path "file.clj" :now 1649072989448]
rgkirch commented 2 years ago
(def count (atom 0))

(def w1 (fw/watch "file.clj"
                  (fn [{:keys [type path] :as _event}]
                    (let [now (System/currentTimeMillis)]
                      (swap! count inc)
                      (prn [:type type :path path :now now :count @count])))
                  {:recursive true
                   :delay-ms 250}))

saving file prints

[:type :write :path "file.clj" :now 1649073215153 :count 1]
[:type :write :path "file.clj" :now 1649073215156 :count 2]

editing and saving again prints

[:type :write :path "file.clj" :now 1649073268285 :count 3]
[:type :write :path "file.clj" :now 1649073268285 :count 4]
rgkirch commented 2 years ago

I'm connecting to bb --nrepl-server from emacs over cider.

lispyclouds commented 2 years ago

Could be something specific to the windows apis or the way the editor saves it. I get two events on my linux machine when saving with vim: chmod and write. Could you try something like echo "foo" > the-file and see if you still have the duplicate events? @borkdude can you try in windows? I dont have a windows machine off hand.

borkdude commented 2 years ago

Will do later this week. Perhaps looking at the issues of the go lib and/or upgrading it will also help.

rgkirch commented 2 years ago

https://github.com/fsnotify/fsnotify/issues/206 I did try deduping but it introduced other strange behavior.

(defn on-change
  ([f paths]
   (on-change f paths {}))
  ([f paths {:keys [timeout]
             :or {timeout 1000}}]
   (let [cache (atom {})
         watchers (for [path paths]
                    (fw/watch path
                              (fn [{:keys [type path] :as _event}]
                                (let [now (System/currentTimeMillis)]
                                  (when (= type :write)
                                    (when (or (not (contains? @cache path))
                                              (< timeout (- now (@cache path))))
                                      (swap! cache assoc path now)
                                      (f path)))))
                              {:recursive true
                               :delay-ms 250}))]
     (fn unwatch-all [] (doseq [w watchers]
                          (fw/unwatch w))))))
borkdude commented 2 years ago

I'm not a fan of trying to "hide" a bug of the underlying library. The best you can do if the underlying library is compensate for this in your own app.

There is another filewatcher pod which is built in Rust, which you could try too:

https://github.com/babashka/pod-registry/blob/master/examples/filewatcher.clj

lispyclouds commented 2 years ago

I guess we don't have a windows build for it yet?

borkdude commented 2 years ago

🤦 Maybe switch to Github actions for that one then so we can more easily make a matrix

rgkirch commented 1 year ago

There is another filewatcher pod which is built in Rust, which you could try too:

It doesn't support Windows.

rgkirch commented 1 year ago
(fw/watch "folder/"
          (fn [event] (prn event))
          {:recursive true
           :delay-ms 2000})

Saving the file with emacs generates 4 messages but echo "text" >> folder/file.txt generates 2 messages.

{:type :write, :path "folder\\file.txt"}
{:type :write, :path "folder\\file.txt"}
{:type :write, :path "folder\\file.txt"}
{:type :write, :path "folder\\file.txt"}
{:type :write, :path "folder\\file.txt"}
keychera commented 1 year ago

I encountered this problem as well on Windows 10, I tried saving in VSCode and Notepad and both fires the write events twice

but then I did some digging and found a suggestion from user steve-kessel-aero and a Go code from sejongk in this thread: https://github.com/fsnotify/fsnotify/issues/122 which suggest making a loop that will fire the latest recorded events on a specific time interval

then I tried writing that Go code in Clojure and come up with this code below:

(require '[babashka.pods :as pods])
(pods/load-pod 'org.babashka/fswatcher "0.0.3")
(require '[pod.babashka.fswatcher :as fw])
(require '[clojure.core.async :refer [go-loop timeout <!]])

(def target "src")
(println "watching" target)

(def latest-event (atom nil))
(fw/watch target (fn [event] (reset! latest-event event))
          {:recursive true})

(defn handle [event]
  (println "handled this:" event))

(go-loop [time-unit 1]
  (<! (timeout 100))
  (let [[event _] (reset-vals! latest-event nil)]
    (some-> event handle))
  (recur (inc time-unit)))

@(promise)

which served for my use case quite well, so I hope this can help (also I still have little experience with Clojure so any feedback will be greatly appreciated)

borkdude commented 1 year ago

@keychera looks good to me!

borkdude commented 1 year ago

I think this is now fixed by #19