diff options
| author | Stefan Boberg <[email protected]> | 2021-05-13 17:37:32 +0200 |
|---|---|---|
| committer | Stefan Boberg <[email protected]> | 2021-05-13 17:37:32 +0200 |
| commit | 5d46e91942d81f6ca4d12225757e633013408369 (patch) | |
| tree | d14316239f40078a91cba3d867dddc2b6bec6e2b /zenstore/filecas.cpp | |
| parent | Added string_view variant of WindowsException constructor (diff) | |
| download | zen-5d46e91942d81f6ca4d12225757e633013408369.tar.xz zen-5d46e91942d81f6ca4d12225757e633013408369.zip | |
Implemented move-in-place for large CAS payloads
Diffstat (limited to 'zenstore/filecas.cpp')
| -rw-r--r-- | zenstore/filecas.cpp | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/zenstore/filecas.cpp b/zenstore/filecas.cpp index 84a06c3be..0011ddec6 100644 --- a/zenstore/filecas.cpp +++ b/zenstore/filecas.cpp @@ -3,11 +3,14 @@ #include "FileCas.h" #include <zencore/except.h> +#include <zencore/filesystem.h> +#include <zencore/fmtutils.h> #include <zencore/memory.h> #include <zencore/string.h> #include <zencore/thread.h> #include <zencore/uid.h> +#include <spdlog/spdlog.h> #include <gsl/gsl-lite.hpp> #include <functional> @@ -25,6 +28,8 @@ struct IUnknown; // Workaround for "combaseapi.h(229): error C2187: syntax erro namespace zen { +using namespace fmt::literals; + WideStringBuilderBase& FileCasStrategy::MakeShardedPath(WideStringBuilderBase& ShardedPath, const IoHash& ChunkHash, size_t& OutShard2len) { @@ -58,6 +63,109 @@ FileCasStrategy::MakeShardedPath(WideStringBuilderBase& ShardedPath, const IoHas CasStore::InsertResult FileCasStrategy::InsertChunk(IoBuffer Chunk, const IoHash& ChunkHash) { + IoBufferFileReference FileRef; + if (Chunk.GetFileReference(/* out */ FileRef)) + { + size_t Shard2len = 0; + ExtendableWideStringBuilder<128> ShardedPath; + ShardedPath.Append(m_Config.RootDirectory.c_str()); + ShardedPath.Append(std::filesystem::path::preferred_separator); + MakeShardedPath(ShardedPath, ChunkHash, /* out */ Shard2len); + + auto DeletePayloadFileOnClose = [&] { + FILE_DISPOSITION_INFO Fdi{}; + Fdi.DeleteFile = TRUE; + BOOL Success = SetFileInformationByHandle(FileRef.FileHandle, FileDispositionInfo, &Fdi, sizeof Fdi); + + if (!Success) + { + spdlog::warn("Failed to flag temporary payload file for deletion: '{}'", PathFromHandle(FileRef.FileHandle)); + } + }; + + // See if file already exists + // + // Future improvement: maintain Bloom filter to avoid expensive file system probes? + + { + CAtlFile PayloadFile; + + HRESULT hRes = PayloadFile.Create(ShardedPath.c_str(), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING); + + if (SUCCEEDED(hRes)) + { + // If we succeeded in opening the file then we don't need to do anything else because it already exists and should contain + // the content we were about to insert + + // We do need to ensure the file goes away on close, however + + DeletePayloadFileOnClose(); + + return CasStore::InsertResult{.New = false}; + } + } + + std::filesystem::path FullPath(ShardedPath.c_str()); + + std::filesystem::path FilePath = FullPath.parent_path(); + std::wstring FileName = FullPath.native(); + + const DWORD BufferSize = sizeof(FILE_RENAME_INFO) + gsl::narrow<DWORD>(FileName.size() * sizeof(WCHAR)); + FILE_RENAME_INFO* RenameInfo = reinterpret_cast<FILE_RENAME_INFO*>(Memory::Alloc(BufferSize)); + memset(RenameInfo, 0, BufferSize); + + RenameInfo->ReplaceIfExists = FALSE; + RenameInfo->FileNameLength = gsl::narrow<DWORD>(FileName.size()); + memcpy(RenameInfo->FileName, FileName.c_str(), FileName.size() * sizeof(WCHAR)); + RenameInfo->FileName[FileName.size()] = 0; + + // Try to move file into place + + BOOL Success = SetFileInformationByHandle(FileRef.FileHandle, FileRenameInfo, RenameInfo, BufferSize); + + if (!Success) + { + CAtlFile DirHandle; + + auto InternalCreateDirectoryHandle = [&] { + return DirHandle.Create(FilePath.c_str(), + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS); + }; + + HRESULT hRes = InternalCreateDirectoryHandle(); + + if (FAILED(hRes)) + { + zen::CreateDirectories(FilePath.c_str()); + + hRes = InternalCreateDirectoryHandle(); + } + + if (FAILED(hRes)) + { + throw WindowsException(hRes, "Failed to open shard directory '{}'"_format(FilePath)); + } + + // Retry + + Success = SetFileInformationByHandle(FileRef.FileHandle, FileRenameInfo, RenameInfo, BufferSize); + } + + Memory::Free(RenameInfo); + + if (Success) + { + return CasStore::InsertResult{.New = true}; + } + + spdlog::warn("rename of CAS payload file failed, falling back to regular write for {}", ChunkHash); + + DeletePayloadFileOnClose(); + } + return InsertChunk(Chunk.Data(), Chunk.Size(), ChunkHash); } |