Синхронизация файловой системы и кэшированных данных при запуске программы - PullRequest
1 голос
/ 12 ноября 2009

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

Итак, вопрос в том, каков хороший алгоритм для этого? Один из способов, который я могу придумать, - обойти файловую систему и собрать путь и последние изменения / размер всех файлов в словаре. Затем я просматриваю весь список в базе данных. Если совпадения нет, то я удаляю элемент из базы / кеша. Если есть совпадение, то я удаляю элемент из словаря. Затем словарь содержит все элементы, данные которых необходимо обновить. Это может сработать, однако, похоже, что при каждом запуске было бы достаточно много памяти и времени, поэтому мне было интересно, есть ли у кого идеи получше?

Если это имеет значение: программа написана только для Windows на C # в .NET CLR 3.5, с использованием объекта SQLite для ADO.NET, доступ к которому осуществляется через структуру сущностей / LINQ для ADO.NET.

Ответы [ 3 ]

2 голосов
/ 19 ноября 2009

В Windows есть механизм журнала изменений, который делает то, что вы хотите: вы подписываетесь на изменения в некоторой части файловой системы и при запуске можете прочитать список изменений, которые произошли с момента вашего последнего чтения. Смотри: http://msdn.microsoft.com/en-us/library/aa363798(VS.85).aspx

РЕДАКТИРОВАТЬ: я думаю, что это требует довольно высоких привилегий, к сожалению

2 голосов
/ 20 ноября 2009

Наше приложение является кроссплатформенным настольным приложением C ++, но имеет очень похожие требования. Вот описание высокого уровня того, что я сделал:

  • В нашей базе данных SQLite есть таблица Files, в которой хранятся file_id, name, hash (в настоящее время мы используем дату последнего изменения в качестве значения хеш-функции) и state.
  • Каждая другая запись ссылается на file_id. Это позволяет легко удалять «грязные» записи при изменении файла.

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

При первом запуске

  • База данных пуста. Walker рекурсивно обходит файловую систему и добавляет записи в таблицу Files. state установлен на UNPROCESSED.
  • Затем Loader перебирает таблицу Files в поисках UNPARSED файлов. Они передаются в Parser (который выполняет фактический анализ и вставку данных)
  • Это занимает некоторое время, поэтому 1-й запуск может быть немного медленным.

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

n + 1 Запуск

  • Scrubber перебирает таблицу Files и ищет файлы, которые были удалены, и файлы, которые были изменены. Он устанавливает state на DIRTY, если файл существует, но был изменен, или DELETED, если файл больше не существует.
  • Deleter (не самое оригинальное имя) перебирает таблицу Files в поисках файлов DIRTY и DELETED. Удаляет другие связанные записи (связанные через file_id). После удаления связанных записей исходная запись File либо удаляется, либо устанавливается обратно на state=UNPARSED
  • Walker затем обходит файловую систему для получения новых файлов.
  • Наконец, Loader загружает все UNPARSED файлы

В настоящее время «наихудший сценарий» (каждый файл изменяется) встречается очень редко - поэтому мы делаем это каждый раз при запуске приложения. Но, разделив процесс на эти этапы, мы можем легко расширить реализацию до:

  • Scrubber / Deleter может быть подвергнут рефакторингу, чтобы оставить грязные записи на месте до окончания новой данные загружаются (поэтому приложение «продолжает работать», пока новые данные кэшируются в базе данных)
  • Loader может загружать / анализировать фоновый поток во время простоя в основном приложении
  • Если вы знаете что-то о файлах данных заранее, вы можете назначить «вес» файлам и немедленно загрузить / проанализировать действительно важные файлы и поставить в очередь менее важные файлы для последующей обработки.

Просто некоторые мысли / предложения. Надеюсь, они помогут!

1 голос
/ 12 ноября 2009

Первая очевидная вещь, которая приходит на ум, - это создание отдельного небольшого приложения, которое всегда будет запускаться (возможно, как служба) и создавать своего рода «журнал» изменений в файловой системе (нет необходимости работать с SQLite, просто запишите их в файл). Затем, когда основное приложение запускается, оно может просмотреть журнал и точно знать, что изменилось (не забудьте впоследствии очистить журнал: -).

Однако, если по какой-то причине это неприемлемо для вас, давайте попробуем взглянуть на исходную проблему.

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

Во-вторых, если вам нужно принять решение по каждому файлу, который только вы знаете, как сделать, вероятно, нет другого пути, кроме как просмотреть все файлы.

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

Следовательно, ваша единственная надежда - сокращение пространства поиска с помощью твиков и хаков. И у меня есть два из них на уме.

Во-первых, лучше сначала запрашивать базу данных для каждого файла, а не создавать словарь для всех файлов. Если вы создаете индекс для столбца пути к файлу в вашей базе данных, он должен быть быстрее и, конечно, менее ресурсоемким.

Во-вторых, вам вообще не нужно запрашивать базу данных :-) Просто сохраните точное время, когда ваше приложение в последний раз выполнялось где-то (в файле .settings?), И проверьте каждый файл, чтобы увидеть, не новее ли он того времени. Если это так, вы знаете, что это изменилось. Если это не так, вы знаете, что заметили, что это изменение в последний раз (с помощью FileSystemWatcher).

Надеюсь, это поможет. Веселитесь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...