It was recently pointed out to me that calling fsync()
or fdatasync()
on a new file is not sufficient to ensure it’s persisted to disk, as the existence of the file itself is stored in the directory inode. This means that ensuring that a new file is actually committed also requires an fsync()
on the parent directory. There are lots of discussions about this topic online, e.g. here. This only applies to new files, calling fsync()
on an old file is always fine.
In theory this means that the block index can race block persistence, as a poorly timed power outage could cause us to commit data to the block index that gets lost in the filesystem. The change here ensures that we call fsync()
on the blocks directory after committing new block files. I checked the LevelDB source code and they already do this when updating their writeahead log. In theory this could happen at the same time as a chain split and that could cause you to come back online and then miss the block you had committed to the index, which would put you permanently out of sync between the two. This seems pretty far fetched, but we should handle this case correctly anyway.
I’m using a new autoconf macro as well, AC_CHECK_FUNCS()
. It checks that a function is available and then defines a HAVE_*
macro if it is, analogous to AC_CHECK_HEADERS
. Right now autoscan
complains a lot about the fact that we’re not using this, so I figured I might as well start now while I was in this part of the code.
Apparently Windows doesn’t have an similar method of syncing filesystem metadata—I’m not an expert on that though.
Also not strictly related to this change, but I have been working on a lot of platform-specific PRs recently and want to refactor util.h
and util.cpp
so the platform-specific bits are isolated from the generic util stuff. I intend to create an issue later today to describe how I think that should be done so I can get feedback before starting that work.