Atom 更新在 Clojure 监视调用中挂起

Atom update hangs inside of Clojure watch call

我有一个 situation,我可以在其中监视特定目录的文件系统更改。如果该目录中的某个文件发生更改,我会重新读取它,附加一些现有的缓存信息,并将其存储在 atom.

相关代码看起来像

(def posts (atom []))

(defn load-posts! []
  (swap!
   posts
   (fn [old]
     (vec
      (map #(let [raw (json/parse-string % (fn [k] (keyword (.toLowerCase k))))]
              (<snip some processing of raw, including getting some pieces from old>))
           (line-seq (io/reader "watched.json")))))))


;; elsewhere, inside of -main
(watch/start-watch
    [{:path "resources/"
      :event-types [:modify]
      :callback (fn [event filename]
                  (when (and (= :modify event) (= "watched.json" filename))
                    (println "Reloading posts.json ...")
                    (posts/load-posts!)))}
     ...])

这最终在本地运行良好,但是当我将它部署到我的服务器时,swap! 调用在中途挂起。

我试过通过 println 调试它

  1. 正在触发文件系统触发器
  2. swap!不是运行函数不止一次
  3. 正在打开和解析监视的文件
  4. 正在处理文件中的某些条目,但该处理在条目 111 处停止(这似乎与之前的任何条目没有显着差异)。
  5. 更新未完成,因此保留了 atom 的旧值
  6. 此事件挂起后不会触发任何文件系统事件。

我怀疑这是某处的内存问题,或者可能是 Clojure-Watch(或底层 FS-watching 库)中的错误。

关于如何修复它或进一步诊断它有什么想法吗?

挂起是由作为 :callback 传递给 watch/start 的函数内部引发的错误引起的。

这种情况的根本原因是scp正在将修改后的文件复制到服务器(这不是原子的,因此第一个事件在复制完成之前触发,这就是导致要抛出的 JSON 解析错误)。

如果 :callback 抛出任何类型的错误,watch/start 会自动失败,这会加剧这种情况。

这里的解决方案是

  1. 使用rsync复制文件。它确实以原子方式复制 它不会在目标文件上生成任何 :modify 事件,只会生成相关的 temp-files。由于其原子副本的工作方式,它将 发出 :create 事件信号。

  2. :callback 包装在 try/catch 中,并使 catch 子句 return 的旧值原子。这将导致 load-posts! 到 运行 多次,但最后一次将在文件复制完成时,最终应该做正确的事情。

(我都做了,但任何一个都可以实际解决问题)。

第三种选择是使用报告错误的 FS-watching 库,例如 Hawk or dirwatch (or possibly hara.io.watch?我没有用过这些,所以我不能发表评论。

诊断此问题涉及用

包装 :callback body
(try 
  <body> 
  (catch Exception e 
    (println "ERROR IN SWAP!" e) 
    old))

查看实际抛出的是什么。一旦打印出 JSON 解析错误,就很容易得出问题所在的理论。