Обновление Atom висит внутри часового вызова Clojure
У меня возникла ситуация, когда я смотрю конкретный каталог на предмет изменений файловой системы. Если определенный файл в этом каталоге изменяется, я перечитываю его, прикрепляю некоторую существующую кэшированную информацию и сохраняю ее в 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).
Любые идеи, как я мог бы исправить это или диагностировать это дальше?
1 ответ
Зависание вызвано ошибкой, генерируемой внутри функции, переданной как :callback
в watch/start
,
Основной причиной в этом случае является то, что измененный файл копируется на сервер scp
(который не является атомарным, и, следовательно, первое событие срабатывает до завершения копирования, что и вызывает ошибку синтаксического анализа JSON).
Это усугубляется тем, что watch/start
молчит, если его :callback
выдает любую ошибку
Решения здесь
использование
rsync
копировать файлы. Копирует атомарно, но не генерирует:modify
события в целевом файле, только связанные временные файлы. Из-за того, как работает его атомная копия, он будет только сигнализировать:create
События.Обернуть
:callback
вtry
/catch
и иметьcatch
предложение возвращает старое значение атома. Это приведет кload-posts!
запускаться несколько раз, но последний раз будет при завершении копирования файла, что должно, наконец, сделать правильно.
(Я сделал и то и другое, но любой бы реально решил проблему).
Третий вариант - использовать библиотеку, отслеживающую FS, которая сообщает об ошибках, таких как Hawk или dirwatch (или, возможно, hara.io.watch? Я не использовал ни одного из них, поэтому не могу комментировать).
Диагностика это включала обертывание :callback
тело с
(try
<body>
(catch Exception e
(println "ERROR IN SWAP!" e)
old))
чтобы увидеть, что на самом деле бросали. Как только это напечатало ошибку синтаксического анализа JSON, было довольно легко получить теорию того, что пошло не так.