Чтение тысяч файлов в clojure

#clojure #io

#clojure #io

Вопрос:

Я работаю над сценарием, который должен считывать десятки тысяч файлов с диска. Я пытаюсь понять, как лучше всего это сделать. Я столкнулся с проблемой, когда я использую map для этого два пакета clj-glob и clojure-mail :

 (def sent-mail-paths
  (->> (str maildir-path "/*/_sent_mail/*")
       (glob) ;; returns files using clojure.java.io/as-file
       (map str) ;; i just want the paths
))

(def msgs
  (->> sent-mail-paths ;; 30K   paths
       (map mail/file->message)))
  

откуда glob берется функция в первом блоке clj-glob и используется as-file для возврата набора файловых объектов (см. Здесь ). Мне нужны только строки пути, поэтому я это делаю (map str) . mail/file->message Функция во втором блоке использует with-open вместе с FileInputStream классом Java для чтения файлов (см. Здесь ).

Проблема, с которой я сталкиваюсь, заключается в том, что этот код вызывает ошибку в тот момент, когда я пытаюсь обработать файлы в результирующей отложенной последовательности, выполнив даже что-то вроде:

 (count msgs)
  

Ошибка:

(Слишком много открытых файлов в системе)

Единственный способ, которым я смог выполнить эту работу, — использовать doseq :

 (def msgs (->> list-of-paths ;; 30K  paths
               (map mail/file->message)))

(def final (atom []))
(doseq [x result]
  (swap! final conj (mail/file->message x)))
  

Мой вопрос в том, является ли это лучшим (единственным?) как выполнить этот процесс, не открывая тысячи и тысячи файлов одновременно? Я не совсем понимаю, почему я не могу использовать отложенную последовательность, которая возвращается map . Почему это приводит к открытию тонны файлов.

Кстати, я заметил одну вещь clj-glob , которая не является хорошо обслуживаемым пакетом, не используется with-open при вызове as-file

Комментарии:

1. Извините, не могли бы вы опубликовать свой фактический код для процесса? Отложенная оценка — это не асинхронность или параллелизм, элементы обрабатываются один за другим, подход должен работать, если у вас нет утечки дескриптора.

2. У людей возникают проблемы с отложенными разделами и файлами, когда они возвращают отложенную последовательность содержимого файла — если вы не будете осторожны, вы можете закрыть файл до его завершения или полностью удалить файл. Это не то:-)

3. Наконец, ваша последняя часть действительно дословна? Вы определяете результаты, а затем используете результат. Вы также определяете результаты с помощью процесса map, а затем позже снова вызываете process … Спасибо!

4. Почему бы и нет (map process list-of-paths) ? Чего мне не хватает? Если вам нужен параллелизм, вы можете использовать (pmap process list-of-paths)

5. Хммм, я обновлю вопрос с помощью некоторого кода. Я думал, что понял проблему с точки зрения того, почему это не сработало, но, похоже, это не так. Я определенно могу обойтись без последовательного доступа, и на самом деле это то, что я пытаюсь сделать.

Ответ №1:

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

Вы можете прочитать здесь о том, как посмотреть, каков этот предел в настоящее время и как его увеличить: https://www.cyberciti.biz/faq/linux-increase-the-maximum-number-of-open-files /

Комментарии:

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

2. Я столкнулся с такой ситуацией, когда служба принимала такой объем запросов, что это могло привести к открытию большого количества файлов одновременно, прежде чем все файлы могли быть закрыты, что привело к превышению предела.