Обновление 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 который сказал мне

  1. Триггер файловой системы запускается.
  2. swap! не запускает функцию более одного раза
  3. Просматриваемый файл открывается и анализируется
  4. Некоторые записи из файла обрабатываются, но эта обработка останавливается при вводе 111 (который, кажется, существенно не отличается от любых предыдущих записей).
  5. Обновление не завершено, и старое значение этого atom поэтому сохраняется
  6. После зависания файловой системы события не запускаются.

Я подозреваю, что это либо проблема с памятью, либо, возможно, ошибка в Clojure-Watch (или лежащая в основе библиотека для наблюдения за FS).

Любые идеи, как я мог бы исправить это или диагностировать это дальше?

1 ответ

Решение

Зависание вызвано ошибкой, генерируемой внутри функции, переданной как :callback в watch/start,

Основной причиной в этом случае является то, что измененный файл копируется на сервер scp (который не является атомарным, и, следовательно, первое событие срабатывает до завершения копирования, что и вызывает ошибку синтаксического анализа JSON).

Это усугубляется тем, что watch/start молчит, если его :callback выдает любую ошибку

Решения здесь

  1. использование rsync копировать файлы. Копирует атомарно, но не генерирует :modify события в целевом файле, только связанные временные файлы. Из-за того, как работает его атомная копия, он будет только сигнализировать :create События.

  2. Обернуть :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, было довольно легко получить теорию того, что пошло не так.

Другие вопросы по тегам