diff options
| -rw-r--r-- | zenserver/cache/structuredcachestore.cpp | 8 | ||||
| -rw-r--r-- | zenstore/CAS.cpp | 121 | ||||
| -rw-r--r-- | zenstore/gc.cpp | 31 | ||||
| -rw-r--r-- | zenstore/include/zenstore/CAS.h | 20 | ||||
| -rw-r--r-- | zenstore/include/zenstore/gc.h | 21 |
5 files changed, 143 insertions, 58 deletions
diff --git a/zenserver/cache/structuredcachestore.cpp b/zenserver/cache/structuredcachestore.cpp index e7b840ed8..15f7b5a7a 100644 --- a/zenserver/cache/structuredcachestore.cpp +++ b/zenserver/cache/structuredcachestore.cpp @@ -15,6 +15,7 @@ #include <zenstore/basicfile.h> #include <zenstore/cas.h> #include <zenstore/caslog.h> +#include <zenstore/gc.h> #include <concepts> #include <filesystem> @@ -1050,7 +1051,12 @@ ZenCacheDiskLayer::Scrub(ScrubContext& Ctx) void ZenCacheDiskLayer::GarbageCollect(GcContext& GcCtx) { - ZEN_UNUSED(GcCtx); + RwLock::SharedLockScope _(m_Lock); + + for (auto& Kv : m_Buckets) + { + Kv.second.GarbageCollect(GcCtx); + } } } // namespace zen diff --git a/zenstore/CAS.cpp b/zenstore/CAS.cpp index a4bbfa340..a71e251e5 100644 --- a/zenstore/CAS.cpp +++ b/zenstore/CAS.cpp @@ -5,6 +5,9 @@ #include "compactcas.h" #include "filecas.h" +#include <zencore/compactbinary.h> +#include <zencore/compactbinarybuilder.h> +#include <zencore/compactbinaryvalidation.h> #include <zencore/except.h> #include <zencore/fmtutils.h> #include <zencore/logging.h> @@ -67,34 +70,6 @@ CasChunkSet::IterateChunks(std::function<void(const IoHash& ChunkHash)>&& Callba ////////////////////////////////////////////////////////////////////////// -struct GcContext::GcState -{ - CasChunkSet m_CasChunks; - CasChunkSet m_CidChunks; -}; - -GcContext::GcContext() : m_State(std::make_unique<GcState>()) -{ -} - -GcContext::~GcContext() -{ -} - -void -GcContext::ContributeCids(std::span<const IoHash> Cids) -{ - m_State->m_CidChunks.AddChunksToSet(Cids); -} - -void -GcContext::ContributeCas(std::span<const IoHash> Cas) -{ - m_State->m_CasChunks.AddChunksToSet(Cas); -} - -////////////////////////////////////////////////////////////////////////// - void ScrubContext::ReportBadCasChunks(std::span<IoHash> BadCasChunks) { @@ -133,6 +108,17 @@ private: CasContainerStrategy m_TinyStrategy; CasContainerStrategy m_SmallStrategy; FileCasStrategy m_LargeStrategy; + CbObject m_ManifestObject; + + enum class StorageScheme + { + Legacy = 0, + WithCbManifest = 1 + }; + + StorageScheme m_StorageScheme = StorageScheme::Legacy; + + void UpdateManifest(bool IsNewStore); }; CasImpl::CasImpl() : m_TinyStrategy(m_Config), m_SmallStrategy(m_Config), m_LargeStrategy(m_Config) @@ -166,21 +152,54 @@ CasImpl::Initialize(const CasStoreConfiguration& InConfig) ManifestPath /= ".ucas_root"; std::error_code Ec; - BasicFile Marker; - Marker.Open(ManifestPath.c_str(), /* IsCreate */ false, Ec); + BasicFile ManifestFile; + ManifestFile.Open(ManifestPath.c_str(), /* IsCreate */ false, Ec); + + bool ManifestIsOk = false; if (Ec) { - IsNewStore = true; - - ExtendableStringBuilder<128> manifest; - manifest.Append("#CAS_ROOT\n"); - manifest.Append("ID="); - zen::Oid id = zen::Oid::NewOid(); - id.ToString(manifest); + if (Ec == std::errc::no_such_file_or_directory) + { + IsNewStore = true; + } + } + else + { + IoBuffer ManifestBuffer = ManifestFile.ReadAll(); + ManifestFile.Close(); + + if (ManifestBuffer.Size() > 0 && ManifestBuffer.Data<uint8_t>()[0] == '#') + { + // Old-style manifest, does not contain any useful information, so we may as well update it + } + else + { + CbObject Manifest{SharedBuffer(ManifestBuffer)}; + CbValidateError ValidationResult = ValidateCompactBinary(ManifestBuffer, CbValidateMode::All); + + if (ValidationResult == CbValidateError::None) + { + if (Manifest["id"]) + { + ManifestIsOk = true; + } + } + else + { + ZEN_ERROR("Store manifest validation failed: {:#x}, will generate new manifest to recover", ValidationResult); + } + + if (ManifestIsOk) + { + m_ManifestObject = std::move(Manifest); + } + } + } - Marker.Open(ManifestPath.c_str(), /* IsCreate */ true); - Marker.Write(manifest.c_str(), (DWORD)manifest.Size(), 0); + if (!ManifestIsOk) + { + UpdateManifest(true); } } @@ -190,6 +209,30 @@ CasImpl::Initialize(const CasStoreConfiguration& InConfig) m_SmallStrategy.Initialize("sobs", 4096, IsNewStore); } +void +CasImpl::UpdateManifest(bool IsNewStore) +{ + if (!m_ManifestObject) + { + CbObjectWriter Cbo; + Cbo << "id" << zen::Oid::NewOid() << "created" << DateTime::Now(); + m_ManifestObject = Cbo.Save(); + } + + // Write manifest to file + + std::filesystem::path ManifestPath = m_Config.RootDirectory; + ManifestPath /= ".ucas_root"; + + // This will throw on failure + + ZEN_TRACE("Writing new manifest to '{}'", ManifestPath); + + BasicFile Marker; + Marker.Open(ManifestPath.c_str(), /* IsCreate */ true); + Marker.Write(m_ManifestObject.GetBuffer(), 0); +} + CasStore::InsertResult CasImpl::InsertChunk(IoBuffer Chunk, const IoHash& ChunkHash) { diff --git a/zenstore/gc.cpp b/zenstore/gc.cpp index bfb8f015e..e7c8f3a1a 100644 --- a/zenstore/gc.cpp +++ b/zenstore/gc.cpp @@ -1,9 +1,40 @@ // Copyright Epic Games, Inc. All Rights Reserved. #include <zenstore/gc.h> +#include <zenstore/CAS.h> namespace zen { +////////////////////////////////////////////////////////////////////////// + +struct GcContext::GcState +{ + CasChunkSet m_CasChunks; + CasChunkSet m_CidChunks; +}; + +GcContext::GcContext() : m_State(std::make_unique<GcState>()) +{ +} + +GcContext::~GcContext() +{ +} + +void +GcContext::ContributeCids(std::span<const IoHash> Cids) +{ + m_State->m_CidChunks.AddChunksToSet(Cids); +} + +void +GcContext::ContributeCas(std::span<const IoHash> Cas) +{ + m_State->m_CasChunks.AddChunksToSet(Cas); +} + +////////////////////////////////////////////////////////////////////////// + CasGc::CasGc(CasStore& Store) : m_CasStore(Store) { } diff --git a/zenstore/include/zenstore/CAS.h b/zenstore/include/zenstore/CAS.h index 86e7e78d9..dc7e260ab 100644 --- a/zenstore/include/zenstore/CAS.h +++ b/zenstore/include/zenstore/CAS.h @@ -19,6 +19,8 @@ namespace zen { +class GcContext; + struct CasStoreConfiguration { // Root directory for CAS store @@ -50,24 +52,6 @@ private: std::unordered_set<IoHash> m_ChunkSet; }; -/** Garbage Collection context object - */ - -class GcContext -{ -public: - GcContext(); - ~GcContext(); - - void ContributeCids(std::span<const IoHash> Cid); - void ContributeCas(std::span<const IoHash> Hash); - -private: - struct GcState; - - std::unique_ptr<GcState> m_State; -}; - /** Context object for data scrubbing * * Data scrubbing is when we traverse stored data to validate it and diff --git a/zenstore/include/zenstore/gc.h b/zenstore/include/zenstore/gc.h index 055843547..33a43f4d2 100644 --- a/zenstore/include/zenstore/gc.h +++ b/zenstore/include/zenstore/gc.h @@ -11,6 +11,27 @@ namespace zen { class CasStore; struct IoHash; +/** Garbage Collection context object + */ + +class GcContext +{ +public: + GcContext(); + ~GcContext(); + + void ContributeCids(std::span<const IoHash> Cid); + void ContributeCas(std::span<const IoHash> Hash); + +private: + struct GcState; + + std::unique_ptr<GcState> m_State; +}; + +/** GC + */ + class CasGc { public: |