1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-07-05 16:31:47 +02:00

Merge pull request #7126 from squalus/fsync-store-paths

Add fsync-store-paths option
This commit is contained in:
Eelco Dolstra 2024-08-22 17:45:11 +02:00 committed by GitHub
commit 1facc3e35e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 173 additions and 13 deletions

View file

@ -294,9 +294,9 @@ void parseDump(FileSystemObjectSink & sink, Source & source)
}
void restorePath(const std::filesystem::path & path, Source & source)
void restorePath(const std::filesystem::path & path, Source & source, bool startFsync)
{
RestoreSink sink;
RestoreSink sink{startFsync};
sink.dstPath = path;
parseDump(sink, source);
}

View file

@ -75,7 +75,7 @@ void dumpString(std::string_view s, Sink & sink);
void parseDump(FileSystemObjectSink & sink, Source & source);
void restorePath(const std::filesystem::path & path, Source & source);
void restorePath(const std::filesystem::path & path, Source & source, bool startFsync = false);
/**
* Read a NAR from 'source' and write it to 'sink'.

View file

@ -88,14 +88,15 @@ void dumpPath(
void restorePath(
const Path & path,
Source & source,
FileSerialisationMethod method)
FileSerialisationMethod method,
bool startFsync)
{
switch (method) {
case FileSerialisationMethod::Flat:
writeFile(path, source);
writeFile(path, source, 0666, startFsync);
break;
case FileSerialisationMethod::NixArchive:
restorePath(path, source);
restorePath(path, source, startFsync);
break;
}
}

View file

@ -70,7 +70,8 @@ void dumpPath(
void restorePath(
const Path & path,
Source & source,
FileSerialisationMethod method);
FileSerialisationMethod method,
bool startFsync = false);
/**

View file

@ -92,7 +92,7 @@ void AutoCloseFD::close()
}
}
void AutoCloseFD::fsync()
void AutoCloseFD::fsync() const
{
if (fd != INVALID_DESCRIPTOR) {
int result;
@ -111,6 +111,18 @@ void AutoCloseFD::fsync()
}
void AutoCloseFD::startFsync() const
{
#if __linux__
if (fd != -1) {
/* Ignore failure, since fsync must be run later anyway. This is just a performance optimization. */
::sync_file_range(fd, 0, 0, SYNC_FILE_RANGE_WRITE);
}
#endif
}
AutoCloseFD::operator bool() const
{
return fd != INVALID_DESCRIPTOR;

View file

@ -128,7 +128,18 @@ public:
explicit operator bool() const;
Descriptor release();
void close();
void fsync();
/**
* Perform a blocking fsync operation.
*/
void fsync() const;
/**
* Asynchronously flush to disk without blocking, if available on
* the platform. This is just a performance optimization, and
* fsync must be run later even if this is called.
*/
void startFsync() const;
};
class Pipe

View file

@ -11,6 +11,7 @@
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <deque>
#include <sstream>
#include <filesystem>
@ -318,6 +319,50 @@ void syncParent(const Path & path)
}
void recursiveSync(const Path & path)
{
/* If it's a file, just fsync and return. */
auto st = lstat(path);
if (S_ISREG(st.st_mode)) {
AutoCloseFD fd = open(path.c_str(), O_RDONLY, 0);
if (!fd)
throw SysError("opening file '%1%'", path);
fd.fsync();
return;
}
/* Otherwise, perform a depth-first traversal of the directory and
fsync all the files. */
std::deque<fs::path> dirsToEnumerate;
dirsToEnumerate.push_back(path);
std::vector<fs::path> dirsToFsync;
while (!dirsToEnumerate.empty()) {
auto currentDir = dirsToEnumerate.back();
dirsToEnumerate.pop_back();
for (auto & entry : std::filesystem::directory_iterator(currentDir)) {
auto st = entry.symlink_status();
if (fs::is_directory(st)) {
dirsToEnumerate.emplace_back(entry.path());
} else if (fs::is_regular_file(st)) {
AutoCloseFD fd = open(entry.path().c_str(), O_RDONLY, 0);
if (!fd)
throw SysError("opening file '%1%'", entry.path());
fd.fsync();
}
}
dirsToFsync.emplace_back(std::move(currentDir));
}
/* Fsync all the directories. */
for (auto dir = dirsToFsync.rbegin(); dir != dirsToFsync.rend(); ++dir) {
AutoCloseFD fd = open(dir->c_str(), O_RDONLY, 0);
if (!fd)
throw SysError("opening directory '%1%'", *dir);
fd.fsync();
}
}
static void _deletePath(Descriptor parentfd, const fs::path & path, uint64_t & bytesFreed)
{
#ifndef _WIN32

View file

@ -134,10 +134,15 @@ void writeFile(const Path & path, std::string_view s, mode_t mode = 0666, bool s
void writeFile(const Path & path, Source & source, mode_t mode = 0666, bool sync = false);
/**
* Flush a file's parent directory to disk
* Flush a path's parent directory to disk.
*/
void syncParent(const Path & path);
/**
* Flush a file or entire directory tree to disk.
*/
void recursiveSync(const Path & path);
/**
* Delete a path; i.e., in the case of a directory, it is deleted
* recursively. It's not an error if the path does not exist. The

View file

@ -76,6 +76,17 @@ void RestoreSink::createDirectory(const CanonPath & path)
struct RestoreRegularFile : CreateRegularFileSink {
AutoCloseFD fd;
bool startFsync = false;
~RestoreRegularFile()
{
/* Initiate an fsync operation without waiting for the
result. The real fsync should be run before registering a
store path, but this is a performance optimization to allow
the disk write to start early. */
if (fd && startFsync)
fd.startFsync();
}
void operator () (std::string_view data) override;
void isExecutable() override;
@ -95,6 +106,7 @@ void RestoreSink::createRegularFile(const CanonPath & path, std::function<void(C
auto p = append(dstPath, path);
RestoreRegularFile crf;
crf.startFsync = startFsync;
crf.fd =
#ifdef _WIN32
CreateFileW(p.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)

View file

@ -78,6 +78,11 @@ struct NullFileSystemObjectSink : FileSystemObjectSink
struct RestoreSink : FileSystemObjectSink
{
std::filesystem::path dstPath;
bool startFsync = false;
explicit RestoreSink(bool startFsync)
: startFsync{startFsync}
{ }
void createDirectory(const CanonPath & path) override;