Merge #13039: Add logging and error handling for file syncing
cf02779
Add logging and error handling for file syncing (Wladimir J. van der Laan)
Pull request description:
Add logging and error handling inside, and outside of FileCommit.
Functions such as fsync, fdatasync will return error in case of hardware I/O errors, and ignoring this means it can silently continue through data corruption.
(c.f. https://lwn.net/SubscriberLink/752063/12b232ab5039efbe/)
EINVAL is handled specially to avoid crashing out on (network, fuse) filesystems that don't handle `f[data]sync`.
I checked that the syncing inside leveldb is already generating an I/O error as appropriate.
Tree-SHA512: 64cc9bbedca3ecc97ff4bac0a7b7ac6526a7ed763c66f6786d03ca4f2e9e366e42b152cb908299c060448d98ca39ff03395280bffaca51d592e728aa2516f5dd
This commit is contained in:
commit
8b4081a889
4 changed files with 36 additions and 13 deletions
|
@ -49,7 +49,8 @@ bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data
|
||||||
|
|
||||||
// Serialize
|
// Serialize
|
||||||
if (!SerializeDB(fileout, data)) return false;
|
if (!SerializeDB(fileout, data)) return false;
|
||||||
FileCommit(fileout.Get());
|
if (!FileCommit(fileout.Get()))
|
||||||
|
return error("%s: Failed to flush file %s", __func__, pathTmp.string());
|
||||||
fileout.fclose();
|
fileout.fclose();
|
||||||
|
|
||||||
// replace existing file, if any, with new file
|
// replace existing file, if any, with new file
|
||||||
|
|
28
src/util.cpp
28
src/util.cpp
|
@ -788,21 +788,37 @@ bool TryCreateDirectories(const fs::path& p)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileCommit(FILE *file)
|
bool FileCommit(FILE *file)
|
||||||
{
|
{
|
||||||
fflush(file); // harmless if redundantly called
|
if (fflush(file) != 0) { // harmless if redundantly called
|
||||||
|
LogPrintf("%s: fflush failed: %d\n", __func__, errno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
|
HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
|
||||||
FlushFileBuffers(hFile);
|
if (FlushFileBuffers(hFile) == 0) {
|
||||||
|
LogPrintf("%s: FlushFileBuffers failed: %d\n", __func__, GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
#if defined(__linux__) || defined(__NetBSD__)
|
#if defined(__linux__) || defined(__NetBSD__)
|
||||||
fdatasync(fileno(file));
|
if (fdatasync(fileno(file)) != 0 && errno != EINVAL) { // Ignore EINVAL for filesystems that don't support sync
|
||||||
|
LogPrintf("%s: fdatasync failed: %d\n", __func__, errno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
#elif defined(__APPLE__) && defined(F_FULLFSYNC)
|
#elif defined(__APPLE__) && defined(F_FULLFSYNC)
|
||||||
fcntl(fileno(file), F_FULLFSYNC, 0);
|
if (fcntl(fileno(file), F_FULLFSYNC, 0) == -1) { // Manpage says "value other than -1" is returned on success
|
||||||
|
LogPrintf("%s: fcntl F_FULLFSYNC failed: %d\n", __func__, errno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
fsync(fileno(file));
|
if (fsync(fileno(file)) != 0 && errno != EINVAL) {
|
||||||
|
LogPrintf("%s: fsync failed: %d\n", __func__, errno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TruncateFile(FILE *file, unsigned int length) {
|
bool TruncateFile(FILE *file, unsigned int length) {
|
||||||
|
|
|
@ -71,7 +71,7 @@ bool error(const char* fmt, const Args&... args)
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintExceptionContinue(const std::exception *pex, const char* pszThread);
|
void PrintExceptionContinue(const std::exception *pex, const char* pszThread);
|
||||||
void FileCommit(FILE *file);
|
bool FileCommit(FILE *file);
|
||||||
bool TruncateFile(FILE *file, unsigned int length);
|
bool TruncateFile(FILE *file, unsigned int length);
|
||||||
int RaiseFileDescriptorLimit(int nMinFD);
|
int RaiseFileDescriptorLimit(int nMinFD);
|
||||||
void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length);
|
void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length);
|
||||||
|
|
|
@ -1615,22 +1615,27 @@ void static FlushBlockFile(bool fFinalize = false)
|
||||||
LOCK(cs_LastBlockFile);
|
LOCK(cs_LastBlockFile);
|
||||||
|
|
||||||
CDiskBlockPos posOld(nLastBlockFile, 0);
|
CDiskBlockPos posOld(nLastBlockFile, 0);
|
||||||
|
bool status = true;
|
||||||
|
|
||||||
FILE *fileOld = OpenBlockFile(posOld);
|
FILE *fileOld = OpenBlockFile(posOld);
|
||||||
if (fileOld) {
|
if (fileOld) {
|
||||||
if (fFinalize)
|
if (fFinalize)
|
||||||
TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nSize);
|
status &= TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nSize);
|
||||||
FileCommit(fileOld);
|
status &= FileCommit(fileOld);
|
||||||
fclose(fileOld);
|
fclose(fileOld);
|
||||||
}
|
}
|
||||||
|
|
||||||
fileOld = OpenUndoFile(posOld);
|
fileOld = OpenUndoFile(posOld);
|
||||||
if (fileOld) {
|
if (fileOld) {
|
||||||
if (fFinalize)
|
if (fFinalize)
|
||||||
TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nUndoSize);
|
status &= TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nUndoSize);
|
||||||
FileCommit(fileOld);
|
status &= FileCommit(fileOld);
|
||||||
fclose(fileOld);
|
fclose(fileOld);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!status) {
|
||||||
|
AbortNode("Flushing block file to disk failed. This is likely the result of an I/O error.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize);
|
static bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize);
|
||||||
|
@ -4760,7 +4765,8 @@ bool DumpMempool(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
file << mapDeltas;
|
file << mapDeltas;
|
||||||
FileCommit(file.Get());
|
if (!FileCommit(file.Get()))
|
||||||
|
throw std::runtime_error("FileCommit failed");
|
||||||
file.fclose();
|
file.fclose();
|
||||||
RenameOver(GetDataDir() / "mempool.dat.new", GetDataDir() / "mempool.dat");
|
RenameOver(GetDataDir() / "mempool.dat.new", GetDataDir() / "mempool.dat");
|
||||||
int64_t last = GetTimeMicros();
|
int64_t last = GetTimeMicros();
|
||||||
|
|
Loading…
Reference in a new issue