Надежное сохранение файлов (File.Replace) в загруженной среде

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

Я принял следующий шаблон:

string tempFileName = Path.GetTempFileName();
// ...write out the data to temporary file...
MoveOrReplaceFile(tempFileName, fileName);

... где MoveOrReplaceFile:

public static void MoveOrReplaceFile( string source, string destination ) {
    if (source == null) throw new ArgumentNullException("source");
    if (destination == null) throw new ArgumentNullException("destination");
    if (File.Exists(destination)) {
        // File.Replace does not work across volumes
        if (Path.GetPathRoot(Path.GetFullPath(source)) == Path.GetPathRoot(Path.GetFullPath(destination))) {
            File.Replace(source, destination, null, true);
        } else {
            File.Copy(source, destination, true);
        }
    } else {
        File.Move(source, destination);
    }
}

Это работает хорошо, пока сервер имеет эксклюзивный доступ к файлам. Однако File.Replace очень чувствителен к внешнему доступу к файлам. В любое время, когда мое программное обеспечение работает в системе с антивирусом или системой резервного копирования в реальном времени, появляются случайные ошибки File.Replace:

System.IO.IOException: невозможно удалить файл для замены.

Вот некоторые возможные причины, которые я устранил:

  • Неидентифицированные дескрипторы файлов: using() гарантирует, что все дескрипторы файлов будут выпущены как можно скорее.
  • Проблемы с потоками: lock() защищает весь доступ к каждому файлу.
  • Различные тома диска. Ошибка File.Replace() при использовании на томах диска. Мой метод проверяет это уже и возвращается к File.Copy().

И вот несколько советов, которые я встретил, и почему я не хочу их использовать:

  • Служба теневого копирования томов. Это работает только в том случае, если проблемное стороннее программное обеспечение (резервные и антивирусные мониторы и т.д.) также использует VSS. Использование VSS требует тонны P/Invoke и имеет проблемы с платформой.
  • Блокировка файлов. В С# блокировка файла требует сохранения FileStream. Он сохранит стороннее программное обеспечение, но 1) я все равно не смогу заменить файл с помощью File.Replace и 2) Как я уже упоминал выше, я бы предпочел сначала написать во временный файл, чтобы избежать случайного коррупция.

Я был бы признателен за любые данные о том, как заставить File.Replace работать каждый раз или, в более общем плане, надежно сохранять/перезаписывать файлы на диске.

Ответ 1

Вы действительно хотите использовать третий параметр - имя файла резервной копии. Это позволяет Windows просто переименовывать исходный файл без необходимости его удаления. Удаление будет неудачно, если какой-либо другой процесс откроет файл без совместного использования, переименование никогда не будет проблемой. Затем вы можете удалить его самостоятельно после вызова Replace() и проигнорировать ошибку. Также удалите его перед вызовом Replace(), чтобы переименование не получилось, и вы очистите неудачные попытки. Так грубо:

string backup = destination + ".bak";
File.Delete(backup);
File.Replace(source, destination, backup, true);
try {
    File.Delete(backup);
}
catch {
    // optional:
    filesToDeleteLater.Add(backup);
}

Ответ 2

Существует несколько возможных подходов: некоторые из них:

  • Используйте файл "lock" - временный файл, созданный до операции, и указывает другим писателям (или читателям), что файл изменяется и, следовательно, исключительно заблокирован. После завершения операции - удалите файл блокировки. Этот метод предполагает, что команда создания файла является атомарной.
  • Использовать API транзакций NTFS (при необходимости).
  • Создайте ссылку на файл, напишите измененный файл под случайным именем (например, Guid.NewGuid()), а затем перетащите ссылку на новый файл. Все читатели получат доступ к файлу по ссылке (имя которой известно).

Конечно, все 3 подхода имеют свои недостатки и преимущества.

Ответ 3

Если программное обеспечение записывает в раздел NTFS, попробуйте использовать Transactional NTFS. Вы можете использовать AlphFS для .NET-оболочки API. Это, вероятно, самый надежный способ записи файлов и предотвращения коррупции.