diff options
| author | Dan Engelbrecht <[email protected]> | 2025-08-29 13:13:37 +0200 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2025-08-29 13:13:37 +0200 |
| commit | b9251e36e2eef54fa82422c09256ceb71c56f454 (patch) | |
| tree | 35c20a27769b3410e361131d778b048355af78d3 /src/zenserver/projectstore/projectstore.cpp | |
| parent | 5.7.0 (diff) | |
| download | zen-b9251e36e2eef54fa82422c09256ceb71c56f454.tar.xz zen-b9251e36e2eef54fa82422c09256ceb71c56f454.zip | |
projectstore lifetime improvements (#481)
- Improvement: Oplogs that have not been touched for 15 min are unloaded from memory during GC pass, oplogs are reloaded on demand
- Improvement: Oplogs read for GC/Validation operations are loaded using a lightweight mode reducing memory usage and load times
- Improvement: Cleaned up logging for oplogs/projects to reduce clutter
Diffstat (limited to 'src/zenserver/projectstore/projectstore.cpp')
| -rw-r--r-- | src/zenserver/projectstore/projectstore.cpp | 805 |
1 files changed, 488 insertions, 317 deletions
diff --git a/src/zenserver/projectstore/projectstore.cpp b/src/zenserver/projectstore/projectstore.cpp index a5ab24cfb..7d4953a8f 100644 --- a/src/zenserver/projectstore/projectstore.cpp +++ b/src/zenserver/projectstore/projectstore.cpp @@ -486,10 +486,10 @@ struct ProjectStore::OplogStorage : public RefCounted ~OplogStorage() { - ZEN_INFO("oplog '{}/{}': closing oplog storage at {}", - m_OwnerOplog->GetOuterProject()->Identifier, - m_OwnerOplog->OplogId(), - m_OplogStoragePath); + ZEN_DEBUG("oplog '{}/{}': closing oplog storage at {}", + m_OwnerOplog->GetOuterProjectIdentifier(), + m_OwnerOplog->OplogId(), + m_OplogStoragePath); try { Flush(); @@ -499,7 +499,7 @@ struct ProjectStore::OplogStorage : public RefCounted catch (const std::exception& Ex) { ZEN_WARN("oplog '{}/{}': flushing oplog at '{}' failed. Reason: '{}'", - m_OwnerOplog->GetOuterProject()->Identifier, + m_OwnerOplog->GetOuterProjectIdentifier(), m_OwnerOplog->OplogId(), m_OplogStoragePath, Ex.what()); @@ -552,20 +552,20 @@ struct ProjectStore::OplogStorage : public RefCounted if (IsCreate) { - ZEN_INFO("oplog '{}/{}': initializing storage at '{}'", - m_OwnerOplog->GetOuterProject()->Identifier, - m_OwnerOplog->OplogId(), - m_OplogStoragePath); + ZEN_DEBUG("oplog '{}/{}': initializing storage at '{}'", + m_OwnerOplog->GetOuterProjectIdentifier(), + m_OwnerOplog->OplogId(), + m_OplogStoragePath); DeleteDirectories(m_OplogStoragePath); CreateDirectories(m_OplogStoragePath); } else { - ZEN_INFO("oplog '{}/{}': opening storage at '{}'", - m_OwnerOplog->GetOuterProject()->Identifier, - m_OwnerOplog->OplogId(), - m_OplogStoragePath); + ZEN_DEBUG("oplog '{}/{}': opening storage at '{}'", + m_OwnerOplog->GetOuterProjectIdentifier(), + m_OwnerOplog->OplogId(), + m_OplogStoragePath); } m_Oplog.Open(GetLogPath(m_OplogStoragePath), IsCreate ? CasLogFile::Mode::kTruncate : CasLogFile::Mode::kWrite); @@ -615,7 +615,7 @@ struct ProjectStore::OplogStorage : public RefCounted ZEN_TRACE_CPU("Store::OplogStorage::Compact"); ZEN_INFO("oplog '{}/{}': compacting at '{}'", - m_OwnerOplog->GetOuterProject()->Identifier, + m_OwnerOplog->GetOuterProjectIdentifier(), m_OwnerOplog->OplogId(), m_OplogStoragePath); @@ -753,7 +753,7 @@ struct ProjectStore::OplogStorage : public RefCounted } ZEN_INFO("oplog '{}/{}': compact completed in {} - Max LSN# {}, New size: {}, old size {}.", - m_OwnerOplog->GetOuterProject()->Identifier, + m_OwnerOplog->GetOuterProjectIdentifier(), m_OwnerOplog->OplogId(), NiceTimeSpanMs(Timer.GetElapsedTimeMs()), m_MaxLsn.load(), @@ -783,10 +783,10 @@ struct ProjectStore::OplogStorage : public RefCounted std::filesystem::path GetBlobsPath() const { return GetBlobsPath(m_OplogStoragePath); } - void ReplayLog(std::function<void(CbObjectView, const OplogEntry&)>&& Handler, uint64_t SkipEntryCount = 0) + void ReadOplogEntriesFromLog(std::vector<OplogEntry>& OutOpLogEntries, uint64_t& OutInvalidEntries, uint64_t SkipEntryCount) { ZEN_MEMSCOPE(GetProjectstoreTag()); - ZEN_TRACE_CPU("Store::OplogStorage::ReplayLog"); + ZEN_TRACE_CPU("Store::OplogStorage::ReadOplogEntriesFromLog"); if (m_Oplog.GetLogCount() == SkipEntryCount) { @@ -797,9 +797,6 @@ struct ProjectStore::OplogStorage : public RefCounted uint64_t OpsBlockSize = m_OpBlobs.FileSize(); - std::vector<OplogEntry> OpLogEntries; - uint64_t InvalidEntries = 0; - { tsl::robin_map<Oid, size_t, Oid::Hasher> LatestKeys; @@ -810,20 +807,20 @@ struct ProjectStore::OplogStorage : public RefCounted if (auto It = LatestKeys.find(LogEntry.OpKeyHash); It == LatestKeys.end()) { ZEN_SCOPED_WARN("found tombstone referencing unknown key {}", LogEntry.OpKeyHash); - ++InvalidEntries; + ++OutInvalidEntries; return; } } else if (LogEntry.OpCoreSize == 0) { ZEN_SCOPED_WARN("skipping zero size op {}", LogEntry.OpKeyHash); - ++InvalidEntries; + ++OutInvalidEntries; return; } else if (LogEntry.OpLsn == 0) { ZEN_SCOPED_WARN("skipping zero lsn op {}", LogEntry.OpKeyHash); - ++InvalidEntries; + ++OutInvalidEntries; return; } @@ -831,13 +828,13 @@ struct ProjectStore::OplogStorage : public RefCounted if ((OpFileOffset + LogEntry.OpCoreSize) > OpsBlockSize) { ZEN_SCOPED_WARN("skipping out of bounds op {}", LogEntry.OpKeyHash); - ++InvalidEntries; + ++OutInvalidEntries; return; } if (auto It = LatestKeys.find(LogEntry.OpKeyHash); It != LatestKeys.end()) { - OplogEntry& Entry = OpLogEntries[It->second]; + OplogEntry& Entry = OutOpLogEntries[It->second]; if (LogEntry.IsTombstone() && Entry.IsTombstone()) { @@ -848,17 +845,31 @@ struct ProjectStore::OplogStorage : public RefCounted } else { - const size_t OpIndex = OpLogEntries.size(); + const size_t OpIndex = OutOpLogEntries.size(); LatestKeys[LogEntry.OpKeyHash] = OpIndex; - OpLogEntries.push_back(LogEntry); + OutOpLogEntries.push_back(LogEntry); } }, SkipEntryCount); } + } - std::sort(OpLogEntries.begin(), OpLogEntries.end(), [&](const OplogEntry& Lhs, const OplogEntry& Rhs) { - return Lhs.OpCoreOffset < Rhs.OpCoreOffset; - }); + void ReplayLog(std::function<void(CbObjectView, const OplogEntry&)>&& Handler, uint64_t SkipEntryCount = 0) + { + ZEN_MEMSCOPE(GetProjectstoreTag()); + ZEN_TRACE_CPU("Store::OplogStorage::ReplayLog"); + + if (m_Oplog.GetLogCount() == SkipEntryCount) + { + return; + } + + Stopwatch Timer; + + std::vector<OplogEntry> OpLogEntries; + uint64_t InvalidEntries = 0; + + ReadOplogEntriesFromLog(OpLogEntries, InvalidEntries, SkipEntryCount); uint64_t TombstoneEntries = 0; @@ -867,6 +878,10 @@ struct ProjectStore::OplogStorage : public RefCounted uint32_t MaxOpLsn = m_MaxLsn; uint64_t NextOpFileOffset = m_NextOpsOffset; + std::sort(OpLogEntries.begin(), OpLogEntries.end(), [&](const OplogEntry& Lhs, const OplogEntry& Rhs) { + return Lhs.OpCoreOffset < Rhs.OpCoreOffset; + }); + for (const OplogEntry& LogEntry : OpLogEntries) { if (LogEntry.IsTombstone()) @@ -885,7 +900,7 @@ struct ProjectStore::OplogStorage : public RefCounted if (OpCoreHash != ExpectedOpCoreHash) { ZEN_WARN("oplog '{}/{}': skipping bad checksum op - {}. Expected: {}, found: {}", - m_OwnerOplog->GetOuterProject()->Identifier, + m_OwnerOplog->GetOuterProjectIdentifier(), m_OwnerOplog->OplogId(), LogEntry.OpKeyHash, ExpectedOpCoreHash, @@ -895,7 +910,7 @@ struct ProjectStore::OplogStorage : public RefCounted Err != CbValidateError::None) { ZEN_WARN("oplog '{}/{}': skipping invalid format op - {}. Error: '{}'", - m_OwnerOplog->GetOuterProject()->Identifier, + m_OwnerOplog->GetOuterProjectIdentifier(), m_OwnerOplog->OplogId(), LogEntry.OpKeyHash, ToString(Err)); @@ -914,7 +929,7 @@ struct ProjectStore::OplogStorage : public RefCounted m_NextOpsOffset = NextOpFileOffset; ZEN_INFO("oplog '{}/{}': replay from '{}' completed in {} - Max LSN# {}, Next offset: {}, {} tombstones, {} invalid entries", - m_OwnerOplog->GetOuterProject()->Identifier, + m_OwnerOplog->GetOuterProjectIdentifier(), m_OwnerOplog->OplogId(), m_OplogStoragePath, NiceTimeSpanMs(Timer.GetElapsedTimeMs()), @@ -1105,12 +1120,14 @@ private: ////////////////////////////////////////////////////////////////////////// -ProjectStore::Oplog::Oplog(std::string_view Id, - Project* Project, +ProjectStore::Oplog::Oplog(const LoggerRef& InLog, + std::string_view ProjectIdentifier, + std::string_view Id, CidStore& Store, - std::filesystem::path BasePath, + const std::filesystem::path& BasePath, const std::filesystem::path& MarkerPath) -: m_OuterProject(Project) +: m_Log(InLog) +, m_OuterProjectId(ProjectIdentifier) , m_OplogId(Id) , m_CidStore(Store) , m_BasePath(BasePath) @@ -1220,7 +1237,7 @@ ProjectStore::Oplog::Scrub(ScrubContext& Ctx) if (Ctx.RunRecovery()) { ZEN_WARN("oplog '{}/{}': scrubbing found {} bad ops in oplog @ '{}', these will be removed from the index", - m_OuterProject->Identifier, + m_OuterProjectId, m_OplogId, BadEntryKeys.size(), m_BasePath); @@ -1244,7 +1261,7 @@ ProjectStore::Oplog::Scrub(ScrubContext& Ctx) else { ZEN_WARN("oplog '{}/{}': scrubbing found {} bad ops in oplog @ '{}' but no cleanup will be performed", - m_OuterProject->Identifier, + m_OuterProjectId, m_OplogId, BadEntryKeys.size(), m_BasePath); @@ -1336,15 +1353,23 @@ ProjectStore::Oplog::Exists() const } void -ProjectStore::Oplog::Read() +ProjectStore::Oplog::Read(EState State) { ZEN_MEMSCOPE(GetProjectstoreTag()); + ZEN_ASSERT(m_State == EState::kUnread); + ZEN_ASSERT(State != EState::kUnread); using namespace std::literals; ZEN_TRACE_CPU("Oplog::Read"); ZEN_LOG_SCOPE("Oplog::Read '{}'", m_OplogId); - ZEN_DEBUG("oplog '{}': reading config from '{}'", m_OuterProject->Identifier, m_OplogId, m_BasePath); + ZEN_DEBUG("oplog '{}/{}': reading from '{}'. State: {}", + m_OuterProjectId, + m_OplogId, + m_BasePath, + State == EState::kFull ? "Full" : "BasicNoLookups"); + + m_State = State; std::optional<CbObject> Config = ReadStateFile(m_BasePath, [this]() { return Log(); }); if (Config.has_value()) @@ -1363,43 +1388,55 @@ ProjectStore::Oplog::Read() RemoveFile(m_MetaPath, DummyEc); } - ReadIndexSnapshot(); + ReadIndexSnapshot(m_State); - m_Storage->ReplayLog( - [&](CbObjectView Op, const OplogEntry& OpEntry) { - // MaxLSN = Max(OpEntry.OpLsn, MaxLSN); + if (m_State == EState::kFull) + { + m_Storage->ReplayLog( + [&](CbObjectView Op, const OplogEntry& OpEntry) { + const OplogEntryMapping OpMapping = GetMapping(Op); + // Update chunk id maps + for (const ChunkMapping& Chunk : OpMapping.Chunks) + { + m_ChunkMap.insert_or_assign(Chunk.Id, Chunk.Hash); + } - const OplogEntryMapping OpMapping = GetMapping(Op); - // Update chunk id maps - for (const ChunkMapping& Chunk : OpMapping.Chunks) - { - m_ChunkMap.insert_or_assign(Chunk.Id, Chunk.Hash); - } + for (const FileMapping& File : OpMapping.Files) + { + if (File.Hash != IoHash::Zero) + { + m_ChunkMap.insert_or_assign(File.Id, File.Hash); + } + m_FileMap.insert_or_assign(File.Id, + FileMapEntry{.ServerPath = File.Hash == IoHash::Zero ? File.ServerPath : std::string(), + .ClientPath = File.ClientPath}); + } - for (const FileMapping& File : OpMapping.Files) - { - if (File.Hash != IoHash::Zero) + for (const ChunkMapping& Meta : OpMapping.Meta) { - m_ChunkMap.insert_or_assign(File.Id, File.Hash); + m_MetaMap.insert_or_assign(Meta.Id, Meta.Hash); } - m_FileMap.insert_or_assign( - File.Id, - FileMapEntry{.ServerPath = File.Hash == IoHash::Zero ? File.ServerPath : std::string(), .ClientPath = File.ClientPath}); - } - for (const ChunkMapping& Meta : OpMapping.Meta) - { - m_MetaMap.insert_or_assign(Meta.Id, Meta.Hash); - } + m_OpAddressMap.emplace(OpEntry.OpLsn, OplogEntryAddress{.Offset = OpEntry.OpCoreOffset, .Size = OpEntry.OpCoreSize}); + m_LatestOpMap[OpEntry.OpKeyHash] = OpEntry.OpLsn; + }, + m_LogFlushPosition); + if (m_Storage->LogCount() != m_LogFlushPosition) + { + WriteIndexSnapshot(); + } + } + else + { + std::vector<OplogEntry> OpLogEntries; + uint64_t InvalidEntries; + m_Storage->ReadOplogEntriesFromLog(OpLogEntries, InvalidEntries, m_LogFlushPosition); + for (const OplogEntry& OpEntry : OpLogEntries) + { m_OpAddressMap.emplace(OpEntry.OpLsn, OplogEntryAddress{.Offset = OpEntry.OpCoreOffset, .Size = OpEntry.OpCoreSize}); m_LatestOpMap[OpEntry.OpKeyHash] = OpEntry.OpLsn; - }, - m_LogFlushPosition); - - if (m_Storage->LogCount() != m_LogFlushPosition) - { - WriteIndexSnapshot(); + } } } @@ -1407,6 +1444,9 @@ void ProjectStore::Oplog::Write() { ZEN_MEMSCOPE(GetProjectstoreTag()); + ZEN_ASSERT(m_State != EState::kBasicNoLookups); + + m_State = EState::kFull; using namespace std::literals; @@ -1420,7 +1460,7 @@ ProjectStore::Oplog::Write() std::filesystem::path StateFilePath = m_BasePath / "oplog.zcb"sv; - ZEN_INFO("oplog '{}/{}': persisting config to '{}'", m_OuterProject->Identifier, m_OplogId, StateFilePath); + ZEN_DEBUG("oplog '{}/{}': persisting config to '{}'", m_OuterProjectId, m_OplogId, StateFilePath); TemporaryFile::SafeWriteFile(StateFilePath, Mem.GetView()); } @@ -1472,6 +1512,30 @@ ProjectStore::Oplog::Reset() return true; } +bool +ProjectStore::Oplog::CanUnload() +{ + ZEN_MEMSCOPE(GetProjectstoreTag()); + + RwLock::SharedLockScope _(m_OplogLock); + + uint64_t LogCount = m_Storage->LogCount(); + if (m_LogFlushPosition != LogCount) + { + return false; // The oplog is not flushed so likely this is an active oplog + } + + if (!m_PendingPrepOpAttachments.empty()) + { + return false; // We have a pending oplog prep operation in flight + } + if (m_UpdateCaptureRefCounter > 0) + { + return false; // GC capture is enable for the oplog + } + return true; +} + std::optional<CbObject> ProjectStore::Oplog::ReadStateFile(const std::filesystem::path& BasePath, std::function<LoggerRef()>&& Log) { @@ -1482,7 +1546,7 @@ ProjectStore::Oplog::ReadStateFile(const std::filesystem::path& BasePath, std::f std::filesystem::path StateFilePath = BasePath / "oplog.zcb"sv; if (IsFile(StateFilePath)) { - // ZEN_INFO("oplog '{}/{}': config read from '{}'", m_OuterProject->Identifier, m_OplogId, StateFilePath); + // ZEN_INFO("oplog '{}/{}': config read from '{}'", m_OuterProjectId, m_OplogId, StateFilePath); BasicFile Blob; Blob.Open(StateFilePath, BasicFile::Mode::kRead); @@ -1503,7 +1567,9 @@ ProjectStore::Oplog::ReadStateFile(const std::filesystem::path& BasePath, std::f } ProjectStore::Oplog::ValidationResult -ProjectStore::Oplog::Validate(std::atomic_bool& IsCancelledFlag, WorkerThreadPool* OptionalWorkerPool) +ProjectStore::Oplog::Validate(const std::filesystem::path& ProjectRootDir, + std::atomic_bool& IsCancelledFlag, + WorkerThreadPool* OptionalWorkerPool) { ZEN_MEMSCOPE(GetProjectstoreTag()); @@ -1566,7 +1632,7 @@ ProjectStore::Oplog::Validate(std::atomic_bool& IsCancelledFlag, WorkerThreadPoo { if (File.Hash == IoHash::Zero) { - std::filesystem::path FilePath = m_OuterProject->RootDir / File.ServerPath; + std::filesystem::path FilePath = ProjectRootDir / File.ServerPath; if (!IsFile(FilePath)) { ResultLock.WithExclusiveLock([&]() { Result.MissingFiles.push_back({KeyHash, File}); }); @@ -1649,12 +1715,14 @@ ProjectStore::Oplog::WriteIndexSnapshot() ZEN_MEMSCOPE(GetProjectstoreTag()); ZEN_TRACE_CPU("Oplog::WriteIndexSnapshot"); - ZEN_DEBUG("oplog '{}/{}': write store snapshot at '{}'", m_OuterProject->Identifier, m_OplogId, m_BasePath); + ZEN_ASSERT(m_State == EState::kFull); + + ZEN_DEBUG("oplog '{}/{}': write store snapshot at '{}'", m_OuterProjectId, m_OplogId, m_BasePath); uint64_t EntryCount = 0; Stopwatch Timer; const auto _ = MakeGuard([&] { ZEN_INFO("oplog '{}/{}': wrote store snapshot for '{}' containing {} entries in {}", - m_OuterProject->Identifier, + m_OuterProjectId, m_OplogId, m_BasePath, EntryCount, @@ -1790,12 +1858,12 @@ ProjectStore::Oplog::WriteIndexSnapshot() } catch (const std::exception& Err) { - ZEN_WARN("oplog '{}/{}': snapshot FAILED, reason: '{}'", m_OuterProject->Identifier, m_OplogId, Err.what()); + ZEN_WARN("oplog '{}/{}': snapshot FAILED, reason: '{}'", m_OuterProjectId, m_OplogId, Err.what()); } } void -ProjectStore::Oplog::ReadIndexSnapshot() +ProjectStore::Oplog::ReadIndexSnapshot(EState State) { ZEN_MEMSCOPE(GetProjectstoreTag()); ZEN_TRACE_CPU("Oplog::ReadIndexSnapshot"); @@ -1806,12 +1874,12 @@ ProjectStore::Oplog::ReadIndexSnapshot() uint64_t EntryCount = 0; Stopwatch Timer; const auto _ = MakeGuard([&] { - ZEN_INFO("oplog '{}/{}': index read from '{}' containing {} entries in {}", - m_OuterProject->Identifier, - m_OplogId, - IndexPath, - EntryCount, - NiceTimeSpanMs(Timer.GetElapsedTimeMs())); + ZEN_DEBUG("oplog '{}/{}': index read from '{}' containing {} entries in {}", + m_OuterProjectId, + m_OplogId, + IndexPath, + EntryCount, + NiceTimeSpanMs(Timer.GetElapsedTimeMs())); }); try @@ -1837,7 +1905,7 @@ ProjectStore::Oplog::ReadIndexSnapshot() if (Header.Checksum != Checksum) { ZEN_WARN("oplog '{}/{}': skipping invalid index file '{}'. Checksum mismatch. Expected: {}, Found: {}", - m_OuterProject->Identifier, + m_OuterProjectId, m_OplogId, IndexPath, Header.Checksum, @@ -1848,7 +1916,7 @@ ProjectStore::Oplog::ReadIndexSnapshot() if (Header.LatestOpMapCount + Header.ChunkMapCount + Header.MetaMapCount + Header.FileMapCount != Header.KeyCount) { ZEN_WARN("oplog '{}/{}': skipping invalid index file '{}'. Key count mismatch. Expected: {}, Found: {}", - m_OuterProject->Identifier, + m_OuterProjectId, m_OplogId, IndexPath, Header.LatestOpMapCount + Header.ChunkMapCount + Header.MetaMapCount + Header.FileMapCount, @@ -1895,55 +1963,58 @@ ProjectStore::Oplog::ReadIndexSnapshot() MaxLSN = Max(MaxLSN, LSN); } } + if (State == EState::kFull) { - std::vector<IoHash> ChunkMapEntries(Header.ChunkMapCount); - ObjectIndexFile.Read(ChunkMapEntries.data(), ChunkMapEntries.size() * sizeof(IoHash), Offset); - Offset += ChunkMapEntries.size() * sizeof(IoHash); - Offset = RoundUp(Offset, OplogIndexHeader::DataAlignment); - m_ChunkMap.reserve(ChunkMapEntries.size()); - for (const IoHash& ChunkId : ChunkMapEntries) { - m_ChunkMap.insert_or_assign(Keys[KeyOffset++], ChunkId); + std::vector<IoHash> ChunkMapEntries(Header.ChunkMapCount); + ObjectIndexFile.Read(ChunkMapEntries.data(), ChunkMapEntries.size() * sizeof(IoHash), Offset); + Offset += ChunkMapEntries.size() * sizeof(IoHash); + Offset = RoundUp(Offset, OplogIndexHeader::DataAlignment); + m_ChunkMap.reserve(ChunkMapEntries.size()); + for (const IoHash& ChunkId : ChunkMapEntries) + { + m_ChunkMap.insert_or_assign(Keys[KeyOffset++], ChunkId); + } } - } - { - std::vector<IoHash> MetaMapEntries(Header.MetaMapCount); - ObjectIndexFile.Read(MetaMapEntries.data(), MetaMapEntries.size() * sizeof(IoHash), Offset); - Offset += MetaMapEntries.size() * sizeof(IoHash); - Offset = RoundUp(Offset, OplogIndexHeader::DataAlignment); - m_MetaMap.reserve(MetaMapEntries.size()); - for (const IoHash& ChunkId : MetaMapEntries) { - m_MetaMap.insert_or_assign(Keys[KeyOffset++], ChunkId); + std::vector<IoHash> MetaMapEntries(Header.MetaMapCount); + ObjectIndexFile.Read(MetaMapEntries.data(), MetaMapEntries.size() * sizeof(IoHash), Offset); + Offset += MetaMapEntries.size() * sizeof(IoHash); + Offset = RoundUp(Offset, OplogIndexHeader::DataAlignment); + m_MetaMap.reserve(MetaMapEntries.size()); + for (const IoHash& ChunkId : MetaMapEntries) + { + m_MetaMap.insert_or_assign(Keys[KeyOffset++], ChunkId); + } } - } - { - m_FileMap.reserve(Header.FileMapCount); - - std::vector<uint32_t> FilePathLengths(Header.FileMapCount * 2); - ObjectIndexFile.Read(FilePathLengths.data(), FilePathLengths.size() * sizeof(uint32_t), Offset); - Offset += FilePathLengths.size() * sizeof(uint32_t); - Offset = RoundUp(Offset, OplogIndexHeader::DataAlignment); - - BasicFileBuffer IndexFile(ObjectIndexFile, 65536); - auto ReadString([&IndexFile, &Offset](uint32_t Length) -> std::string { - MemoryView StringData = IndexFile.MakeView(Length, Offset); - if (StringData.GetSize() != Length) - { - throw std::runtime_error(fmt::format("Invalid format. Expected to read %u bytes but got %u", - Length, - uint32_t(StringData.GetSize()))); - } - Offset += Length; - return std::string((const char*)StringData.GetData(), Length); - }); - for (uint64_t FileLengthOffset = 0; FileLengthOffset < FilePathLengths.size();) { - std::string ServerPath = ReadString(FilePathLengths[FileLengthOffset++]); - std::string ClientPath = ReadString(FilePathLengths[FileLengthOffset++]); - m_FileMap.insert_or_assign( - Keys[KeyOffset++], - FileMapEntry{.ServerPath = std::move(ServerPath), .ClientPath = std::move(ClientPath)}); + m_FileMap.reserve(Header.FileMapCount); + + std::vector<uint32_t> FilePathLengths(Header.FileMapCount * 2); + ObjectIndexFile.Read(FilePathLengths.data(), FilePathLengths.size() * sizeof(uint32_t), Offset); + Offset += FilePathLengths.size() * sizeof(uint32_t); + Offset = RoundUp(Offset, OplogIndexHeader::DataAlignment); + + BasicFileBuffer IndexFile(ObjectIndexFile, 65536); + auto ReadString([&IndexFile, &Offset](uint32_t Length) -> std::string { + MemoryView StringData = IndexFile.MakeView(Length, Offset); + if (StringData.GetSize() != Length) + { + throw std::runtime_error(fmt::format("Invalid format. Expected to read %u bytes but got %u", + Length, + uint32_t(StringData.GetSize()))); + } + Offset += Length; + return std::string((const char*)StringData.GetData(), Length); + }); + for (uint64_t FileLengthOffset = 0; FileLengthOffset < FilePathLengths.size();) + { + std::string ServerPath = ReadString(FilePathLengths[FileLengthOffset++]); + std::string ClientPath = ReadString(FilePathLengths[FileLengthOffset++]); + m_FileMap.insert_or_assign( + Keys[KeyOffset++], + FileMapEntry{.ServerPath = std::move(ServerPath), .ClientPath = std::move(ClientPath)}); + } } } m_LogFlushPosition = Header.LogPosition; @@ -1952,7 +2023,7 @@ ProjectStore::Oplog::ReadIndexSnapshot() } else { - ZEN_WARN("oplog '{}/{}': skipping invalid index file '{}'", m_OuterProject->Identifier, m_OplogId, IndexPath); + ZEN_WARN("oplog '{}/{}': skipping invalid index file '{}'", m_OuterProjectId, m_OplogId, IndexPath); } } } @@ -1965,7 +2036,7 @@ ProjectStore::Oplog::ReadIndexSnapshot() m_FileMap.clear(); m_LogFlushPosition = 0; ZEN_ERROR("oplog '{}/{}': failed reading index snapshot from '{}'. Reason: '{}'", - m_OuterProject->Identifier, + m_OuterProjectId, m_OplogId, IndexPath, Ex.what()); @@ -2023,6 +2094,8 @@ ProjectStore::Oplog::Compact(bool DryRun, bool RetainLSNs, std::string_view LogP void ProjectStore::Oplog::Compact(RwLock::ExclusiveLockScope&, bool DryRun, bool RetainLSNs, std::string_view LogPrefix) { + ZEN_ASSERT(m_State == EState::kFull); + ZEN_MEMSCOPE(GetProjectstoreTag()); Stopwatch Timer; @@ -2060,7 +2133,7 @@ ProjectStore::Oplog::Compact(RwLock::ExclusiveLockScope&, bool DryRun, bool Reta ZEN_INFO("{} oplog '{}/{}': Compacted in {}. New size: {}, freeing: {}", LogPrefix, - m_OuterProject->Identifier, + m_OuterProjectId, m_OplogId, NiceTimeSpanMs(Timer.GetElapsedTimeMs()), NiceBytes(PostSize), @@ -2070,6 +2143,7 @@ ProjectStore::Oplog::Compact(RwLock::ExclusiveLockScope&, bool DryRun, bool Reta IoBuffer ProjectStore::Oplog::GetChunkByRawHash(const IoHash& RawHash) { + ZEN_ASSERT(m_State == EState::kFull); return m_CidStore.FindChunkByCid(RawHash); } @@ -2080,6 +2154,7 @@ ProjectStore::Oplog::IterateChunks(std::span<IoHash> RawHashes, WorkerThreadPool* OptionalWorkerPool, uint64_t LargeSizeLimit) { + ZEN_ASSERT(m_State == EState::kFull); return m_CidStore.IterateChunks( RawHashes, [&](size_t Index, const IoBuffer& Payload) { @@ -2090,12 +2165,14 @@ ProjectStore::Oplog::IterateChunks(std::span<IoHash> RawHashes, } bool -ProjectStore::Oplog::IterateChunks(std::span<Oid> ChunkIds, +ProjectStore::Oplog::IterateChunks(const std::filesystem::path& ProjectRootDir, + std::span<Oid> ChunkIds, bool IncludeModTag, const std::function<bool(size_t Index, const IoBuffer& Payload, uint64_t ModTag)>& AsyncCallback, WorkerThreadPool* OptionalWorkerPool, uint64_t LargeSizeLimit) { + ZEN_ASSERT(m_State == EState::kFull); ZEN_MEMSCOPE(GetProjectstoreTag()); std::vector<size_t> CidChunkIndexes; @@ -2120,7 +2197,7 @@ ProjectStore::Oplog::IterateChunks(std::span<Oid> ChunkIds, else if (auto FileIt = m_FileMap.find(ChunkId); FileIt != m_FileMap.end()) { FileChunkIndexes.push_back(ChunkIndex); - FileChunkPaths.emplace_back(m_OuterProject->RootDir / FileIt->second.ServerPath); + FileChunkPaths.emplace_back(ProjectRootDir / FileIt->second.ServerPath); } } } @@ -2165,7 +2242,7 @@ ProjectStore::Oplog::IterateChunks(std::span<Oid> ChunkIds, catch (const std::exception& Ex) { ZEN_WARN("oplog '{}/{}': exception caught when iterating file chunk {}, path '{}'. Reason: '{}'", - m_OuterProject->Identifier, + m_OuterProjectId, m_OplogId, FileChunkIndex, FilePath, @@ -2235,8 +2312,10 @@ ProjectStore::Oplog::IterateChunks(std::span<Oid> ChunkIds, } IoBuffer -ProjectStore::Oplog::FindChunk(const Oid& ChunkId, uint64_t* OptOutModificationTag) +ProjectStore::Oplog::FindChunk(const std::filesystem::path& ProjectRootDir, const Oid& ChunkId, uint64_t* OptOutModificationTag) { + ZEN_ASSERT(m_State == EState::kFull); + RwLock::SharedLockScope OplogLock(m_OplogLock); if (!m_Storage) { @@ -2258,7 +2337,7 @@ ProjectStore::Oplog::FindChunk(const Oid& ChunkId, uint64_t* OptOutModificationT if (auto FileIt = m_FileMap.find(ChunkId); FileIt != m_FileMap.end()) { - std::filesystem::path FilePath = m_OuterProject->RootDir / FileIt->second.ServerPath; + std::filesystem::path FilePath = ProjectRootDir / FileIt->second.ServerPath; OplogLock.ReleaseNow(); @@ -2291,8 +2370,10 @@ ProjectStore::Oplog::FindChunk(const Oid& ChunkId, uint64_t* OptOutModificationT } std::vector<ProjectStore::Oplog::ChunkInfo> -ProjectStore::Oplog::GetAllChunksInfo() +ProjectStore::Oplog::GetAllChunksInfo(const std::filesystem::path& ProjectRootDir) { + ZEN_ASSERT(m_State == EState::kFull); + ZEN_MEMSCOPE(GetProjectstoreTag()); // First just capture all the chunk ids @@ -2322,7 +2403,7 @@ ProjectStore::Oplog::GetAllChunksInfo() for (ChunkInfo& Info : InfoArray) { - if (IoBuffer Chunk = FindChunk(Info.ChunkId, nullptr)) + if (IoBuffer Chunk = FindChunk(ProjectRootDir, Info.ChunkId, nullptr)) { Info.ChunkSize = Chunk.GetSize(); } @@ -2334,6 +2415,8 @@ ProjectStore::Oplog::GetAllChunksInfo() void ProjectStore::Oplog::IterateChunkMap(std::function<void(const Oid&, const IoHash&)>&& Fn) { + ZEN_ASSERT(m_State == EState::kFull); + RwLock::SharedLockScope _(m_OplogLock); if (!m_Storage) { @@ -2350,6 +2433,8 @@ void ProjectStore::Oplog::IterateFileMap( std::function<void(const Oid&, const std::string_view& ServerPath, const std::string_view& ClientPath)>&& Fn) { + ZEN_ASSERT(m_State == EState::kFull); + RwLock::SharedLockScope _(m_OplogLock); if (!m_Storage) { @@ -2476,7 +2561,7 @@ ProjectStore::Oplog::GetAttachmentsLocked(std::vector<IoHash>& OutAttachments, b { m_MetaValid = false; ZEN_WARN("oplog '{}/{}': unable to set meta data meta path: '{}'. Reason: '{}'", - m_OuterProject->Identifier, + m_OuterProjectId, m_OplogId, MetaPath, Ec.message()); @@ -2622,16 +2707,6 @@ ProjectStore::Oplog::GetOpByIndex(uint32_t Index) } void -ProjectStore::Oplog::AddChunkMappings(const std::unordered_map<Oid, IoHash, Oid::Hasher>& ChunkMappings) -{ - RwLock::ExclusiveLockScope OplogLock(m_OplogLock); - for (const auto& It : ChunkMappings) - { - AddChunkMapping(OplogLock, It.first, It.second); - } -} - -void ProjectStore::Oplog::CaptureAddedAttachments(std::span<const IoHash> AttachmentHashes) { ZEN_MEMSCOPE(GetProjectstoreTag()); @@ -2840,7 +2915,7 @@ ProjectStore::Oplog::GetMapping(CbObjectView Core) Oid Id = PackageObj["id"sv].AsObjectId(); IoHash Hash = PackageObj["data"sv].AsBinaryAttachment(); Result.Chunks.emplace_back(ChunkMapping{Id, Hash}); - ZEN_DEBUG("oplog {}/{}: package data {} -> {}", m_OuterProject->Identifier, m_OplogId, Id, Hash); + ZEN_DEBUG("oplog {}/{}: package data {} -> {}", m_OuterProjectId, m_OplogId, Id, Hash); continue; } if (FieldName == "bulkdata"sv) @@ -2852,7 +2927,7 @@ ProjectStore::Oplog::GetMapping(CbObjectView Core) Oid Id = BulkObj["id"sv].AsObjectId(); IoHash Hash = BulkObj["data"sv].AsBinaryAttachment(); Result.Chunks.emplace_back(ChunkMapping{Id, Hash}); - ZEN_DEBUG("oplog {}/{}: bulkdata {} -> {}", m_OuterProject->Identifier, m_OplogId, Id, Hash); + ZEN_DEBUG("oplog {}/{}: bulkdata {} -> {}", m_OuterProjectId, m_OplogId, Id, Hash); } continue; } @@ -2865,7 +2940,7 @@ ProjectStore::Oplog::GetMapping(CbObjectView Core) Oid Id = PackageDataObj["id"sv].AsObjectId(); IoHash Hash = PackageDataObj["data"sv].AsBinaryAttachment(); Result.Chunks.emplace_back(ChunkMapping{Id, Hash}); - ZEN_DEBUG("oplog {}/{}: package {} -> {}", m_OuterProject->Identifier, m_OplogId, Id, Hash); + ZEN_DEBUG("oplog {}/{}: package {} -> {}", m_OuterProjectId, m_OplogId, Id, Hash); } continue; } @@ -2884,23 +2959,20 @@ ProjectStore::Oplog::GetMapping(CbObjectView Core) if (ServerPath.empty() && Hash == IoHash::Zero) { ZEN_WARN("oplog {}/{}: invalid file for entry '{}', missing both 'serverpath' and 'data' fields", - m_OuterProject->Identifier, + m_OuterProjectId, m_OplogId, Id); continue; } if (ClientPath.empty()) { - ZEN_WARN("oplog {}/{}: invalid file for entry '{}', missing 'clientpath' field", - m_OuterProject->Identifier, - m_OplogId, - Id); + ZEN_WARN("oplog {}/{}: invalid file for entry '{}', missing 'clientpath' field", m_OuterProjectId, m_OplogId, Id); continue; } Result.Files.emplace_back(FileMapping{Id, Hash, std::string(ServerPath), std::string(ClientPath)}); ZEN_DEBUG("oplog {}/{}: file {} -> {}, ServerPath: {}, ClientPath: {}", - m_OuterProject->Identifier, + m_OuterProjectId, m_OplogId, Id, Hash, @@ -2920,7 +2992,7 @@ ProjectStore::Oplog::GetMapping(CbObjectView Core) IoHash Hash = MetaObj["data"sv].AsBinaryAttachment(); Result.Meta.emplace_back(ChunkMapping{Id, Hash}); auto NameString = MetaObj["name"sv].AsString(); - ZEN_DEBUG("oplog {}/{}: meta data ({}) {} -> {}", m_OuterProject->Identifier, m_OplogId, NameString, Id, Hash); + ZEN_DEBUG("oplog {}/{}: meta data ({}) {} -> {}", m_OuterProjectId, m_OplogId, NameString, Id, Hash); } continue; } @@ -2964,6 +3036,8 @@ ProjectStore::Oplog::RegisterOplogEntry(RwLock::ExclusiveLockScope& OplogLock, uint32_t ProjectStore::Oplog::AppendNewOplogEntry(CbPackage OpPackage) { + ZEN_ASSERT(m_State == EState::kFull); + ZEN_MEMSCOPE(GetProjectstoreTag()); ZEN_TRACE_CPU("Store::Oplog::AppendNewOplogEntry"); @@ -3034,6 +3108,8 @@ ProjectStore::Oplog::GetStorage() uint32_t ProjectStore::Oplog::AppendNewOplogEntry(CbObjectView Core) { + ZEN_ASSERT(m_State == EState::kFull); + ZEN_MEMSCOPE(GetProjectstoreTag()); ZEN_TRACE_CPU("Store::Oplog::AppendNewOplogEntry"); @@ -3064,6 +3140,8 @@ ProjectStore::Oplog::AppendNewOplogEntry(CbObjectView Core) std::vector<uint32_t> ProjectStore::Oplog::AppendNewOplogEntries(std::span<CbObjectView> Cores) { + ZEN_ASSERT(m_State == EState::kFull); + ZEN_MEMSCOPE(GetProjectstoreTag()); ZEN_TRACE_CPU("Store::Oplog::AppendNewOplogEntries"); @@ -3321,7 +3399,7 @@ ProjectStore::Project::BasePathForOplog(std::string_view OplogId) const return m_OplogStoragePath / OplogId; } -ProjectStore::Oplog* +Ref<ProjectStore::Oplog> ProjectStore::Project::NewOplog(std::string_view OplogId, const std::filesystem::path& MarkerPath) { ZEN_MEMSCOPE(GetProjectstoreTag()); @@ -3331,21 +3409,25 @@ ProjectStore::Project::NewOplog(std::string_view OplogId, const std::filesystem: try { - ZEN_INFO("oplog '{}/{}': creating oplog at '{}'", Identifier, OplogId, OplogBasePath); + Stopwatch Timer; - Oplog* Log = m_Oplogs - .try_emplace(std::string{OplogId}, - std::make_unique<ProjectStore::Oplog>(OplogId, this, m_CidStore, OplogBasePath, MarkerPath)) - .first->second.get(); + Ref<Oplog> NewLog(new Oplog(Log(), Identifier, OplogId, m_CidStore, OplogBasePath, MarkerPath)); + m_Oplogs.insert_or_assign(std::string{OplogId}, NewLog); - Log->Write(); + NewLog->Write(); + + ZEN_INFO("oplog '{}/{}': created oplog at '{}' in {}", + Identifier, + OplogId, + OplogBasePath, + NiceTimeSpanMs(Timer.GetElapsedTimeMs())); if (m_CapturedOplogs) { m_CapturedOplogs->push_back(std::string(OplogId)); } - return Log; + return NewLog; } catch (const std::exception&) { @@ -3355,11 +3437,11 @@ ProjectStore::Project::NewOplog(std::string_view OplogId, const std::filesystem: m_Oplogs.erase(std::string{OplogId}); - return nullptr; + return {}; } } -ProjectStore::Oplog* +Ref<ProjectStore::Oplog> ProjectStore::Project::OpenOplog(std::string_view OplogId, bool AllowCompact, bool VerifyPathOnDisk) { ZEN_MEMSCOPE(GetProjectstoreTag()); @@ -3394,7 +3476,7 @@ ProjectStore::Project::OpenOplog(std::string_view OplogId, bool AllowCompact, bo if (!ReOpen) { - return OplogIt->second.get(); + return OplogIt->second; } } } @@ -3404,30 +3486,35 @@ ProjectStore::Project::OpenOplog(std::string_view OplogId, bool AllowCompact, bo RwLock::ExclusiveLockScope Lock(m_ProjectLock); if (auto It = m_Oplogs.find(std::string{OplogId}); It != m_Oplogs.end()) { - return It->second.get(); + return It->second; } if (Oplog::ExistsAt(OplogBasePath)) { try { - ZEN_INFO("oplog '{}/{}': opening oplog at '{}'", Identifier, OplogId, OplogBasePath); + Stopwatch Timer; + + Ref<Oplog> ExistingLog( + new Oplog(m_ProjectStore->Log(), Identifier, OplogId, m_CidStore, OplogBasePath, std::filesystem::path{})); - Oplog* Log = - m_Oplogs - .try_emplace(std::string{OplogId}, - std::make_unique<ProjectStore::Oplog>(OplogId, this, m_CidStore, OplogBasePath, std::filesystem::path{})) - .first->second.get(); - Log->Read(); + m_Oplogs.insert_or_assign(std::string{OplogId}, ExistingLog); + ExistingLog->Read(Oplog::EState::kFull); Lock.ReleaseNow(); + ZEN_INFO("oplog '{}/{}': opened oplog at '{}' in {}", + Identifier, + OplogId, + OplogBasePath, + NiceTimeSpanMs(Timer.GetElapsedTimeMs())); + if (AllowCompact) { const uint32_t CompactUnusedThreshold = 50; - Log->CompactIfUnusedExceeds(/*DryRun*/ false, - CompactUnusedThreshold, - fmt::format("Compact on initial open of oplog {}/{}: ", Identifier, OplogId)); + ExistingLog->CompactIfUnusedExceeds(/*DryRun*/ false, + CompactUnusedThreshold, + fmt::format("Compact on initial open of oplog {}/{}: ", Identifier, OplogId)); } - return Log; + return ExistingLog; } catch (const std::exception& Ex) { @@ -3436,7 +3523,7 @@ ProjectStore::Project::OpenOplog(std::string_view OplogId, bool AllowCompact, bo } } - return nullptr; + return {}; } void @@ -3460,6 +3547,27 @@ ProjectStore::Oplog::CompactIfUnusedExceeds(bool DryRun, uint32_t CompactUnusedT } bool +ProjectStore::Project::TryUnloadOplog(std::string_view OplogId) +{ + ZEN_MEMSCOPE(GetProjectstoreTag()); + + RwLock::ExclusiveLockScope _(m_ProjectLock); + if (auto OplogIt = m_Oplogs.find(std::string(OplogId)); OplogIt != m_Oplogs.end()) + { + Ref<Oplog>& Oplog = OplogIt->second; + + if (Oplog->CanUnload()) + { + m_Oplogs.erase(OplogIt); + return true; + } + return false; + } + + return false; +} + +bool ProjectStore::Project::RemoveOplog(std::string_view OplogId, std::filesystem::path& OutDeletePath) { ZEN_MEMSCOPE(GetProjectstoreTag()); @@ -3480,12 +3588,11 @@ ProjectStore::Project::RemoveOplog(std::string_view OplogId, std::filesystem::pa } else { - std::unique_ptr<Oplog>& Oplog = OplogIt->second; + Ref<Oplog>& Oplog = OplogIt->second; if (!Oplog->PrepareForDelete(OutDeletePath)) { return false; } - m_DeletedOplogs.emplace_back(std::move(Oplog)); m_Oplogs.erase(OplogIt); } } @@ -3638,7 +3745,6 @@ ProjectStore::Project::PrepareForDelete(std::filesystem::path& OutDeletePath) for (auto& It : m_Oplogs) { It.second->ResetState(); - m_DeletedOplogs.emplace_back(std::move(It.second)); } m_Oplogs.clear(); @@ -3790,7 +3896,7 @@ ProjectStore::Project::IsExpired(const GcClock::TimePoint ExpireTime, std::strin if (OplogIt != m_Oplogs.end()) { Lock.ReleaseNow(); - return IsExpired(ExpireTime, *OplogIt->second.get()); + return IsExpired(ExpireTime, *OplogIt->second); } } @@ -4220,7 +4326,7 @@ ProjectStore::GetProjectFiles(const std::string_view ProjectId, } Project->TouchProject(); - ProjectStore::Oplog* FoundLog = Project->OpenOplog(OplogId, /*AllowCompact*/ true, /*VerifyPathOnDisk*/ true); + Ref<ProjectStore::Oplog> FoundLog = Project->OpenOplog(OplogId, /*AllowCompact*/ true, /*VerifyPathOnDisk*/ true); if (!FoundLog) { return {HttpResponseCode::NotFound, fmt::format("Project files for unknown oplog '{}/{}'", ProjectId, OplogId)}; @@ -4269,6 +4375,7 @@ ProjectStore::GetProjectFiles(const std::string_view ProjectId, } FoundLog->IterateChunks( + Project->RootDir, Ids, false, [&](size_t Index, const IoBuffer& Payload, uint64_t /*ModTag*/) { @@ -4384,7 +4491,7 @@ ProjectStore::GetProjectChunkInfos(const std::string_view ProjectId, } Project->TouchProject(); - ProjectStore::Oplog* FoundLog = Project->OpenOplog(OplogId, /*AllowCompact*/ true, /*VerifyPathOnDisk*/ true); + Ref<ProjectStore::Oplog> FoundLog = Project->OpenOplog(OplogId, /*AllowCompact*/ true, /*VerifyPathOnDisk*/ true); if (!FoundLog) { return {HttpResponseCode::NotFound, fmt::format("unknown oplog '{}/{}'", ProjectId, OplogId)}; @@ -4549,7 +4656,7 @@ ProjectStore::GetChunkInfo(const std::string_view ProjectId, } Project->TouchProject(); - ProjectStore::Oplog* FoundLog = Project->OpenOplog(OplogId, /*AllowCompact*/ false, /*VerifyPathOnDisk*/ false); + Ref<ProjectStore::Oplog> FoundLog = Project->OpenOplog(OplogId, /*AllowCompact*/ false, /*VerifyPathOnDisk*/ false); if (!FoundLog) { return {HttpResponseCode::NotFound, fmt::format("Chunk info request for unknown oplog '{}/{}'", ProjectId, OplogId)}; @@ -4564,7 +4671,7 @@ ProjectStore::GetChunkInfo(const std::string_view ProjectId, const Oid Obj = Oid::FromHexString(ChunkId); - IoBuffer Chunk = FoundLog->FindChunk(Obj, nullptr); + IoBuffer Chunk = FoundLog->FindChunk(Project->RootDir, Obj, nullptr); if (!Chunk) { return {HttpResponseCode::NotFound, {}}; @@ -4744,7 +4851,7 @@ ProjectStore::GetChunkRange(const std::string_view ProjectId, } Project->TouchProject(); - ProjectStore::Oplog* FoundLog = Project->OpenOplog(OplogId, /*AllowCompact*/ false, /*VerifyPathOnDisk*/ false); + Ref<ProjectStore::Oplog> FoundLog = Project->OpenOplog(OplogId, /*AllowCompact*/ false, /*VerifyPathOnDisk*/ false); if (!FoundLog) { return {HttpResponseCode::NotFound, fmt::format("Chunk request for unknown oplog '{}/{}'", ProjectId, OplogId)}; @@ -4752,7 +4859,7 @@ ProjectStore::GetChunkRange(const std::string_view ProjectId, Project->TouchOplog(OplogId); uint64_t OldTag = OptionalInOutModificationTag == nullptr ? 0 : *OptionalInOutModificationTag; - IoBuffer Chunk = FoundLog->FindChunk(ChunkId, OptionalInOutModificationTag); + IoBuffer Chunk = FoundLog->FindChunk(Project->RootDir, ChunkId, OptionalInOutModificationTag); if (!Chunk) { return {HttpResponseCode::NotFound, {}}; @@ -4789,7 +4896,7 @@ ProjectStore::GetChunk(const std::string_view ProjectId, } Project->TouchProject(); - ProjectStore::Oplog* FoundLog = Project->OpenOplog(OplogId, /*AllowCompact*/ false, /*VerifyPathOnDisk*/ false); + Ref<ProjectStore::Oplog> FoundLog = Project->OpenOplog(OplogId, /*AllowCompact*/ false, /*VerifyPathOnDisk*/ false); if (!FoundLog) { return {HttpResponseCode::NotFound, fmt::format("Chunk request for unknown oplog '{}/{}'", ProjectId, OplogId)}; @@ -4838,7 +4945,7 @@ ProjectStore::PutChunk(const std::string_view ProjectId, } Project->TouchProject(); - ProjectStore::Oplog* FoundLog = Project->OpenOplog(OplogId, /*AllowCompact*/ false, /*VerifyPathOnDisk*/ false); + Ref<ProjectStore::Oplog> FoundLog = Project->OpenOplog(OplogId, /*AllowCompact*/ false, /*VerifyPathOnDisk*/ false); if (!FoundLog) { return {HttpResponseCode::NotFound, fmt::format("Chunk put request for unknown oplog '{}/{}'", ProjectId, OplogId)}; @@ -4887,7 +4994,7 @@ ProjectStore::GetChunks(const std::string_view ProjectId, } Project->TouchProject(); - ProjectStore::Oplog* FoundLog = Project->OpenOplog(OplogId, /*AllowCompact*/ true, /*VerifyPathOnDisk*/ false); + Ref<ProjectStore::Oplog> FoundLog = Project->OpenOplog(OplogId, /*AllowCompact*/ true, /*VerifyPathOnDisk*/ false); if (!FoundLog) { return {HttpResponseCode::NotFound, fmt::format("getchunks rpc request for unknown oplog '{}/{}'", ProjectId, OplogId)}; @@ -5031,6 +5138,7 @@ ProjectStore::GetChunks(const std::string_view ProjectId, if (!ChunkIdsRequestIndex.empty()) { FoundLog->IterateChunks( + Project->RootDir, ChunkIds, true, [&](size_t Index, const IoBuffer& Payload, uint64_t ModTag) -> bool { @@ -5072,7 +5180,7 @@ ProjectStore::GetChunks(const std::string_view ProjectId, { const Oid& ChunkId = std::get<Oid>(ChunkRequest.Input.Id); uint64_t ModTag = 0; - IoBuffer Payload = FoundLog->FindChunk(ChunkId, &ModTag); + IoBuffer Payload = FoundLog->FindChunk(Project->RootDir, ChunkId, &ModTag); if (Payload) { ChunkRequest.Output.Exists = true; @@ -5231,7 +5339,7 @@ ProjectStore::WriteOplog(const std::string_view ProjectId, const std::string_vie } Project->TouchProject(); - ProjectStore::Oplog* Oplog = Project->OpenOplog(OplogId, /*AllowCompact*/ true, /*VerifyPathOnDisk*/ false); + Ref<ProjectStore::Oplog> Oplog = Project->OpenOplog(OplogId, /*AllowCompact*/ true, /*VerifyPathOnDisk*/ false); if (!Oplog) { return {HttpResponseCode::NotFound, fmt::format("Write oplog request for unknown oplog '{}/{}'", ProjectId, OplogId)}; @@ -5317,7 +5425,7 @@ ProjectStore::ReadOplog(const std::string_view ProjectId, } Project->TouchProject(); - ProjectStore::Oplog* Oplog = Project->OpenOplog(OplogId, /*AllowCompact*/ true, /*VerifyPathOnDisk*/ true); + Ref<ProjectStore::Oplog> Oplog = Project->OpenOplog(OplogId, /*AllowCompact*/ true, /*VerifyPathOnDisk*/ true); if (!Oplog) { return {HttpResponseCode::NotFound, fmt::format("Read oplog request for unknown oplog '{}/{}'", ProjectId, OplogId)}; @@ -5452,7 +5560,7 @@ ProjectStore::Rpc(HttpServerRequest& HttpReq, bool VerifyPathOnDisk = Method != "getchunks"sv; - ProjectStore::Oplog* Oplog = Project->OpenOplog(OplogId, /*AllowCompact*/ false, VerifyPathOnDisk); + Ref<ProjectStore::Oplog> Oplog = Project->OpenOplog(OplogId, /*AllowCompact*/ false, VerifyPathOnDisk); if (!Oplog) { HttpReq.WriteResponse(HttpResponseCode::NotFound, @@ -5842,7 +5950,7 @@ ProjectStore::Import(ProjectStore::Project& Project, ProjectStore::Oplog& Oplog, IgnoreMissingAttachments, CleanOplog](JobContext& Context) { Context.ReportMessage(fmt::format("Loading oplog '{}/{}' from {}", - OplogPtr->GetOuterProject()->Identifier, + OplogPtr->GetOuterProjectIdentifier(), OplogPtr->OplogId(), ActualRemoteStore->GetInfo().Description)); @@ -5995,19 +6103,34 @@ public: CompactOplogCount += OplogsToCompact.size(); for (const std::string& OplogId : OplogsToCompact) { - ProjectStore::Oplog* OpLog = nullptr; + Ref<ProjectStore::Oplog> OpLog; { RwLock::SharedLockScope __(Project->m_ProjectLock); if (auto OpIt = Project->m_Oplogs.find(OplogId); OpIt != Project->m_Oplogs.end()) { - OpLog = OpIt->second.get(); + OpLog = OpIt->second; } else { + Stopwatch OplogTimer; std::filesystem::path OplogBasePath = Project->BasePathForOplog(OplogId); - OpLog = - new ProjectStore::Oplog(OplogId, Project.Get(), Project->m_CidStore, OplogBasePath, std::filesystem::path{}); - OpLog->Read(); + OpLog = new ProjectStore::Oplog(Project->Log(), + Project->Identifier, + OplogId, + Project->m_CidStore, + OplogBasePath, + std::filesystem::path{}); + OpLog->Read( + ProjectStore::Oplog::EState::kFull); // We need it to be a full read so we can write a new index snapshot + if (Ctx.Settings.Verbose) + { + ZEN_INFO("GCV2: projectstore [COMPACT] '{}': opened oplog '{}/{}' at '{}' in {}", + m_BasePath, + Project->Identifier, + OplogId, + OplogBasePath, + NiceTimeSpanMs(OplogTimer.GetElapsedTimeMs())); + } } if (OpLog) @@ -6024,11 +6147,6 @@ public: Stats.RemovedDisk += FreedSize; } - - if (auto OpIt = Project->m_Oplogs.find(OplogId); OpIt == Project->m_Oplogs.end()) - { - delete OpLog; - } } } } @@ -6117,6 +6235,16 @@ ProjectStore::RemoveExpiredData(GcCtx& Ctx, GcStats& Stats) { ExpiredOplogs.push_back(OplogId); } + else if (!Project->IsOplogTouchedSince(GcClock::Now() - std::chrono::minutes(15), OplogId)) + { + if (Project->TryUnloadOplog(OplogId)) + { + ZEN_INFO("GCV2: projectstore [REMOVE EXPIRED] '{}': Unloaded oplog {}/{} due to inactivity", + m_ProjectBasePath, + Project->Identifier, + OplogId); + } + } } std::filesystem::path ProjectPath = BasePathForProject(Project->Identifier); @@ -6208,7 +6336,7 @@ public: Stopwatch Timer; - std::vector<ProjectStore::Oplog*> AddedOplogs; + std::vector<Ref<ProjectStore::Oplog>> AddedOplogs; const auto _ = MakeGuard([&] { if (!Ctx.Settings.Verbose) @@ -6230,7 +6358,7 @@ public: ProjectStore::Project& Project = *It->second; for (auto& OplogPair : Project.m_Oplogs) { - ProjectStore::Oplog* Oplog = OplogPair.second.get(); + Ref<ProjectStore::Oplog> Oplog = OplogPair.second; AddedOplogs.push_back(Oplog); } } @@ -6244,13 +6372,13 @@ public: { if (auto It = Project.m_Oplogs.find(OplogName); It != Project.m_Oplogs.end()) { - ProjectStore::Oplog* Oplog = It->second.get(); + Ref<ProjectStore::Oplog> Oplog = It->second; AddedOplogs.push_back(Oplog); } } } - for (ProjectStore::Oplog* Oplog : AddedOplogs) + for (const Ref<ProjectStore::Oplog>& Oplog : AddedOplogs) { size_t BaseReferenceCount = m_References.size(); @@ -6326,13 +6454,14 @@ public: { m_Project->DisableUpdateCapture(); - RwLock::SharedLockScope _(m_Project->m_ProjectLock); - if (auto It = m_Project->m_Oplogs.find(m_OplogId); It != m_Project->m_Oplogs.end()) + if (m_OplogHasUpdateCapture) { - ProjectStore::Oplog* Oplog = It->second.get(); - if (Oplog == m_OplogWithUpdateCapture) + RwLock::SharedLockScope _(m_Project->m_ProjectLock); + if (auto It = m_Project->m_Oplogs.find(m_OplogId); It != m_Project->m_Oplogs.end()) { + Ref<ProjectStore::Oplog> Oplog = It->second; Oplog->DisableUpdateCapture(); + m_OplogHasUpdateCapture = false; } } } @@ -6364,50 +6493,60 @@ public: m_OplogId); }); - ProjectStore::Oplog* Oplog = nullptr; - auto __ = MakeGuard([this, &Oplog]() { - if (Oplog != nullptr && m_OplogWithUpdateCapture == nullptr) - { - delete Oplog; - } - }); - m_OplogBasePath = m_Project->BasePathForOplog(m_OplogId); - - RwLock::SharedLockScope ___(m_Project->m_ProjectLock); - if (auto It = m_Project->m_Oplogs.find(m_OplogId); It != m_Project->m_Oplogs.end()) - { - It->second->EnableUpdateCapture(); - Oplog = It->second.get(); - m_OplogWithUpdateCapture = Oplog; - } - else if (ProjectStore::Oplog::ExistsAt(m_OplogBasePath)) + m_OplogBasePath = m_Project->BasePathForOplog(m_OplogId); { - Oplog = new ProjectStore::Oplog(m_OplogId, m_Project.Get(), m_Project->m_CidStore, m_OplogBasePath, std::filesystem::path{}); - Oplog->Read(); - } - else - { - return; - } + Ref<ProjectStore::Oplog> Oplog; - RwLock::SharedLockScope ____(Oplog->m_OplogLock); - if (Ctx.IsCancelledFlag) - { - return; - } + RwLock::SharedLockScope __(m_Project->m_ProjectLock); + if (auto It = m_Project->m_Oplogs.find(m_OplogId); It != m_Project->m_Oplogs.end()) + { + Oplog = It->second; + Oplog->EnableUpdateCapture(); + m_OplogHasUpdateCapture = true; + } + else if (ProjectStore::Oplog::ExistsAt(m_OplogBasePath)) + { + Stopwatch OplogTimer; + Oplog = new ProjectStore::Oplog(m_Project->Log(), + m_Project->Identifier, + m_OplogId, + m_Project->m_CidStore, + m_OplogBasePath, + std::filesystem::path{}); + Oplog->Read(ProjectStore::Oplog::EState::kBasicNoLookups); + if (Ctx.Settings.Verbose) + { + ZEN_INFO("GCV2: projectstore [PRECACHE] '{}': opened oplog '{}/{}' in {}", + m_OplogBasePath, + m_Project->Identifier, + m_OplogId, + NiceTimeSpanMs(OplogTimer.GetElapsedTimeMs())); + } + } + else + { + return; + } - GcClock::TimePoint CompactExpireTime = GcClock::Now() - std::chrono::minutes(30); - if (!m_Project->IsOplogTouchedSince(CompactExpireTime, m_OplogId)) - { - const uint32_t CompactUnusedThreshold = 25; - if (Oplog->GetUnusedSpacePercent() >= CompactUnusedThreshold) + RwLock::SharedLockScope ___(Oplog->m_OplogLock); + if (Ctx.IsCancelledFlag) { - m_Project->AddOplogToCompact(m_OplogId); + return; + } + + GcClock::TimePoint CompactExpireTime = GcClock::Now() - std::chrono::minutes(30); + if (!m_Project->IsOplogTouchedSince(CompactExpireTime, m_OplogId)) + { + const uint32_t CompactUnusedThreshold = 25; + if (Oplog->GetUnusedSpacePercent() >= CompactUnusedThreshold) + { + m_Project->AddOplogToCompact(m_OplogId); + } } - } - Oplog->GetAttachmentsLocked(m_References, Ctx.Settings.StoreProjectAttachmentMetaData); - m_OplogAccessTime = m_Project->LastOplogAccessTime(m_OplogId); + Oplog->GetAttachmentsLocked(m_References, Ctx.Settings.StoreProjectAttachmentMetaData); + m_OplogAccessTime = m_Project->LastOplogAccessTime(m_OplogId); + } FilterReferences(Ctx, fmt::format("projectstore [PRECACHE] '{}'", m_OplogBasePath), m_References); } @@ -6433,7 +6572,7 @@ public: if (auto It = m_Project->m_Oplogs.find(m_OplogId); It != m_Project->m_Oplogs.end()) { - ProjectStore::Oplog* Oplog = It->second.get(); + Ref<ProjectStore::Oplog> Oplog = It->second; Oplog->IterateCapturedLSNsLocked([&](const CbObjectView& UpdateOp) -> bool { UpdateOp.IterateAttachments([&](CbFieldView Visitor) { m_AddedReferences.emplace_back(Visitor.AsAttachment()); }); return true; @@ -6447,16 +6586,36 @@ public: } else if (m_Project->LastOplogAccessTime(m_OplogId) > m_OplogAccessTime && ProjectStore::Oplog::ExistsAt(m_OplogBasePath)) { - ProjectStore::Oplog* Oplog = - new ProjectStore::Oplog(m_OplogId, m_Project.Get(), m_Project->m_CidStore, m_OplogBasePath, std::filesystem::path{}); - auto __ = MakeGuard([Oplog]() { - if (Oplog != nullptr) + Stopwatch OplogTimer; + { + Ref<ProjectStore::Oplog> Oplog(new ProjectStore::Oplog(m_Project->Log(), + m_Project->Identifier, + m_OplogId, + m_Project->m_CidStore, + m_OplogBasePath, + std::filesystem::path{})); + Oplog->Read(ProjectStore::Oplog::EState::kBasicNoLookups); + if (Ctx.Settings.Verbose) { - delete Oplog; + ZEN_INFO("GCV2: projectstore [LOCKSTATE] '{}': opened oplog '{}/{}' in {}", + m_OplogBasePath, + m_Project->Identifier, + m_OplogId, + NiceTimeSpanMs(OplogTimer.GetElapsedTimeMs())); } - }); - Oplog->Read(); - Oplog->GetAttachmentsLocked(m_AddedReferences, Ctx.Settings.StoreProjectAttachmentMetaData); + + OplogTimer.Reset(); + + Oplog->GetAttachmentsLocked(m_AddedReferences, Ctx.Settings.StoreProjectAttachmentMetaData); + } + if (Ctx.Settings.Verbose) + { + ZEN_INFO("GCV2: projectstore [LOCKSTATE] '{}': read referenced attachments from oplog '{}/{}' in {}", + m_OplogBasePath, + m_Project->Identifier, + m_OplogId, + NiceTimeSpanMs(OplogTimer.GetElapsedTimeMs())); + } } FilterReferences(Ctx, fmt::format("projectstore [LOCKSTATE] '{}'", m_OplogBasePath), m_AddedReferences); } @@ -6495,7 +6654,7 @@ public: Ref<ProjectStore::Project> m_Project; std::string m_OplogId; std::filesystem::path m_OplogBasePath; - ProjectStore::Oplog* m_OplogWithUpdateCapture = nullptr; + bool m_OplogHasUpdateCapture = false; std::vector<IoHash> m_References; std::vector<IoHash> m_AddedReferences; GcClock::TimePoint m_OplogAccessTime; @@ -6608,7 +6767,7 @@ public: ProjectStore::Oplog::ValidationResult Result; Stopwatch Timer; - const auto _ = MakeGuard([&] { + const auto _ = MakeGuard([&] { if (!Ctx.Settings.Verbose) { return; @@ -6623,38 +6782,47 @@ public: Result.LSNHigh, Status); }); - ProjectStore::Oplog* TempOplog = nullptr; - auto __ = MakeGuard([this, &TempOplog]() { - if (TempOplog != nullptr) - { - delete TempOplog; - } - }); - ProjectStore::Oplog* Oplog = nullptr; - Ref<ProjectStore::Project> Project = m_ProjectStore.OpenProject(m_ProjectId); + Ref<ProjectStore::Oplog> Oplog; + Ref<ProjectStore::Project> Project = m_ProjectStore.OpenProject(m_ProjectId); if (Project) { - RwLock::SharedLockScope ___(Project->m_ProjectLock); - if (auto It = Project->m_Oplogs.find(m_OplogId); It != Project->m_Oplogs.end()) { - Oplog = It->second.get(); - } - else - { - std::filesystem::path OplogBasePath = Project->BasePathForOplog(m_OplogId); - TempOplog = new ProjectStore::Oplog(m_OplogId, Project.Get(), Project->m_CidStore, OplogBasePath, std::filesystem::path{}); - Oplog = TempOplog; - Oplog->Read(); - - if (Ctx.IsCancelledFlag) + RwLock::SharedLockScope __(Project->m_ProjectLock); + if (auto It = Project->m_Oplogs.find(m_OplogId); It != Project->m_Oplogs.end()) { - return; + Oplog = It->second; + } + else + { + Stopwatch OplogTimer; + + std::filesystem::path OplogBasePath = Project->BasePathForOplog(m_OplogId); + Oplog = Ref<ProjectStore::Oplog>(new ProjectStore::Oplog(Project->Log(), + Project->Identifier, + m_OplogId, + Project->m_CidStore, + OplogBasePath, + std::filesystem::path{})); + Oplog->Read(ProjectStore::Oplog::EState::kBasicNoLookups); + if (Ctx.Settings.Verbose) + { + ZEN_INFO("GCV2: projectstore [VALIDATE] '{}': opened oplog '{}/{}' in {}", + OplogBasePath, + Project->Identifier, + m_OplogId, + NiceTimeSpanMs(OplogTimer.GetElapsedTimeMs())); + } + + if (Ctx.IsCancelledFlag) + { + return; + } } } - if (Oplog != nullptr) + if (Oplog) { - Result = Oplog->Validate(Ctx.IsCancelledFlag, nullptr); + Result = Oplog->Validate(Project->RootDir, Ctx.IsCancelledFlag, nullptr); if (Ctx.IsCancelledFlag) { return; @@ -6691,6 +6859,9 @@ ProjectStore::CreateReferenceValidators(GcCtx& Ctx) { return {}; } + + auto Log = [&Ctx]() { return Ctx.Logger; }; + DiscoverProjects(); std::vector<std::pair<std::string, std::string>> Oplogs; @@ -6996,13 +7167,13 @@ TEST_CASE("project.store.lifetimes") EngineRootDir.string(), ProjectRootDir.string(), ProjectFilePath.string())); - ProjectStore::Oplog* Oplog = Project->NewOplog("oplog1", {}); - CHECK(Oplog != nullptr); + Ref<ProjectStore::Oplog> Oplog = Project->NewOplog("oplog1", {}); + CHECK(Oplog); std::filesystem::path DeletePath; CHECK(Project->PrepareForDelete(DeletePath)); CHECK(!DeletePath.empty()); - CHECK(Project->OpenOplog("oplog1", /*AllowCompact*/ false, /*VerifyPathOnDisk*/ true) == nullptr); + CHECK(!Project->OpenOplog("oplog1", /*AllowCompact*/ false, /*VerifyPathOnDisk*/ true)); // Oplog is now invalid, but pointer can still be accessed since we store old oplog pointers CHECK(Oplog->OplogCount() == 0); // Project is still valid since we have a Ref to it @@ -7059,8 +7230,8 @@ TEST_CASE_TEMPLATE("project.store.export", EngineRootDir.string(), ProjectRootDir.string(), ProjectFilePath.string())); - ProjectStore::Oplog* Oplog = Project->NewOplog("oplog1", {}); - CHECK(Oplog != nullptr); + Ref<ProjectStore::Oplog> Oplog = Project->NewOplog("oplog1", {}); + CHECK(Oplog); Oplog->AppendNewOplogEntry(CreateBulkDataOplogPackage(Oid::NewOid(), {})); Oplog->AppendNewOplogEntry(CreateBulkDataOplogPackage(Oid::NewOid(), CreateAttachments(std::initializer_list<size_t>{77}))); @@ -7095,8 +7266,8 @@ TEST_CASE_TEMPLATE("project.store.export", CHECK(ExportResult.ErrorCode == 0); - ProjectStore::Oplog* OplogImport = Project->NewOplog("oplog2", {}); - CHECK(OplogImport != nullptr); + Ref<ProjectStore::Oplog> OplogImport = Project->NewOplog("oplog2", {}); + CHECK(OplogImport); RemoteProjectStore::Result ImportResult = LoadOplog(CidStore, *RemoteStore, @@ -7195,8 +7366,8 @@ TEST_CASE("project.store.gc") EngineRootDir.string(), Project1RootDir.string(), Project1FilePath.string())); - ProjectStore::Oplog* Oplog = Project1->NewOplog("oplog1", Project1OplogPath); - CHECK(Oplog != nullptr); + Ref<ProjectStore::Oplog> Oplog = Project1->NewOplog("oplog1", Project1OplogPath); + CHECK(Oplog); Oplog->AppendNewOplogEntry(CreateBulkDataOplogPackage(Oid::NewOid(), {})); Oplog->AppendNewOplogEntry(CreateBulkDataOplogPackage(Oid::NewOid(), CreateAttachments(std::initializer_list<size_t>{77}))); @@ -7213,8 +7384,8 @@ TEST_CASE("project.store.gc") Project2RootDir.string(), Project2FilePath.string())); { - ProjectStore::Oplog* Oplog = Project2->NewOplog("oplog2", Project2Oplog1Path); - CHECK(Oplog != nullptr); + Ref<ProjectStore::Oplog> Oplog = Project2->NewOplog("oplog2", Project2Oplog1Path); + CHECK(Oplog); Oplog->AppendNewOplogEntry(CreateBulkDataOplogPackage(Oid::NewOid(), {})); Oplog->AppendNewOplogEntry(CreateBulkDataOplogPackage(Oid::NewOid(), CreateAttachments(std::initializer_list<size_t>{177}))); @@ -7224,8 +7395,8 @@ TEST_CASE("project.store.gc") CreateBulkDataOplogPackage(Oid::NewOid(), CreateAttachments(std::initializer_list<size_t>{535, 221}))); } { - ProjectStore::Oplog* Oplog = Project2->NewOplog("oplog3", Project2Oplog2Path); - CHECK(Oplog != nullptr); + Ref<ProjectStore::Oplog> Oplog = Project2->NewOplog("oplog3", Project2Oplog2Path); + CHECK(Oplog); Oplog->AppendNewOplogEntry(CreateBulkDataOplogPackage(Oid::NewOid(), {})); Oplog->AppendNewOplogEntry(CreateBulkDataOplogPackage(Oid::NewOid(), CreateAttachments(std::initializer_list<size_t>{137}))); @@ -7383,7 +7554,7 @@ TEST_CASE("project.store.gc.prep") EngineRootDir.string(), Project1RootDir.string(), Project1FilePath.string())); - ProjectStore::Oplog* Oplog = Project1->NewOplog("oplog1"sv, Project1OplogPath); + Ref<ProjectStore::Oplog> Oplog = Project1->NewOplog("oplog1"sv, Project1OplogPath); Oplog->AppendNewOplogEntry(CreateBulkDataOplogPackage(Oid::NewOid(), OpAttachments)); } { @@ -7414,13 +7585,13 @@ TEST_CASE("project.store.gc.prep") { // Make sure the chunks are stored but not the referencing op Ref<ProjectStore::Project> Project1 = ProjectStore.OpenProject("proj1"sv); - ProjectStore::Oplog* Oplog = Project1->NewOplog("oplog1"sv, Project1OplogPath); + Ref<ProjectStore::Oplog> Oplog = Project1->NewOplog("oplog1"sv, Project1OplogPath); Oplog->AppendNewOplogEntry(CreateBulkDataOplogPackage(Oid::NewOid(), OpAttachments)); Project1->DeleteOplog("oplog1"sv); } { Ref<ProjectStore::Project> Project1 = ProjectStore.OpenProject("proj1"sv); - ProjectStore::Oplog* Oplog = Project1->NewOplog("oplog1"sv, Project1OplogPath); + Ref<ProjectStore::Oplog> Oplog = Project1->NewOplog("oplog1"sv, Project1OplogPath); // Equivalent of a `prep` call with tracking of ops CHECK(Oplog->CheckPendingChunkReferences(OpChunkHashes, std::chrono::hours(1)).empty()); @@ -7461,7 +7632,7 @@ TEST_CASE("project.store.gc.prep") { Ref<ProjectStore::Project> Project1 = ProjectStore.OpenProject("proj1"sv); - ProjectStore::Oplog* Oplog = Project1->OpenOplog("oplog1"sv, true, true); + Ref<ProjectStore::Oplog> Oplog = Project1->OpenOplog("oplog1"sv, true, true); Oplog->AppendNewOplogEntry(CreateBulkDataOplogPackage(Oid::NewOid(), OpAttachments)); Oplog->RemovePendingChunkReferences(OpChunkHashes); CHECK(Oplog->GetPendingChunkReferencesLocked().size() == 0); @@ -7495,7 +7666,7 @@ TEST_CASE("project.store.gc.prep") { // Make sure the chunks are stored but not the referencing op Ref<ProjectStore::Project> Project1 = ProjectStore.OpenProject("proj1"sv); - ProjectStore::Oplog* Oplog = Project1->NewOplog("oplog1"sv, Project1OplogPath); + Ref<ProjectStore::Oplog> Oplog = Project1->NewOplog("oplog1"sv, Project1OplogPath); Oplog->AppendNewOplogEntry(CreateBulkDataOplogPackage(Oid::NewOid(), OpAttachments)); Project1->DeleteOplog("oplog1"sv); } @@ -7503,7 +7674,7 @@ TEST_CASE("project.store.gc.prep") // Caution - putting breakpoints and stepping through this part of the test likely makes it fails due to expiry time of pending chunks { Ref<ProjectStore::Project> Project1 = ProjectStore.OpenProject("proj1"sv); - ProjectStore::Oplog* Oplog = Project1->NewOplog("oplog1"sv, Project1OplogPath); + Ref<ProjectStore::Oplog> Oplog = Project1->NewOplog("oplog1"sv, Project1OplogPath); CHECK(Oplog->CheckPendingChunkReferences(OpChunkHashes, std::chrono::milliseconds(100)).empty()); } @@ -7590,8 +7761,8 @@ TEST_CASE("project.store.rpc.getchunks") EngineRootDir.string(), Project1RootDir.string(), Project1FilePath.string())); - ProjectStore::Oplog* Oplog = Project1->NewOplog("oplog1"sv, {}); - CHECK(Oplog != nullptr); + Ref<ProjectStore::Oplog> Oplog = Project1->NewOplog("oplog1"sv, {}); + CHECK(Oplog); Attachments[OpIds[0]] = {}; Attachments[OpIds[1]] = CreateAttachments(std::initializer_list<size_t>{77}); Attachments[OpIds[2]] = @@ -8510,8 +8681,8 @@ TEST_CASE("project.store.partial.read") EngineRootDir.string(), Project1RootDir.string(), Project1FilePath.string())); - ProjectStore::Oplog* Oplog = Project1->NewOplog("oplog1"sv, {}); - CHECK(Oplog != nullptr); + Ref<ProjectStore::Oplog> Oplog = Project1->NewOplog("oplog1"sv, {}); + CHECK(Oplog); Attachments[OpIds[0]] = {}; Attachments[OpIds[1]] = CreateAttachments(std::initializer_list<size_t>{77}); Attachments[OpIds[2]] = CreateAttachments(std::initializer_list<size_t>{7123, 9583, 690, 99}); @@ -8690,8 +8861,8 @@ TEST_CASE("project.store.iterateoplog") EngineRootDir.string(), ProjectRootDir.string(), ProjectFilePath.string())); - ProjectStore::Oplog* Oplog = TestProject->NewOplog("oplog"sv, ProjectOplogPath); - CHECK(Oplog != nullptr); + Ref<ProjectStore::Oplog> Oplog = TestProject->NewOplog("oplog"sv, ProjectOplogPath); + CHECK(Oplog); struct TestOidData { |