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:
commit
1facc3e35e
15 changed files with 173 additions and 13 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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'.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,7 +70,8 @@ void dumpPath(
|
|||
void restorePath(
|
||||
const Path & path,
|
||||
Source & source,
|
||||
FileSerialisationMethod method);
|
||||
FileSerialisationMethod method,
|
||||
bool startFsync = false);
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue