mirror of
https://github.com/facebook/rocksdb.git
synced 2026-07-07 14:47:40 +08:00
b5922eab11
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/14739 Adds public path-level file sync APIs to RocksDB so callers can ask `Env` or `FileSystem` to sync a named file without hand-rolled reopen logic. This diff: - Adds public `Env::SyncFile` and `FileSystem::SyncFile` APIs for syncing or fsyncing a file by name without requiring callers to reopen it directly. - Documents the `SyncFile` contract: filesystems may override it as a no-op when flush/close already provides durability, and RocksDB callers should use it instead of hand-rolled `ReopenWritableFile()+Sync()/Fsync()` so filesystems can reject post-close data-write reopen paths. - Implements the default `Env` and `FileSystem` behavior by reopening the file as writable, calling `Sync` or `Fsync`, closing the handle, returning the sync/fsync error ahead of a close error when both fail, and explicitly consuming the close status on the sync-failure path. - Wires `SyncFile` through the Env/FileSystem bridge and wrapper layers, including `EnvWrapper`, `FileSystemWrapper`, `CompositeEnvWrapper`, `EncryptedFileSystem`, `RemapFileSystem`, read-only file systems, mock file systems, and fault-injection wrappers. - Keeps fault-injection `SyncFile` overrides on the base default implementation so the operation still exercises the wrapper's `ReopenWritableFile`, wrapped-file `Sync`/`Fsync`, and `Close` hooks instead of bypassing them through target forwarding. - Updates external SST ingestion to call `FileSystem::SyncFile` instead of manually reopening an ingested file as writable, while preserving the `NotSupported` skip behavior and failure logging. - Adds release-note coverage for the new public API and unit coverage for default `Env`/`FileSystem` success, reopen failure, close failure, sync-versus-close failure precedence, and external SST sync behavior. There was an earlier version of this API which got reverted (PR #13987) due to internal change broken. Re-apply the change. The internal issue will be resolved in next release. Reviewed By: pdillinger Differential Revision: D104918547 fbshipit-source-id: 3f8d2d127fc962b68b423bbb90de551fe6706224
363 lines
15 KiB
C++
363 lines
15 KiB
C++
// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
|
// This source code is licensed under both the GPLv2 (found in the
|
|
// COPYING file in the root directory) and Apache 2.0 License
|
|
// (found in the LICENSE.Apache file in the root directory).
|
|
|
|
#include "env/fs_remap.h"
|
|
|
|
namespace ROCKSDB_NAMESPACE {
|
|
|
|
RemapFileSystem::RemapFileSystem(const std::shared_ptr<FileSystem>& base)
|
|
: FileSystemWrapper(base) {}
|
|
|
|
std::pair<IOStatus, std::string> RemapFileSystem::EncodePathWithNewBasename(
|
|
const std::string& path) {
|
|
// No difference by default
|
|
return EncodePath(path);
|
|
}
|
|
|
|
Status RemapFileSystem::RegisterDbPaths(const std::vector<std::string>& paths) {
|
|
std::vector<std::string> encoded_paths;
|
|
encoded_paths.reserve(paths.size());
|
|
for (auto& path : paths) {
|
|
auto status_and_enc_path = EncodePathWithNewBasename(path);
|
|
if (!status_and_enc_path.first.ok()) {
|
|
return status_and_enc_path.first;
|
|
}
|
|
encoded_paths.emplace_back(status_and_enc_path.second);
|
|
}
|
|
return FileSystemWrapper::RegisterDbPaths(encoded_paths);
|
|
}
|
|
|
|
Status RemapFileSystem::UnregisterDbPaths(
|
|
const std::vector<std::string>& paths) {
|
|
std::vector<std::string> encoded_paths;
|
|
encoded_paths.reserve(paths.size());
|
|
for (auto& path : paths) {
|
|
auto status_and_enc_path = EncodePathWithNewBasename(path);
|
|
if (!status_and_enc_path.first.ok()) {
|
|
return status_and_enc_path.first;
|
|
}
|
|
encoded_paths.emplace_back(status_and_enc_path.second);
|
|
}
|
|
return FileSystemWrapper::UnregisterDbPaths(encoded_paths);
|
|
}
|
|
|
|
IOStatus RemapFileSystem::NewSequentialFile(
|
|
const std::string& fname, const FileOptions& options,
|
|
std::unique_ptr<FSSequentialFile>* result, IODebugContext* dbg) {
|
|
auto status_and_enc_path = EncodePathWithNewBasename(fname);
|
|
if (!status_and_enc_path.first.ok()) {
|
|
return status_and_enc_path.first;
|
|
}
|
|
return FileSystemWrapper::NewSequentialFile(status_and_enc_path.second,
|
|
options, result, dbg);
|
|
}
|
|
|
|
IOStatus RemapFileSystem::NewRandomAccessFile(
|
|
const std::string& fname, const FileOptions& options,
|
|
std::unique_ptr<FSRandomAccessFile>* result, IODebugContext* dbg) {
|
|
auto status_and_enc_path = EncodePathWithNewBasename(fname);
|
|
if (!status_and_enc_path.first.ok()) {
|
|
return status_and_enc_path.first;
|
|
}
|
|
return FileSystemWrapper::NewRandomAccessFile(status_and_enc_path.second,
|
|
options, result, dbg);
|
|
}
|
|
|
|
IOStatus RemapFileSystem::NewWritableFile(
|
|
const std::string& fname, const FileOptions& options,
|
|
std::unique_ptr<FSWritableFile>* result, IODebugContext* dbg) {
|
|
auto status_and_enc_path = EncodePathWithNewBasename(fname);
|
|
if (!status_and_enc_path.first.ok()) {
|
|
return status_and_enc_path.first;
|
|
}
|
|
return FileSystemWrapper::NewWritableFile(status_and_enc_path.second, options,
|
|
result, dbg);
|
|
}
|
|
|
|
IOStatus RemapFileSystem::ReopenWritableFile(
|
|
const std::string& fname, const FileOptions& options,
|
|
std::unique_ptr<FSWritableFile>* result, IODebugContext* dbg) {
|
|
auto status_and_enc_path = EncodePathWithNewBasename(fname);
|
|
if (!status_and_enc_path.first.ok()) {
|
|
return status_and_enc_path.first;
|
|
}
|
|
return FileSystemWrapper::ReopenWritableFile(status_and_enc_path.second,
|
|
options, result, dbg);
|
|
}
|
|
|
|
IOStatus RemapFileSystem::ReuseWritableFile(
|
|
const std::string& fname, const std::string& old_fname,
|
|
const FileOptions& options, std::unique_ptr<FSWritableFile>* result,
|
|
IODebugContext* dbg) {
|
|
auto status_and_enc_path = EncodePathWithNewBasename(fname);
|
|
if (!status_and_enc_path.first.ok()) {
|
|
return status_and_enc_path.first;
|
|
}
|
|
auto status_and_old_enc_path = EncodePath(old_fname);
|
|
if (!status_and_old_enc_path.first.ok()) {
|
|
return status_and_old_enc_path.first;
|
|
}
|
|
return FileSystemWrapper::ReuseWritableFile(status_and_old_enc_path.second,
|
|
status_and_old_enc_path.second,
|
|
options, result, dbg);
|
|
}
|
|
|
|
IOStatus RemapFileSystem::NewRandomRWFile(
|
|
const std::string& fname, const FileOptions& options,
|
|
std::unique_ptr<FSRandomRWFile>* result, IODebugContext* dbg) {
|
|
auto status_and_enc_path = EncodePathWithNewBasename(fname);
|
|
if (!status_and_enc_path.first.ok()) {
|
|
return status_and_enc_path.first;
|
|
}
|
|
return FileSystemWrapper::NewRandomRWFile(status_and_enc_path.second, options,
|
|
result, dbg);
|
|
}
|
|
|
|
IOStatus RemapFileSystem::NewDirectory(const std::string& dir,
|
|
const IOOptions& options,
|
|
std::unique_ptr<FSDirectory>* result,
|
|
IODebugContext* dbg) {
|
|
// A hassle to remap DirFsyncOptions::renamed_new_name
|
|
class RemapFSDirectory : public FSDirectoryWrapper {
|
|
public:
|
|
RemapFSDirectory(RemapFileSystem* fs, std::unique_ptr<FSDirectory>&& t)
|
|
: FSDirectoryWrapper(std::move(t)), fs_(fs) {}
|
|
IOStatus FsyncWithDirOptions(
|
|
const IOOptions& options, IODebugContext* dbg,
|
|
const DirFsyncOptions& dir_fsync_options) override {
|
|
if (dir_fsync_options.renamed_new_name.empty()) {
|
|
return FSDirectoryWrapper::FsyncWithDirOptions(options, dbg,
|
|
dir_fsync_options);
|
|
} else {
|
|
auto status_and_enc_path =
|
|
fs_->EncodePath(dir_fsync_options.renamed_new_name);
|
|
if (status_and_enc_path.first.ok()) {
|
|
DirFsyncOptions mapped_options = dir_fsync_options;
|
|
mapped_options.renamed_new_name = status_and_enc_path.second;
|
|
return FSDirectoryWrapper::FsyncWithDirOptions(options, dbg,
|
|
mapped_options);
|
|
} else {
|
|
return status_and_enc_path.first;
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
RemapFileSystem* const fs_;
|
|
};
|
|
|
|
auto status_and_enc_path = EncodePathWithNewBasename(dir);
|
|
if (!status_and_enc_path.first.ok()) {
|
|
return status_and_enc_path.first;
|
|
}
|
|
IOStatus ios = FileSystemWrapper::NewDirectory(status_and_enc_path.second,
|
|
options, result, dbg);
|
|
if (ios.ok()) {
|
|
*result = std::make_unique<RemapFSDirectory>(this, std::move(*result));
|
|
}
|
|
return ios;
|
|
}
|
|
|
|
IOStatus RemapFileSystem::FileExists(const std::string& fname,
|
|
const IOOptions& options,
|
|
IODebugContext* dbg) {
|
|
auto status_and_enc_path = EncodePathWithNewBasename(fname);
|
|
if (!status_and_enc_path.first.ok()) {
|
|
return status_and_enc_path.first;
|
|
}
|
|
return FileSystemWrapper::FileExists(status_and_enc_path.second, options,
|
|
dbg);
|
|
}
|
|
|
|
IOStatus RemapFileSystem::GetChildren(const std::string& dir,
|
|
const IOOptions& options,
|
|
std::vector<std::string>* result,
|
|
IODebugContext* dbg) {
|
|
auto status_and_enc_path = EncodePath(dir);
|
|
if (!status_and_enc_path.first.ok()) {
|
|
return status_and_enc_path.first;
|
|
}
|
|
return FileSystemWrapper::GetChildren(status_and_enc_path.second, options,
|
|
result, dbg);
|
|
}
|
|
|
|
IOStatus RemapFileSystem::GetChildrenFileAttributes(
|
|
const std::string& dir, const IOOptions& options,
|
|
std::vector<FileAttributes>* result, IODebugContext* dbg) {
|
|
auto status_and_enc_path = EncodePath(dir);
|
|
if (!status_and_enc_path.first.ok()) {
|
|
return status_and_enc_path.first;
|
|
}
|
|
return FileSystemWrapper::GetChildrenFileAttributes(
|
|
status_and_enc_path.second, options, result, dbg);
|
|
}
|
|
|
|
IOStatus RemapFileSystem::DeleteFile(const std::string& fname,
|
|
const IOOptions& options,
|
|
IODebugContext* dbg) {
|
|
auto status_and_enc_path = EncodePath(fname);
|
|
if (!status_and_enc_path.first.ok()) {
|
|
return status_and_enc_path.first;
|
|
}
|
|
return FileSystemWrapper::DeleteFile(status_and_enc_path.second, options,
|
|
dbg);
|
|
}
|
|
|
|
IOStatus RemapFileSystem::CreateDir(const std::string& dirname,
|
|
const IOOptions& options,
|
|
IODebugContext* dbg) {
|
|
auto status_and_enc_path = EncodePathWithNewBasename(dirname);
|
|
if (!status_and_enc_path.first.ok()) {
|
|
return status_and_enc_path.first;
|
|
}
|
|
return FileSystemWrapper::CreateDir(status_and_enc_path.second, options, dbg);
|
|
}
|
|
|
|
IOStatus RemapFileSystem::CreateDirIfMissing(const std::string& dirname,
|
|
const IOOptions& options,
|
|
IODebugContext* dbg) {
|
|
auto status_and_enc_path = EncodePathWithNewBasename(dirname);
|
|
if (!status_and_enc_path.first.ok()) {
|
|
return status_and_enc_path.first;
|
|
}
|
|
return FileSystemWrapper::CreateDirIfMissing(status_and_enc_path.second,
|
|
options, dbg);
|
|
}
|
|
|
|
IOStatus RemapFileSystem::DeleteDir(const std::string& dirname,
|
|
const IOOptions& options,
|
|
IODebugContext* dbg) {
|
|
auto status_and_enc_path = EncodePath(dirname);
|
|
if (!status_and_enc_path.first.ok()) {
|
|
return status_and_enc_path.first;
|
|
}
|
|
return FileSystemWrapper::DeleteDir(status_and_enc_path.second, options, dbg);
|
|
}
|
|
|
|
IOStatus RemapFileSystem::GetFileSize(const std::string& fname,
|
|
const IOOptions& options,
|
|
uint64_t* file_size,
|
|
IODebugContext* dbg) {
|
|
auto status_and_enc_path = EncodePath(fname);
|
|
if (!status_and_enc_path.first.ok()) {
|
|
return status_and_enc_path.first;
|
|
}
|
|
return FileSystemWrapper::GetFileSize(status_and_enc_path.second, options,
|
|
file_size, dbg);
|
|
}
|
|
|
|
IOStatus RemapFileSystem::GetFileModificationTime(const std::string& fname,
|
|
const IOOptions& options,
|
|
uint64_t* file_mtime,
|
|
IODebugContext* dbg) {
|
|
auto status_and_enc_path = EncodePath(fname);
|
|
if (!status_and_enc_path.first.ok()) {
|
|
return status_and_enc_path.first;
|
|
}
|
|
return FileSystemWrapper::GetFileModificationTime(status_and_enc_path.second,
|
|
options, file_mtime, dbg);
|
|
}
|
|
|
|
IOStatus RemapFileSystem::IsDirectory(const std::string& path,
|
|
const IOOptions& options, bool* is_dir,
|
|
IODebugContext* dbg) {
|
|
auto status_and_enc_path = EncodePath(path);
|
|
if (!status_and_enc_path.first.ok()) {
|
|
return status_and_enc_path.first;
|
|
}
|
|
return FileSystemWrapper::IsDirectory(status_and_enc_path.second, options,
|
|
is_dir, dbg);
|
|
}
|
|
|
|
IOStatus RemapFileSystem::RenameFile(const std::string& src,
|
|
const std::string& dest,
|
|
const IOOptions& options,
|
|
IODebugContext* dbg) {
|
|
auto status_and_src_enc_path = EncodePath(src);
|
|
if (!status_and_src_enc_path.first.ok()) {
|
|
if (status_and_src_enc_path.first.IsNotFound()) {
|
|
const IOStatus& s = status_and_src_enc_path.first;
|
|
status_and_src_enc_path.first = IOStatus::PathNotFound(s.ToString());
|
|
}
|
|
return status_and_src_enc_path.first;
|
|
}
|
|
auto status_and_dest_enc_path = EncodePathWithNewBasename(dest);
|
|
if (!status_and_dest_enc_path.first.ok()) {
|
|
return status_and_dest_enc_path.first;
|
|
}
|
|
return FileSystemWrapper::RenameFile(status_and_src_enc_path.second,
|
|
status_and_dest_enc_path.second, options,
|
|
dbg);
|
|
}
|
|
|
|
IOStatus RemapFileSystem::LinkFile(const std::string& src,
|
|
const std::string& dest,
|
|
const IOOptions& options,
|
|
IODebugContext* dbg) {
|
|
auto status_and_src_enc_path = EncodePath(src);
|
|
if (!status_and_src_enc_path.first.ok()) {
|
|
return status_and_src_enc_path.first;
|
|
}
|
|
auto status_and_dest_enc_path = EncodePathWithNewBasename(dest);
|
|
if (!status_and_dest_enc_path.first.ok()) {
|
|
return status_and_dest_enc_path.first;
|
|
}
|
|
return FileSystemWrapper::LinkFile(status_and_src_enc_path.second,
|
|
status_and_dest_enc_path.second, options,
|
|
dbg);
|
|
}
|
|
|
|
IOStatus RemapFileSystem::SyncFile(const std::string& fname,
|
|
const FileOptions& file_opts,
|
|
const IOOptions& io_opts, bool use_fsync,
|
|
IODebugContext* dbg) {
|
|
auto status_and_enc_path = EncodePathWithNewBasename(fname);
|
|
if (!status_and_enc_path.first.ok()) {
|
|
return status_and_enc_path.first;
|
|
}
|
|
return FileSystemWrapper::SyncFile(status_and_enc_path.second, file_opts,
|
|
io_opts, use_fsync, dbg);
|
|
}
|
|
|
|
IOStatus RemapFileSystem::LockFile(const std::string& fname,
|
|
const IOOptions& options, FileLock** lock,
|
|
IODebugContext* dbg) {
|
|
auto status_and_enc_path = EncodePathWithNewBasename(fname);
|
|
if (!status_and_enc_path.first.ok()) {
|
|
return status_and_enc_path.first;
|
|
}
|
|
// FileLock subclasses may store path (e.g., PosixFileLock stores it). We
|
|
// can skip stripping the chroot directory from this path because callers
|
|
// shouldn't use it.
|
|
return FileSystemWrapper::LockFile(status_and_enc_path.second, options, lock,
|
|
dbg);
|
|
}
|
|
|
|
IOStatus RemapFileSystem::NewLogger(const std::string& fname,
|
|
const IOOptions& options,
|
|
std::shared_ptr<Logger>* result,
|
|
IODebugContext* dbg) {
|
|
auto status_and_enc_path = EncodePathWithNewBasename(fname);
|
|
if (!status_and_enc_path.first.ok()) {
|
|
return status_and_enc_path.first;
|
|
}
|
|
return FileSystemWrapper::NewLogger(status_and_enc_path.second, options,
|
|
result, dbg);
|
|
}
|
|
|
|
IOStatus RemapFileSystem::GetAbsolutePath(const std::string& db_path,
|
|
const IOOptions& options,
|
|
std::string* output_path,
|
|
IODebugContext* dbg) {
|
|
auto status_and_enc_path = EncodePathWithNewBasename(db_path);
|
|
if (!status_and_enc_path.first.ok()) {
|
|
return status_and_enc_path.first;
|
|
}
|
|
return FileSystemWrapper::GetAbsolutePath(status_and_enc_path.second, options,
|
|
output_path, dbg);
|
|
}
|
|
|
|
} // namespace ROCKSDB_NAMESPACE
|