Как надежно обновить файл в общей файловой системе?
То, что я хочу сделать, это безопасно обновить файл в общей файловой системе, где несколько программ могут обновлять файл одновременно. Я хочу заверить, что одна программа получает эксклюзивный доступ на период времени для чтения и обновления файла. Я также хочу убедиться, что файл записан полностью или не записан вообще, поэтому я записываю файл как временный файл, а затем переименовываю его.
- Актуальный файл для обновления:
MyFile.json
- Temp файл для записи:
tmp~MyFile.json{timestamp}~tmp
- Блокировка файла для блокировки:
MyFile.json#LOCK
Для этого нам, кажется, нужны три имени файла. Фактическое имя файла, которое мы обновляем. Существует временный файл, в который записывается содержимое, а затем временный файл переименовывается в фактическое имя. Но я хочу удерживать блокировку все время, пока это происходит, поэтому для файла нулевой длины требуется третье имя файла, которое блокируется перед доступом к файлу и освобождается после завершения всего процесса.
Есть ли более простой способ сделать это?
Я предполагаю, что нет. Если я заблокирую фактическое имя файла, то я не смогу удалить его, чтобы переименовать временный файл в реальный файл. Я также не могу использовать расширенную команду IO Files.move для удаления и переименования атомарно. Если файл заблокирован, моя программа не может заменить файл. Поэтому вместо этого я блокирую файл блокировки токена, и по крайней мере все программы, подчиняющиеся этому протоколу, должны быть соответствующим образом синхронизированы.
Это протокол:
- Блокировка файла блокировки, блокировка до получения блокировки
- прочитать фактический файл
- напишите временный файл
- удаляя прежний фактический файл, и проверьте, что он удален
- переименуйте temp в фактический и убедитесь, что новый фактический файл существует.
- Разблокировать файл блокировки
Проблема в следующем: в следующем проценте случаев следующий компьютер обнаруживает фактический файл, отсутствующий на шаге 2 (после получения блокировки). Похоже, что "не найден", но только временно. Это обновление не будет выполнено без изменения чего-либо на шаге 2, но при следующем доступе оно будет найдено там со всей необходимой информацией.
Проблема, как представляется, заключается в том, что "разблокировка" сигнализируется другой системе быстрее, чем "переименование". Второй компьютер может получить блокировку, но переименованный файл еще не появляется на нем. Первый компьютер выполняет переименование, а затем выполняет разблокировку, но второй компьютер может получить блокировку до появления переименования.
Шаг 4 и 5 можно выполнить за один шаг, используя атомарное обновление Files.move, но я получаю те же результаты.
ВОПРОС: Есть ли способ убедиться, что второй компьютер не сможет получить блокировку, пока не появится переименование.
ВОПРОС: Есть ли способ заставить Windows пойти и проверить, действительно ли файл действительно там?
Источник доступен на GitHub:
LockableJSONFile: https://github.com/agilepro/mendocino/blob/master/src/com/purplehillsbooks/json/LockableJSONFile.java
Источник теста: https://github.com/agilepro/mendocino/blob/master/src/com/purplehillsbooks/testcase/FileLockTest.java
Если вы хотите проверить, вытащите весь модуль и скомпилируйте. Инструкции по запуску теста приведены по адресу: https://github.com/agilepro/mendocino/wiki/File-Locking-Test
В качестве предупреждения, сразу после блокировки на шаге 1, если файл не существует, метод блокировки ожидает 100 мс в надежде, что он обнаружится в это время. Я все еще получаю сбои... в некоторых редких случаях это, очевидно, занимает более 100 мс. Это действительно брутто. Я хочу знать, существует ли лучший, полностью надежный способ обеспечения эксклюзивного доступа к файлу в общей файловой системе.