This is a small Java library for crash-safe file I/O. The procedures here offer much stronger guarantees than the standard library routines, but incur more I/O performance overhead.
This library is not currently available from any major repos, but it has no run-time dependencies and is easy to build from source:
$ git clone https://github.com/Calvin-L/crash-safe-io.git
$ gradle build
$ ls build/libs/crash-safe-io-*.jar
To build the JavaDocs, run
$ gradle javadoc
and then open build/docs/javadoc/index.html
.
See also: the "Important functionality in this library" section below.
Atomicity and durability are the essence of crash safety:
- Atomicity means that there are only two observable outcomes: either the operation either happens in its entirety or the operation does not happen at all. Even if the system crashes during the operation, there will be no corrupt data or partial effects.
- Durability means that the effect is saved to permanent storage and will survive a subsequent power outage.
The standard library routines are neither atomic nor durable. For example,
Files.write(path, data)
is not
atomic, since if the system crashes it may have only written some of the desired
data to the file. It is also not durable, since if the system crashes shortly
after it returns, the written file may disappear if it was only written to a
cache and was not flushed to the physical hard drive.
DurableIOUtil
contains static methods for common filesystem operations:write(path, data)
writes a file atomically and durablycreateDirectories(path)
durably creates a directory and all intermediate directoriesatomicallyDelete(path)
atomically and durably deletes an entire subtreemove(source, target)
atomically and durably renames the source file to the target file
AtomicDurableOutputStream
is similar toFileOutputStream
, but only creates its file atomically and durably whencommit()
is called.
Do not use the filesystem for thread synchronization. It is possible for
threads and processes on the machine to see the effects of calls to this
library before they have been durably saved. For instance, if thread A calls
DurableIOUtil.write(...)
, then thread B might see the new file before thread
A has made the creation durable. It is dangerous for thread B to act as if
the file has been durably created, even though it is observable. Instead,
thread B must wait until the call made by thread A has completely returned,
which requires a different form of thread synchronization.
This library achieves the best guarantees possible using only the Java standard library. On some systems there may be machine-dependent techniques that further improve the guarantees.