aboutsummaryrefslogtreecommitdiff
path: root/zenstore/filecas.cpp
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2021-05-13 17:37:32 +0200
committerStefan Boberg <[email protected]>2021-05-13 17:37:32 +0200
commit5d46e91942d81f6ca4d12225757e633013408369 (patch)
treed14316239f40078a91cba3d867dddc2b6bec6e2b /zenstore/filecas.cpp
parentAdded string_view variant of WindowsException constructor (diff)
downloadzen-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.cpp108
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);
}