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
调试它
- 正在触发文件系统触发器 。
swap!
不是运行函数不止一次
- 正在打开和解析监视的文件
- 正在处理文件中的某些条目,但该处理在条目
111
处停止(这似乎与之前的任何条目没有显着差异)。
- 更新未完成,因此保留了
atom
的旧值
- 此事件挂起后不会触发任何文件系统事件。
我怀疑这是某处的内存问题,或者可能是 Clojure-Watch(或底层 FS-watching 库)中的错误。
关于如何修复它或进一步诊断它有什么想法吗?
挂起是由作为 :callback
传递给 watch/start
的函数内部引发的错误引起的。
这种情况的根本原因是scp
正在将修改后的文件复制到服务器(这不是原子的,因此第一个事件在复制完成之前触发,这就是导致要抛出的 JSON 解析错误)。
如果 :callback
抛出任何类型的错误,watch/start
会自动失败,这会加剧这种情况。
这里的解决方案是
使用rsync
复制文件。它确实以原子方式复制 但 它不会在目标文件上生成任何 :modify
事件,只会生成相关的 temp-files。由于其原子副本的工作方式,它将 仅 发出 :create
事件信号。
将 :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 解析错误,就很容易得出问题所在的理论。
我有一个 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
调试它
- 正在触发文件系统触发器 。
swap!
不是运行函数不止一次- 正在打开和解析监视的文件
- 正在处理文件中的某些条目,但该处理在条目
111
处停止(这似乎与之前的任何条目没有显着差异)。 - 更新未完成,因此保留了
atom
的旧值 - 此事件挂起后不会触发任何文件系统事件。
我怀疑这是某处的内存问题,或者可能是 Clojure-Watch(或底层 FS-watching 库)中的错误。
关于如何修复它或进一步诊断它有什么想法吗?
挂起是由作为 :callback
传递给 watch/start
的函数内部引发的错误引起的。
这种情况的根本原因是scp
正在将修改后的文件复制到服务器(这不是原子的,因此第一个事件在复制完成之前触发,这就是导致要抛出的 JSON 解析错误)。
如果 :callback
抛出任何类型的错误,watch/start
会自动失败,这会加剧这种情况。
这里的解决方案是
使用
rsync
复制文件。它确实以原子方式复制 但 它不会在目标文件上生成任何:modify
事件,只会生成相关的 temp-files。由于其原子副本的工作方式,它将 仅 发出:create
事件信号。将
: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 解析错误,就很容易得出问题所在的理论。