aboutsummaryrefslogtreecommitdiff
path: root/zenserver/cache
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2021-09-27 12:34:52 +0200
committerStefan Boberg <[email protected]>2021-09-27 12:34:52 +0200
commitf0036eada7f6bcf6e08afe3ea8517367ed73450e (patch)
treeb1ce3466bba36175cad369028fad1b410a34b5ec /zenserver/cache
parentFixed httpsys Windows compilation error (diff)
parentGetWindowsErrorAsString() -> GetSystemErrorAsString() (diff)
downloadzen-f0036eada7f6bcf6e08afe3ea8517367ed73450e.tar.xz
zen-f0036eada7f6bcf6e08afe3ea8517367ed73450e.zip
Merged latest from main
Diffstat (limited to 'zenserver/cache')
-rw-r--r--zenserver/cache/cachestore.cpp252
-rw-r--r--zenserver/cache/cachestore.h84
-rw-r--r--zenserver/cache/structuredcachestore.cpp271
-rw-r--r--zenserver/cache/structuredcachestore.h1
4 files changed, 187 insertions, 421 deletions
diff --git a/zenserver/cache/cachestore.cpp b/zenserver/cache/cachestore.cpp
deleted file mode 100644
index 2fc253a07..000000000
--- a/zenserver/cache/cachestore.cpp
+++ /dev/null
@@ -1,252 +0,0 @@
-// Copyright Epic Games, Inc. All Rights Reserved.
-
-#include "cachestore.h"
-
-#include <zencore/crc32.h>
-#include <zencore/except.h>
-#include <zencore/logging.h>
-#include <zencore/windows.h>
-
-#include <zencore/filesystem.h>
-#include <zencore/fmtutils.h>
-#include <zencore/iobuffer.h>
-#include <zencore/string.h>
-#include <zencore/thread.h>
-#include <zenstore/basicfile.h>
-#include <zenstore/cas.h>
-#include <zenstore/caslog.h>
-
-#include <fmt/core.h>
-#include <concepts>
-#include <filesystem>
-#include <gsl/gsl-lite.hpp>
-#include <unordered_map>
-
-#include <atlfile.h>
-
-using namespace zen;
-using namespace fmt::literals;
-
-namespace UE {
-
-struct CorruptionTrailer
-{
- enum
- {
- /** Arbitrary number used to identify corruption **/
- MagicConstant = 0x1e873d89
- };
-
- uint32_t Magic = MagicConstant;
- uint32_t Version = 1;
- uint32_t CRCofPayload = 0;
- uint32_t SizeOfPayload = 0;
-
- void Initialize(const void* Data, size_t Size)
- {
- CRCofPayload = zen::MemCrc32_Deprecated(Data, Size);
- SizeOfPayload = (uint32_t)Size;
- }
-};
-
-std::filesystem::path
-GenerateDdcPath(std::string_view Key, std::filesystem::path& rootDir)
-{
- std::filesystem::path FilePath = rootDir;
-
- std::string k8{Key};
- for (auto& c : k8)
- c = (char)toupper(c);
-
- const uint32_t Hash = zen::StrCrc_Deprecated(k8.c_str());
-
- std::wstring DirName;
-
- DirName = u'0' + ((Hash / 100) % 10);
- FilePath /= DirName;
- DirName = u'0' + ((Hash / 10) % 10);
- FilePath /= DirName;
- DirName = u'0' + (Hash % 10);
- FilePath /= DirName;
-
- FilePath /= Key;
-
- auto NativePath = FilePath.native();
- NativePath.append(L".udd");
-
- return NativePath;
-}
-
-} // namespace UE
-
-//////////////////////////////////////////////////////////////////////////
-
-FileCacheStore::FileCacheStore(const char* RootDir, const char* ReadRootDir)
-{
- // Ensure root directory exists - create if it doesn't exist already
-
- ZEN_INFO("Initializing FileCacheStore at '{}'", std::string_view(RootDir));
-
- m_RootDir = RootDir;
-
- std::error_code ErrorCode;
-
- std::filesystem::create_directories(m_RootDir, ErrorCode);
-
- if (ErrorCode)
- {
- ExtendableStringBuilder<256> Name;
- WideToUtf8(m_RootDir.c_str(), Name);
-
- ZEN_ERROR("Could not open file cache directory '{}' for writing ({})", Name.c_str(), ErrorCode.message());
-
- m_IsOk = false;
- }
-
- if (ReadRootDir)
- {
- m_ReadRootDir = ReadRootDir;
-
- if (std::filesystem::exists(m_ReadRootDir, ErrorCode))
- {
- ZEN_INFO("FileCacheStore will use additional read tree at '{}'", std::string_view(ReadRootDir));
-
- m_ReadRootIsValid = true;
- }
- }
-}
-
-FileCacheStore::~FileCacheStore()
-{
-}
-
-bool
-FileCacheStore::Get(std::string_view Key, CacheValue& OutValue)
-{
- CAtlFile File;
-
- std::filesystem::path NativePath;
-
- HRESULT hRes = E_FAIL;
-
- if (m_ReadRootDir.empty() == false)
- {
- NativePath = UE::GenerateDdcPath(Key, m_ReadRootDir);
-
- hRes = File.Create(NativePath.c_str(), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING);
- }
-
- if (FAILED(hRes))
- {
- NativePath = UE::GenerateDdcPath(Key, m_RootDir);
-
- hRes = File.Create(NativePath.c_str(), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING);
- }
-
- if (FAILED(hRes))
- {
- ZEN_DEBUG("GET MISS {}", Key);
-
- return false;
- }
-
- ULONGLONG FileSize;
- File.GetSize(FileSize);
-
- if (FileSize <= 16)
- {
- return false;
- }
-
- FileSize -= 16; // CorruptionWrapper trailer
-
- OutValue.Value = IoBuffer(IoBuffer::File, File.Detach(), 0, FileSize);
-
- ZEN_DEBUG("GET HIT {}", Key);
-
- return true;
-}
-
-void
-FileCacheStore::Put(std::string_view Key, const CacheValue& Value)
-{
- const void* Data = Value.Value.Data();
- size_t Size = Value.Value.Size();
-
- UE::CorruptionTrailer Trailer;
- Trailer.Initialize(Data, Size);
-
- std::filesystem::path NativePath = UE::GenerateDdcPath(Key, m_RootDir);
-
- CAtlTemporaryFile File;
-
- ZEN_DEBUG("PUT {}", Key);
-
- HRESULT hRes = File.Create(m_RootDir.c_str());
-
- if (SUCCEEDED(hRes))
- {
- const uint8_t* WritePointer = reinterpret_cast<const uint8_t*>(Data);
-
- while (Size)
- {
- const int MaxChunkSize = 16 * 1024 * 1024;
- const int ChunkSize = (int)((Size > MaxChunkSize) ? MaxChunkSize : Size);
-
- DWORD BytesWritten = 0;
- File.Write(WritePointer, ChunkSize, &BytesWritten);
-
- Size -= BytesWritten;
- WritePointer += BytesWritten;
- }
-
- File.Write(&Trailer, sizeof Trailer);
- hRes = File.Close(NativePath.c_str()); // This renames the file to its final name
-
- if (FAILED(hRes))
- {
- ZEN_WARN("Failed to rename temp file for key '{}' - deleting temporary file", Key);
-
- if (!DeleteFile(File.TempFileName()))
- {
- ZEN_WARN("Temp file for key '{}' could not be deleted - no value persisted", Key);
- }
- }
- }
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-MemoryCacheStore::MemoryCacheStore()
-{
-}
-
-MemoryCacheStore::~MemoryCacheStore()
-{
-}
-
-bool
-MemoryCacheStore::Get(std::string_view InKey, CacheValue& OutValue)
-{
- RwLock::SharedLockScope _(m_Lock);
-
- auto it = m_CacheMap.find(std::string(InKey));
-
- if (it == m_CacheMap.end())
- {
- return false;
- }
- else
- {
- OutValue.Value = it->second;
-
- return true;
- }
-}
-
-void
-MemoryCacheStore::Put(std::string_view Key, const CacheValue& Value)
-{
- RwLock::ExclusiveLockScope _(m_Lock);
- m_CacheMap[std::string(Key)] = Value.Value;
-}
diff --git a/zenserver/cache/cachestore.h b/zenserver/cache/cachestore.h
deleted file mode 100644
index 89c6396b8..000000000
--- a/zenserver/cache/cachestore.h
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright Epic Games, Inc. All Rights Reserved.
-
-#pragma once
-
-#include <zencore/IoBuffer.h>
-#include <zencore/iohash.h>
-#include <zencore/thread.h>
-#include <zencore/uid.h>
-#include <zenstore/cas.h>
-#include <compare>
-#include <filesystem>
-#include <unordered_map>
-
-namespace zen {
-
-class WideStringBuilderBase;
-class CasStore;
-
-} // namespace zen
-
-struct CacheValue
-{
- zen::IoBuffer Value;
-};
-
-/******************************************************************************
-
- /$$ /$$/$$ /$$ /$$$$$$ /$$
- | $$ /$$| $$ | $$ /$$__ $$ | $$
- | $$ /$$/| $$ | $$ | $$ \__/ /$$$$$$ /$$$$$$| $$$$$$$ /$$$$$$
- | $$$$$/ | $$ / $$/ | $$ |____ $$/$$_____| $$__ $$/$$__ $$
- | $$ $$ \ $$ $$/ | $$ /$$$$$$| $$ | $$ \ $| $$$$$$$$
- | $$\ $$ \ $$$/ | $$ $$/$$__ $| $$ | $$ | $| $$_____/
- | $$ \ $$ \ $/ | $$$$$$| $$$$$$| $$$$$$| $$ | $| $$$$$$$
- |__/ \__/ \_/ \______/ \_______/\_______|__/ |__/\_______/
-
- Basic Key-Value cache. No restrictions on keys, and values are always opaque
- binary blobs.
-
-******************************************************************************/
-
-class CacheStore
-{
-public:
- virtual bool Get(std::string_view Key, CacheValue& OutValue) = 0;
- virtual void Put(std::string_view Key, const CacheValue& Value) = 0;
-};
-
-/** File system based implementation
-
- Emulates the behaviour of UE4 with regards to file system structure,
- and also adds a file corruption trailer to remain compatible with
- the file-system based implementation (this should be made configurable)
-
- */
-class FileCacheStore : public CacheStore
-{
-public:
- FileCacheStore(const char* RootDir, const char* ReadRootDir = nullptr);
- ~FileCacheStore();
-
- virtual bool Get(std::string_view Key, CacheValue& OutValue) override;
- virtual void Put(std::string_view Key, const CacheValue& Value) override;
-
-private:
- std::filesystem::path m_RootDir;
- std::filesystem::path m_ReadRootDir;
- bool m_IsOk = true;
- bool m_ReadRootIsValid = false;
-};
-
-class MemoryCacheStore : public CacheStore
-{
-public:
- MemoryCacheStore();
- ~MemoryCacheStore();
-
- virtual bool Get(std::string_view Key, CacheValue& OutValue) override;
- virtual void Put(std::string_view Key, const CacheValue& Value) override;
-
-private:
- zen::RwLock m_Lock;
- std::unordered_map<std::string, zen::IoBuffer> m_CacheMap;
-};
diff --git a/zenserver/cache/structuredcachestore.cpp b/zenserver/cache/structuredcachestore.cpp
index 3d80bb14c..5e93ebaa9 100644
--- a/zenserver/cache/structuredcachestore.cpp
+++ b/zenserver/cache/structuredcachestore.cpp
@@ -22,8 +22,6 @@
#include <gsl/gsl-lite.hpp>
#include <unordered_map>
-#include <atlfile.h>
-
//////////////////////////////////////////////////////////////////////////
namespace zen {
@@ -131,23 +129,18 @@ ZenCacheMemoryLayer::~ZenCacheMemoryLayer()
bool
ZenCacheMemoryLayer::Get(std::string_view InBucket, const IoHash& HashKey, ZenCacheValue& OutValue)
{
- CacheBucket* Bucket = nullptr;
-
- {
- RwLock::SharedLockScope _(m_Lock);
+ RwLock::SharedLockScope _(m_Lock);
- auto it = m_Buckets.find(std::string(InBucket));
+ auto it = m_Buckets.find(std::string(InBucket));
- if (it != m_Buckets.end())
- {
- Bucket = &it->second;
- }
+ if (it == m_Buckets.end())
+ {
+ return false;
}
- if (Bucket == nullptr)
- return false;
+ CacheBucket* Bucket = Bucket = &it->second;
- ZEN_ASSERT(Bucket != nullptr);
+ _.ReleaseNow();
return Bucket->Get(HashKey, OutValue);
}
@@ -177,8 +170,6 @@ ZenCacheMemoryLayer::Put(std::string_view InBucket, const IoHash& HashKey, const
Bucket = &m_Buckets[std::string(InBucket)];
}
- ZEN_ASSERT(Bucket != nullptr);
-
// Note that since the underlying IoBuffer is retained, the content type is also
Bucket->Put(HashKey, Value);
@@ -195,7 +186,31 @@ ZenCacheMemoryLayer::DropBucket(std::string_view Bucket)
void
ZenCacheMemoryLayer::Scrub(ScrubContext& Ctx)
{
- ZEN_UNUSED(Ctx);
+ RwLock::SharedLockScope _(m_Lock);
+
+ for (auto& Kv : m_Buckets)
+ {
+ Kv.second.Scrub(Ctx);
+ }
+}
+
+void
+ZenCacheMemoryLayer::CacheBucket::Scrub(ScrubContext& Ctx)
+{
+ std::vector<IoHash> BadHashes;
+
+ for (auto& Kv : m_cacheMap)
+ {
+ if (Kv.first != IoHash::HashBuffer(Kv.second))
+ {
+ BadHashes.push_back(Kv.first);
+ }
+ }
+
+ if (!BadHashes.empty())
+ {
+ Ctx.ReportBadChunks(BadHashes);
+ }
}
bool
@@ -203,16 +218,16 @@ ZenCacheMemoryLayer::CacheBucket::Get(const IoHash& HashKey, ZenCacheValue& OutV
{
RwLock::SharedLockScope _(m_bucketLock);
- auto bucketIt = m_cacheMap.find(HashKey);
-
- if (bucketIt == m_cacheMap.end())
+ if (auto bucketIt = m_cacheMap.find(HashKey); bucketIt == m_cacheMap.end())
{
return false;
}
+ else
+ {
+ OutValue.Value = bucketIt->second;
- OutValue.Value = bucketIt->second;
-
- return true;
+ return true;
+ }
}
void
@@ -241,8 +256,19 @@ struct DiskLocation
static uint64_t CombineOffsetAndFlags(uint64_t Offset, uint64_t Flags) { return Offset | Flags; }
- inline uint64_t Offset() const { return OffsetAndFlags & kOffsetMask; }
- inline uint64_t IsFlagSet(uint64_t Flag) const { return OffsetAndFlags & Flag; }
+ inline uint64_t Offset() const { return OffsetAndFlags & kOffsetMask; }
+ inline uint64_t IsFlagSet(uint64_t Flag) const { return OffsetAndFlags & Flag; }
+ inline ZenContentType GetContentType() const
+ {
+ ZenContentType ContentType = ZenContentType::kBinary;
+
+ if (IsFlagSet(DiskLocation::kStructured))
+ {
+ ContentType = ZenContentType::kCbObject;
+ }
+
+ return ContentType;
+ }
};
struct DiskIndexEntry
@@ -267,6 +293,7 @@ struct ZenCacheDiskLayer::CacheBucket
void Put(const IoHash& HashKey, const ZenCacheValue& Value);
void Drop();
void Flush();
+ void Scrub(ScrubContext& Ctx);
inline bool IsOk() const { return m_Ok; }
@@ -277,15 +304,19 @@ private:
bool m_Ok = false;
uint64_t m_LargeObjectThreshold = 64 * 1024;
+ // These files are used to manage storage of small objects for this bucket
+
BasicFile m_SobsFile;
TCasLogFile<DiskIndexEntry> m_SlogFile;
- void BuildPath(WideStringBuilderBase& Path, const IoHash& HashKey);
- void PutLargeObject(const IoHash& HashKey, const ZenCacheValue& Value);
-
RwLock m_IndexLock;
tsl::robin_map<IoHash, DiskLocation, IoHash::Hasher> m_Index;
uint64_t m_WriteCursor = 0;
+
+ void BuildPath(WideStringBuilderBase& Path, const IoHash& HashKey);
+ void PutLargeObject(const IoHash& HashKey, const ZenCacheValue& Value);
+ bool GetStandaloneCacheValue(const IoHash& HashKey, ZenCacheValue& OutValue, const DiskLocation& Loc);
+ bool GetInlineCacheValue(const DiskLocation& Loc, ZenCacheValue& OutValue);
};
ZenCacheDiskLayer::CacheBucket::CacheBucket(CasStore& Cas) : m_CasStore(Cas)
@@ -320,27 +351,24 @@ ZenCacheDiskLayer::CacheBucket::OpenOrCreate(std::filesystem::path BucketDir)
std::filesystem::path SobsPath{m_BucketDir / "zen.sobs"};
std::filesystem::path SlogPath{m_BucketDir / "zen.slog"};
- CAtlFile ManifestFile;
+ BasicFile ManifestFile;
// Try opening existing manifest file first
bool IsNew = false;
- HRESULT hRes = ManifestFile.Create(ManifestPath.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, OPEN_EXISTING);
+ std::error_code Ec;
+ ManifestFile.Open(ManifestPath, /* IsCreate */ false, Ec);
- if (SUCCEEDED(hRes))
+ if (!Ec)
{
- ULONGLONG FileSize;
- ManifestFile.GetSize(FileSize);
+ uint64_t FileSize = ManifestFile.FileSize();
if (FileSize == sizeof(Oid))
{
- hRes = ManifestFile.Read(&m_BucketId, sizeof(Oid));
+ ManifestFile.Read(&m_BucketId, sizeof(Oid), 0);
- if (SUCCEEDED(hRes))
- {
- m_Ok = true;
- }
+ m_Ok = true;
}
if (!m_Ok)
@@ -353,16 +381,16 @@ ZenCacheDiskLayer::CacheBucket::OpenOrCreate(std::filesystem::path BucketDir)
{
// No manifest file found, this is a new bucket
- hRes = ManifestFile.Create(ManifestPath.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS);
+ ManifestFile.Open(ManifestPath, /* IsCreate */ true, Ec);
- if (FAILED(hRes))
+ if (Ec)
{
- ThrowLastError("Failed to create bucket manifest '{}'"_format(ManifestPath));
+ throw std::system_error(Ec, "Failed to create bucket manifest '{}'"_format(ManifestPath));
}
m_BucketId.Generate();
- hRes = ManifestFile.Write(&m_BucketId, sizeof(Oid));
+ ManifestFile.Write(&m_BucketId, sizeof(Oid), /* FileOffset */ 0);
IsNew = true;
}
@@ -407,6 +435,37 @@ ZenCacheDiskLayer::CacheBucket::BuildPath(WideStringBuilderBase& Path, const IoH
}
bool
+ZenCacheDiskLayer::CacheBucket::GetInlineCacheValue(const DiskLocation& Loc, ZenCacheValue& OutValue)
+{
+ if (!Loc.IsFlagSet(DiskLocation::kStandaloneFile))
+ {
+ OutValue.Value = IoBufferBuilder::MakeFromFileHandle(m_SobsFile.Handle(), Loc.Offset(), Loc.Size);
+ OutValue.Value.SetContentType(Loc.GetContentType());
+
+ return true;
+ }
+
+ return false;
+}
+
+bool
+ZenCacheDiskLayer::CacheBucket::GetStandaloneCacheValue(const IoHash& HashKey, ZenCacheValue& OutValue, const DiskLocation& Loc)
+{
+ WideStringBuilder<128> DataFilePath;
+ BuildPath(DataFilePath, HashKey);
+
+ if (IoBuffer Data = IoBufferBuilder::MakeFromFile(DataFilePath.c_str()))
+ {
+ OutValue.Value = Data;
+ OutValue.Value.SetContentType(Loc.GetContentType());
+
+ return true;
+ }
+
+ return false;
+}
+
+bool
ZenCacheDiskLayer::CacheBucket::Get(const IoHash& HashKey, ZenCacheValue& OutValue)
{
if (!m_Ok)
@@ -420,35 +479,14 @@ ZenCacheDiskLayer::CacheBucket::Get(const IoHash& HashKey, ZenCacheValue& OutVal
{
const DiskLocation& Loc = it->second;
- ZenContentType ContentType = ZenContentType::kBinary;
-
- if (Loc.IsFlagSet(DiskLocation::kStructured))
- {
- ContentType = ZenContentType::kCbObject;
- }
-
- if (!Loc.IsFlagSet(DiskLocation::kStandaloneFile))
+ if (GetInlineCacheValue(Loc, OutValue))
{
- OutValue.Value = IoBufferBuilder::MakeFromFileHandle(m_SobsFile.Handle(), Loc.Offset(), Loc.Size);
- OutValue.Value.SetContentType(ContentType);
-
return true;
}
- else
- {
- _.ReleaseNow();
-
- WideStringBuilder<128> DataFilePath;
- BuildPath(DataFilePath, HashKey);
- if (IoBuffer Data = IoBufferBuilder::MakeFromFile(DataFilePath.c_str()))
- {
- OutValue.Value = Data;
- OutValue.Value.SetContentType(ContentType);
+ _.ReleaseNow();
- return true;
- }
- }
+ return GetStandaloneCacheValue(HashKey, OutValue, Loc);
}
return false;
@@ -518,9 +556,58 @@ ZenCacheDiskLayer::CacheBucket::Flush()
}
void
-ZenCacheDiskLayer::Scrub(ScrubContext& Ctx)
+ZenCacheDiskLayer::CacheBucket::Scrub(ScrubContext& Ctx)
{
- ZEN_UNUSED(Ctx);
+ std::vector<DiskIndexEntry> StandaloneFiles;
+
+ std::vector<IoHash> BadChunks;
+ std::vector<IoBuffer> BadStandaloneChunks;
+
+ {
+ RwLock::SharedLockScope _(m_IndexLock);
+
+ for (auto& Kv : m_Index)
+ {
+ const IoHash& Hash = Kv.first;
+ const DiskLocation& Loc = Kv.second;
+
+ ZenCacheValue Value;
+
+ if (!GetInlineCacheValue(Loc, Value))
+ {
+ ZEN_ASSERT(Loc.IsFlagSet(DiskLocation::kStandaloneFile));
+ StandaloneFiles.push_back({.Key = Hash, .Location = Loc});
+ }
+ else
+ {
+ if (GetStandaloneCacheValue(Hash, Value, Loc))
+ {
+ // Hash contents
+
+ const IoHash ComputedHash = HashBuffer(Value.Value);
+
+ if (ComputedHash != Hash)
+ {
+ BadChunks.push_back(Hash);
+ }
+ }
+ else
+ {
+ // Non-existent
+ }
+ }
+ }
+ }
+
+ if (Ctx.RunRecovery())
+ {
+ // Clean out bad chunks
+ }
+
+ if (!BadChunks.empty())
+ {
+ Ctx.ReportBadChunks(BadChunks);
+ }
}
void
@@ -529,35 +616,38 @@ ZenCacheDiskLayer::CacheBucket::PutLargeObject(const IoHash& HashKey, const ZenC
WideStringBuilder<128> DataFilePath;
BuildPath(DataFilePath, HashKey);
- // TODO: replace this process with a more efficient implementation with proper atomic rename
- // and also avoid creating directories if we can
-
- std::filesystem::path ParentPath = std::filesystem::path(DataFilePath.c_str()).parent_path();
- CreateDirectories(ParentPath);
+ TemporaryFile DataFile;
- CAtlTemporaryFile DataFile;
+ std::error_code Ec;
+ DataFile.CreateTemporary(m_BucketDir.c_str(), Ec);
- HRESULT hRes = DataFile.Create(m_BucketDir.c_str());
-
- if (FAILED(hRes))
+ if (Ec)
{
- ThrowSystemException(hRes, "Failed to open temporary file for put at '{}'"_format(m_BucketDir));
+ throw std::system_error(Ec, "Failed to open temporary file for put at '{}'"_format(m_BucketDir));
}
- hRes = DataFile.Write(Value.Value.Data(), gsl::narrow<DWORD>(Value.Value.Size()));
+ DataFile.WriteAll(Value.Value, Ec);
- if (FAILED(hRes))
+ if (Ec)
{
- ThrowSystemException(hRes, "Failed to write payload ({} bytes) to file"_format(NiceBytes(Value.Value.Size())));
+ throw std::system_error(Ec, "Failed to write payload ({} bytes) to file"_format(NiceBytes(Value.Value.Size())));
}
- // Move file into place (note: not fully atomic!)
+ // Move file into place (atomically)
- hRes = DataFile.Close(DataFilePath.c_str());
+ DataFile.MoveTemporaryIntoPlace(DataFilePath.c_str(), Ec);
- if (FAILED(hRes))
+ if (Ec)
{
- ThrowSystemException(hRes, "Failed to finalize file '{}'"_format(WideToUtf8(DataFilePath)));
+ std::filesystem::path ParentPath = std::filesystem::path(DataFilePath.c_str()).parent_path();
+ CreateDirectories(ParentPath);
+
+ DataFile.MoveTemporaryIntoPlace(DataFilePath.c_str(), Ec);
+
+ if (Ec)
+ {
+ throw std::system_error(Ec, "Failed to finalize file '{}'"_format(WideToUtf8(DataFilePath)));
+ }
}
// Update index
@@ -729,6 +819,17 @@ ZenCacheDiskLayer::Flush()
}
}
+void
+ZenCacheDiskLayer::Scrub(ScrubContext& Ctx)
+{
+ RwLock::SharedLockScope _(m_Lock);
+
+ for (auto& Kv : m_Buckets)
+ {
+ Kv.second.Scrub(Ctx);
+ }
+}
+
//////////////////////////////////////////////////////////////////////////
ZenCacheTracker::ZenCacheTracker(ZenCacheStore& CacheStore)
diff --git a/zenserver/cache/structuredcachestore.h b/zenserver/cache/structuredcachestore.h
index 2cc3abb53..f96757409 100644
--- a/zenserver/cache/structuredcachestore.h
+++ b/zenserver/cache/structuredcachestore.h
@@ -65,6 +65,7 @@ private:
bool Get(const IoHash& HashKey, ZenCacheValue& OutValue);
void Put(const IoHash& HashKey, const ZenCacheValue& Value);
+ void Scrub(ScrubContext& Ctx);
};
RwLock m_Lock;