Skip to content

writeFileAtomic retry strategy could be improved with threadDelay #10938

Open
@Bodigrim

Description

@Bodigrim

writeFileAtomic :: FilePath -> LBS.ByteString -> IO ()
writeFileAtomic targetPath content = do
let targetFile = takeFileName targetPath
tmpDir <- getTemporaryDirectory
Exception.bracketOnError
(openBinaryTempFileWithDefaultPermissions tmpDir $ targetFile <.> "tmp")
(\(tmpPath, handle) -> hClose handle >> removeFile tmpPath)
( \(tmpPath, handle) -> do
LBS.hPut handle content
hClose handle
Exception.catch
(renameFile tmpPath targetPath)
( \(_ :: Exception.SomeException) -> do
copyFile tmpPath targetPath
removeFile tmpPath
)
)

So if renameFile failed we immediately try copyFile followed by removeFile. My experience however is that this is not helpful, at least on Windows. The most common reason for renameFile to fail is because another process (typically, an antivirus) opened the source file for reading. If this is the case then copyFile is likely to be too short delay for removeFile to succeed, because the source file remains locked by antivirus.

To make it worse, copyFile from directory internally makes its own attempt at atomicity, creating a temporary file and renaming it, which triggers the antivirus yet again.

In short, if renameFile fails then there is no reason to expect copyFile / removeFile to succeed.

I'd suggest to scrap copyFile / removeFile catch: just keep retrying renameFile after threadDelay 10000 a few times more.

CC @GulinSS who was looking at writeFileAtomic recently.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions