diff options
| author | Dan Engelbrecht <[email protected]> | 2025-04-03 14:28:51 +0200 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2025-04-03 14:28:51 +0200 |
| commit | 64cd6e328bee3a94bc0bf10441b4057f7be34d1c (patch) | |
| tree | c1656ae98110ed24b88ac82e32ad3d849bea3ae9 /src | |
| parent | `zen oplog-export`, `zen oplog-import` for `--url` (cloud) and `--builds` (bu... (diff) | |
| download | zen-64cd6e328bee3a94bc0bf10441b4057f7be34d1c.tar.xz zen-64cd6e328bee3a94bc0bf10441b4057f7be34d1c.zip | |
build store save access times (#341)v5.6.3-pre0
* save payload size in log for buildstore
* read/write access times and manifest for buldstore
* use retry when removing temporary files
Diffstat (limited to 'src')
| -rw-r--r-- | src/zen/cmds/builds_cmd.cpp | 10 | ||||
| -rw-r--r-- | src/zenstore/buildstore/buildstore.cpp | 283 | ||||
| -rw-r--r-- | src/zenstore/include/zenstore/accesstime.h | 12 | ||||
| -rw-r--r-- | src/zenstore/include/zenstore/buildstore/buildstore.h | 39 |
4 files changed, 309 insertions, 35 deletions
diff --git a/src/zen/cmds/builds_cmd.cpp b/src/zen/cmds/builds_cmd.cpp index f6b7299cb..d503ae45e 100644 --- a/src/zen/cmds/builds_cmd.cpp +++ b/src/zen/cmds/builds_cmd.cpp @@ -4932,7 +4932,7 @@ namespace { FilteredWrittenBytesPerSecond.Stop(); } - RemoveFile(CompressedChunkPath); + RemoveFileWithRetry(CompressedChunkPath); std::vector<uint32_t> CompletedSequences = CompleteChunkTargets(ChunkTargetPtrs, SequenceIndexChunksLeftToWriteCounters); @@ -5761,7 +5761,7 @@ namespace { FilteredWrittenBytesPerSecond.Stop(); } - RemoveFile(CompressedChunkPath); + RemoveFileWithRetry(CompressedChunkPath); std::vector<uint32_t> CompletedSequences = CompleteChunkTargets(ChunkTargetPtrs, SequenceIndexChunksLeftToWriteCounters); @@ -6169,7 +6169,7 @@ namespace { throw std::runtime_error(fmt::format("Block {} is malformed", BlockDescription.BlockHash)); } WritePartsComplete++; - RemoveFile(BlockChunkPath); + RemoveFileWithRetry(BlockChunkPath); if (WritePartsComplete == TotalPartWriteCount) { FilteredWrittenBytesPerSecond.Stop(); @@ -6328,7 +6328,7 @@ namespace { if (!BlockChunkPath.empty()) { - RemoveFile(BlockChunkPath); + RemoveFileWithRetry(BlockChunkPath); } WritePartsComplete++; @@ -6508,7 +6508,7 @@ namespace { if (!BlockChunkPath.empty()) { - RemoveFile(BlockChunkPath); + RemoveFileWithRetry(BlockChunkPath); } WritePartsComplete++; diff --git a/src/zenstore/buildstore/buildstore.cpp b/src/zenstore/buildstore/buildstore.cpp index f26901458..d6d727aa9 100644 --- a/src/zenstore/buildstore/buildstore.cpp +++ b/src/zenstore/buildstore/buildstore.cpp @@ -2,6 +2,7 @@ #include <zenstore/buildstore/buildstore.h> +#include <zencore/compactbinarybuilder.h> #include <zencore/fmtutils.h> #include <zencore/logging.h> #include <zencore/memory/llm.h> @@ -36,9 +37,18 @@ using namespace std::literals; namespace blobstore::impl { - const std::string BaseName = "builds"; - const char* IndexExtension = ".uidx"; - const char* LogExtension = ".slog"; + const std::string BaseName = "builds"; + const std::string ManifestExtension = ".cbo"; + const char* IndexExtension = ".uidx"; + const char* LogExtension = ".slog"; + const char* AccessTimeExtension = ".zacs"; + + const uint32_t ManifestVersion = (1 << 16) | (0 << 8) | (0); + + std::filesystem::path GetManifestPath(const std::filesystem::path& RootDirectory) + { + return RootDirectory / (BaseName + ManifestExtension); + } std::filesystem::path GetBlobIndexPath(const std::filesystem::path& RootDirectory) { @@ -56,10 +66,47 @@ namespace blobstore::impl { { return RootDirectory / (BaseName + "_meta" + LogExtension); } + + std::filesystem::path GetAccessTimesPath(const std::filesystem::path& RootDirectory) + { + return RootDirectory / (BaseName + AccessTimeExtension); + } + + struct AccessTimeRecord + { + IoHash Key; + std::uint32_t SecondsSinceEpoch = 0; + }; + + static_assert(sizeof(AccessTimeRecord) == 24); + +#pragma pack(push) +#pragma pack(1) + struct AccessTimesHeader + { + static constexpr uint32_t ExpectedMagic = 0x7363617a; // 'zacs'; + static constexpr uint32_t CurrentVersion = 1; + static constexpr uint64_t DataAlignment = 8; + + uint32_t Magic = ExpectedMagic; + uint32_t Version = CurrentVersion; + uint32_t AccessTimeCount = 0; + uint32_t Checksum = 0; + + static uint32_t ComputeChecksum(const AccessTimesHeader& Header) + { + return XXH32(&Header.Magic, sizeof(AccessTimesHeader) - sizeof(uint32_t), 0xC0C0'BABA); + } + }; +#pragma pack(pop) + + static_assert(sizeof(AccessTimesHeader) == 16); + } // namespace blobstore::impl BuildStore::BuildStore(const BuildStoreConfig& Config, GcManager& Gc) -: m_Config(Config) +: m_Log(logging::Get("builds")) +, m_Config(Config) , m_Gc(Gc) , m_LargeBlobStore(m_Gc) , m_SmallBlobStore(Gc) @@ -69,14 +116,57 @@ BuildStore::BuildStore(const BuildStoreConfig& Config, GcManager& Gc) ZEN_MEMSCOPE(GetBuildstoreTag()); try { - std::filesystem::path BlobLogPath = blobstore::impl::GetBlobLogPath(Config.RootDirectory); - std::filesystem::path MetaLogPath = blobstore::impl::GetMetaLogPath(Config.RootDirectory); - bool IsNew = !(IsFile(BlobLogPath) && IsFile(MetaLogPath)); + bool IsNew = true; + Stopwatch Timer; + const auto _ = MakeGuard([&] { + ZEN_INFO("{} build store at {} in {}", + IsNew ? "Initialized" : "Read", + m_Config.RootDirectory, + NiceTimeSpanMs(Timer.GetElapsedTimeMs())); + }); + + std::filesystem::path BlobLogPath = blobstore::impl::GetBlobLogPath(Config.RootDirectory); + std::filesystem::path MetaLogPath = blobstore::impl::GetMetaLogPath(Config.RootDirectory); + std::filesystem::path ManifestPath = blobstore::impl::GetManifestPath(Config.RootDirectory); + std::filesystem::path AccessTimesPath = blobstore::impl::GetAccessTimesPath(Config.RootDirectory); + if (IsFile(ManifestPath) && IsFile(BlobLogPath) && IsFile(MetaLogPath)) + { + IsNew = false; + } if (!IsNew) { - m_BlobLogFlushPosition = ReadPayloadLog(RwLock::ExclusiveLockScope(m_Lock), BlobLogPath, 0); - m_MetaLogFlushPosition = ReadMetadataLog(RwLock::ExclusiveLockScope(m_Lock), MetaLogPath, 0); + RwLock::ExclusiveLockScope Lock(m_Lock); + + CbObject ManifestReader = LoadCompactBinaryObject(ReadFile(ManifestPath).Flatten()); + Oid ManifestId = ManifestReader["id"].AsObjectId(); + uint32_t Version = ManifestReader["version"].AsUInt32(); + DateTime CreationDate = ManifestReader["createdAt"].AsDateTime(); + ZEN_UNUSED(CreationDate); + if (ManifestId == Oid::Zero || Version != blobstore::impl::ManifestVersion) + { + ZEN_WARN("Invalid manifest at {}, wiping state", ManifestPath); + IsNew = true; + } + else + { + m_BlobLogFlushPosition = ReadPayloadLog(Lock, BlobLogPath, 0); + m_MetaLogFlushPosition = ReadMetadataLog(Lock, MetaLogPath, 0); + if (IsFile(AccessTimesPath)) + { + ReadAccessTimes(Lock, AccessTimesPath); + } + } + } + + if (IsNew) + { + CleanDirectory(Config.RootDirectory, false); + CbObjectWriter ManifestWriter; + ManifestWriter.AddObjectId("id", Oid::NewOid()); + ManifestWriter.AddInteger("version", blobstore::impl::ManifestVersion); + ManifestWriter.AddDateTime("createdAt", DateTime::Now()); + TemporaryFile::SafeWriteFile(ManifestPath, ManifestWriter.Save().GetBuffer().AsIoBuffer()); } m_LargeBlobStore.Initialize(Config.RootDirectory / "file_cas", IsNew); m_SmallBlobStore.Initialize(Config.RootDirectory, @@ -147,18 +237,19 @@ BuildStore::PutBlob(const IoHash& BlobHash, const IoBuffer& Payload) } } + uint64_t PayloadSize = Payload.GetSize(); PayloadEntry Entry; if (Payload.GetSize() > m_Config.SmallBlobBlockStoreMaxBlockEmbedSize) { CasStore::InsertResult Result = m_LargeBlobStore.InsertChunk(Payload, BlobHash); ZEN_UNUSED(Result); - Entry = {.Flags = PayloadEntry::kStandalone}; + Entry = PayloadEntry(PayloadEntry::kStandalone, PayloadSize); } else { CasStore::InsertResult Result = m_SmallBlobStore.InsertChunk(Payload, BlobHash); ZEN_UNUSED(Result); - Entry = {.Flags = 0}; + Entry = PayloadEntry(0, PayloadSize); } m_PayloadlogFile.Append(PayloadDiskEntry{.Entry = Entry, .BlobHash = BlobHash}); @@ -188,6 +279,7 @@ BuildStore::PutBlob(const IoHash& BlobHash, const IoBuffer& Payload) m_BlobEntries.push_back(BlobEntry{.Payload = NewPayloadIndex, .LastAccessTime = AccessTime(GcClock::TickCount())}); m_BlobLookup.insert({BlobHash, NewBlobIndex}); } + m_LastAccessTimeUpdateCount++; } IoBuffer @@ -204,7 +296,7 @@ BuildStore::GetBlob(const IoHash& BlobHash) if (Blob.Payload) { const PayloadEntry& Entry = m_PayloadEntries[Blob.Payload]; - const bool IsStandalone = (Entry.Flags & PayloadEntry::kStandalone) != 0; + const bool IsStandalone = (Entry.GetFlags() & PayloadEntry::kStandalone) != 0; Lock.ReleaseNow(); IoBuffer Chunk; @@ -299,6 +391,7 @@ BuildStore::PutMetadatas(std::span<const IoHash> BlobHashes, std::span<const IoB m_BlobEntries.push_back(BlobEntry{.Metadata = NewMetadataIndex, .LastAccessTime = AccessTime(GcClock::TickCount())}); m_BlobLookup.insert({BlobHash, NewBlobIndex}); } + m_LastAccessTimeUpdateCount++; WriteBlobIndex++; if (m_TrackedCacheKeys) { @@ -341,6 +434,7 @@ BuildStore::GetMetadatas(std::span<const IoHash> BlobHashes, WorkerThreadPool* O ResultContentTypes[Index] = ExistingMetadataEntry.ContentType; } ExistingBlobEntry.LastAccessTime = AccessTime(GcClock::TickCount()); + m_LastAccessTimeUpdateCount++; } } } @@ -434,12 +528,23 @@ BuildStore::Flush() ZEN_TRACE_CPU("BuildStore::Flush"); try { + Stopwatch Timer; + const auto _ = MakeGuard( + [&] { ZEN_INFO("Flushed build store at {} in {}", m_Config.RootDirectory, NiceTimeSpanMs(Timer.GetElapsedTimeMs())); }); + m_LargeBlobStore.Flush(); m_SmallBlobStore.Flush(); m_MetadataBlockStore.Flush(false); m_PayloadlogFile.Flush(); m_MetadatalogFile.Flush(); + + if (uint64_t LastAccessTimeUpdateCount = m_LastAccessTimeUpdateCount.load(); LastAccessTimeUpdateCount > 0) + { + m_LastAccessTimeUpdateCount -= LastAccessTimeUpdateCount; + RwLock::ExclusiveLockScope UpdateLock(m_Lock); + WriteAccessTimes(UpdateLock, blobstore::impl::GetAccessTimesPath(m_Config.RootDirectory)); + } } catch (const std::exception& Ex) { @@ -447,6 +552,35 @@ BuildStore::Flush() } } +#if ZEN_WITH_TESTS +std::optional<AccessTime> +BuildStore::GetLastAccessTime(const IoHash& Key) const +{ + RwLock::SharedLockScope _(m_Lock); + if (auto It = m_BlobLookup.find(Key); It != m_BlobLookup.end()) + { + const BlobIndex Index = It->second; + const BlobEntry& Entry = m_BlobEntries[Index]; + return Entry.LastAccessTime; + } + return {}; +} + +bool +BuildStore::SetLastAccessTime(const IoHash& Key, const AccessTime& Time) +{ + RwLock::SharedLockScope _(m_Lock); + if (auto It = m_BlobLookup.find(Key); It != m_BlobLookup.end()) + { + const BlobIndex Index = It->second; + BlobEntry& Entry = m_BlobEntries[Index]; + Entry.LastAccessTime = Time; + return true; + } + return false; +} +#endif // ZEN_WITH_TESTS + void BuildStore::CompactState() { @@ -540,7 +674,7 @@ BuildStore::ReadPayloadLog(const RwLock::ExclusiveLockScope&, const std::filesys CasLog.Replay( [&](const PayloadDiskEntry& Record) { std::string InvalidEntryReason; - if (Record.Entry.Flags & PayloadEntry::kTombStone) + if (Record.Entry.GetFlags() & PayloadEntry::kTombStone) { // Note: this leaves m_BlobLookup and other arrays with 'holes' in them, this will get clean up in compact gc operation if (auto ExistingIt = m_BlobLookup.find(Record.BlobHash); ExistingIt != m_BlobLookup.end()) @@ -702,6 +836,114 @@ BuildStore::ReadMetadataLog(const RwLock::ExclusiveLockScope&, const std::filesy return LogEntryCount; } +void +BuildStore::ReadAccessTimes(const RwLock::ExclusiveLockScope&, const std::filesystem::path& AccessTimesPath) +{ + ZEN_TRACE_CPU("BuildStore::ReadAccessTimes"); + + using namespace blobstore::impl; + + BasicFile AccessTimesFile; + AccessTimesFile.Open(AccessTimesPath, BasicFile::Mode::kRead); + uint64_t Size = AccessTimesFile.FileSize(); + if (Size >= sizeof(AccessTimesHeader)) + { + AccessTimesHeader Header; + uint64_t Offset = 0; + AccessTimesFile.Read(&Header, sizeof(Header), 0); + Offset += sizeof(AccessTimesHeader); + Offset = RoundUp(Offset, AccessTimesHeader::DataAlignment); + if ((Header.Magic == AccessTimesHeader::ExpectedMagic) && (Header.Version == AccessTimesHeader::CurrentVersion) && + (Header.Checksum == AccessTimesHeader::ComputeChecksum(Header))) + { + uint64_t RecordsSize = sizeof(AccessTimeRecord) * Header.AccessTimeCount; + if (AccessTimesFile.FileSize() >= Offset + RecordsSize) + { + std::vector<AccessTimeRecord> AccessRecords(Header.AccessTimeCount); + AccessTimesFile.Read(AccessRecords.data(), RecordsSize, Offset); + for (const AccessTimeRecord& Record : AccessRecords) + { + const IoHash& Key = Record.Key; + const uint32_t SecondsSinceEpoch = Record.SecondsSinceEpoch; + if (auto It = m_BlobLookup.find(Key); It != m_BlobLookup.end()) + { + const BlobIndex Index = It->second; + BlobEntry& Entry = m_BlobEntries[Index]; + Entry.LastAccessTime.SetSecondsSinceEpoch(SecondsSinceEpoch); + } + else + { + m_LastAccessTimeUpdateCount++; + } + } + } + else + { + m_LastAccessTimeUpdateCount++; + } + } + else + { + m_LastAccessTimeUpdateCount++; + } + } + else + { + m_LastAccessTimeUpdateCount++; + } +} + +void +BuildStore::WriteAccessTimes(const RwLock::ExclusiveLockScope&, const std::filesystem::path& AccessTimesPath) +{ + ZEN_TRACE_CPU("BuildStore::WriteAccessTimes"); + + using namespace blobstore::impl; + + uint32_t Count = gsl::narrow<uint32_t>(m_BlobLookup.size()); + AccessTimesHeader Header = {.AccessTimeCount = Count}; + Header.Checksum = AccessTimesHeader::ComputeChecksum(Header); + + TemporaryFile TempFile; + std::error_code Ec; + if (TempFile.CreateTemporary(AccessTimesPath.parent_path(), Ec); Ec) + { + throw std::runtime_error(fmt::format("Failed to create temporary file {} to write access times. Reason ({}) {}", + TempFile.GetPath(), + Ec.value(), + Ec.message())); + } + { + uint64_t Offset = 0; + TempFile.Write(&Header, sizeof(AccessTimesHeader), Offset); + Offset += sizeof(AccessTimesHeader); + Offset = RoundUp(Offset, AccessTimesHeader::DataAlignment); + + std::vector<AccessTimeRecord> AccessRecords; + AccessRecords.reserve(Header.AccessTimeCount); + + for (auto It : m_BlobLookup) + { + const IoHash& Key = It.first; + const BlobIndex Index = It.second; + const BlobEntry& Entry = m_BlobEntries[Index]; + const uint32_t SecondsSinceEpoch = Entry.LastAccessTime.GetSecondsSinceEpoch(); + AccessRecords.emplace_back(AccessTimeRecord{.Key = Key, .SecondsSinceEpoch = SecondsSinceEpoch}); + } + uint64_t RecordsSize = sizeof(AccessTimeRecord) * Header.AccessTimeCount; + TempFile.Write(AccessRecords.data(), RecordsSize, Offset); + Offset += sizeof(AccessTimesHeader) * Header.AccessTimeCount; + } + if (TempFile.MoveTemporaryIntoPlace(AccessTimesPath, Ec); Ec) + { + throw std::runtime_error(fmt::format("Failed to move temporary file {} to {} when write access times. Reason ({}) {}", + TempFile.GetPath(), + AccessTimesPath, + Ec.value(), + Ec.message())); + } +} + bool BuildStore::ValidatePayloadDiskEntry(const PayloadDiskEntry& Entry, std::string& OutReason) { @@ -710,18 +952,18 @@ BuildStore::ValidatePayloadDiskEntry(const PayloadDiskEntry& Entry, std::string& OutReason = fmt::format("Invalid blob hash {}", Entry.BlobHash.ToHexString()); return false; } - if (Entry.Entry.Flags & ~(PayloadEntry::kTombStone | PayloadEntry::kStandalone)) + if (Entry.Entry.GetFlags() & ~(PayloadEntry::kTombStone | PayloadEntry::kStandalone)) { - OutReason = fmt::format("Invalid flags {} for entry {}", Entry.Entry.Flags, Entry.BlobHash.ToHexString()); + OutReason = fmt::format("Invalid flags {} for entry {}", Entry.Entry.GetFlags(), Entry.BlobHash.ToHexString()); return false; } - if (Entry.Entry.Flags & PayloadEntry::kTombStone) + if (Entry.Entry.GetFlags() & PayloadEntry::kTombStone) { return true; } - if (Entry.Entry.Reserved1 != 0 || Entry.Entry.Reserved2 != 0 || Entry.Entry.Reserved3 != 0) + if (Entry.Entry.GetSize() == 0 || Entry.Entry.GetSize() == 0x00ffffffffffffffu) { - OutReason = fmt::format("Invalid reserved fields for meta entry {}", Entry.BlobHash.ToHexString()); + OutReason = fmt::format("Invalid size field {} for meta entry {}", Entry.Entry.GetSize(), Entry.BlobHash.ToHexString()); return false; } return true; @@ -869,6 +1111,8 @@ public: NiceTimeSpanMs(Timer.GetElapsedTimeMs())); }); + const auto __ = MakeGuard([&] { m_Store.Flush(); }); + if (!m_RemovedBlobs.empty()) { if (Ctx.Settings.CollectSmallObjects) @@ -1080,7 +1324,7 @@ BuildStore::RemoveExpiredData(GcCtx& Ctx, GcStats& Stats) { RemovedPayloads.push_back( PayloadDiskEntry{.Entry = m_PayloadEntries[ReadBlobEntry.Payload], .BlobHash = ExpiredBlob}); - RemovedPayloads.back().Entry.Flags |= PayloadEntry::kTombStone; + RemovedPayloads.back().Entry.AddFlag(PayloadEntry::kTombStone); m_PayloadEntries[ReadBlobEntry.Payload] = {}; m_BlobEntries[ReadBlobIndex].Payload = {}; } @@ -1094,6 +1338,7 @@ BuildStore::RemoveExpiredData(GcCtx& Ctx, GcStats& Stats) } m_BlobLookup.erase(It); + m_LastAccessTimeUpdateCount++; RemovedBlobs.push_back(ExpiredBlob); Stats.DeletedCount++; diff --git a/src/zenstore/include/zenstore/accesstime.h b/src/zenstore/include/zenstore/accesstime.h index a28dc908b..e53937b52 100644 --- a/src/zenstore/include/zenstore/accesstime.h +++ b/src/zenstore/include/zenstore/accesstime.h @@ -11,10 +11,10 @@ namespace zen { // This store the access time as seconds since epoch internally in a 32-bit value giving is a range of 136 years since epoch struct AccessTime { - explicit AccessTime(GcClock::Tick Tick) noexcept : SecondsSinceEpoch(ToSeconds(Tick)) {} + explicit AccessTime(GcClock::Tick Tick) noexcept : SecondsSinceEpoch(ToSecondsSinceEpoch(Tick)) {} AccessTime& operator=(GcClock::Tick Tick) noexcept { - SecondsSinceEpoch.store(ToSeconds(Tick), std::memory_order_relaxed); + SecondsSinceEpoch.store(ToSecondsSinceEpoch(Tick), std::memory_order_relaxed); return *this; } operator GcClock::Tick() const noexcept @@ -36,8 +36,14 @@ struct AccessTime return *this; } + void SetSecondsSinceEpoch(uint32_t InSecondsSinceEpoch) { SecondsSinceEpoch.store(InSecondsSinceEpoch); } + + uint32_t GetSecondsSinceEpoch() const { return SecondsSinceEpoch.load(); } + private: - static uint32_t ToSeconds(GcClock::Tick Tick) + AccessTime(uint32_t InSecondsSinceEpoch) noexcept : SecondsSinceEpoch(InSecondsSinceEpoch) {} + + static uint32_t ToSecondsSinceEpoch(GcClock::Tick Tick) { return gsl::narrow<uint32_t>(std::chrono::duration_cast<std::chrono::seconds>(GcClock::Duration(Tick)).count()); } diff --git a/src/zenstore/include/zenstore/buildstore/buildstore.h b/src/zenstore/include/zenstore/buildstore/buildstore.h index 302af5f9c..d88e682de 100644 --- a/src/zenstore/include/zenstore/buildstore/buildstore.h +++ b/src/zenstore/include/zenstore/buildstore/buildstore.h @@ -48,11 +48,19 @@ public: void Flush(); +#if ZEN_WITH_TESTS + std::optional<AccessTime> GetLastAccessTime(const IoHash& Key) const; + bool SetLastAccessTime(const IoHash& Key, const AccessTime& Time); +#endif // ZEN_WITH_TESTS private: + LoggerRef Log() { return m_Log; } + void CompactState(); uint64_t ReadPayloadLog(const RwLock::ExclusiveLockScope&, const std::filesystem::path& LogPath, uint64_t SkipEntryCount); uint64_t ReadMetadataLog(const RwLock::ExclusiveLockScope&, const std::filesystem::path& LogPath, uint64_t SkipEntryCount); + void WriteAccessTimes(const RwLock::ExclusiveLockScope&, const std::filesystem::path& AccessTimesPath); + void ReadAccessTimes(const RwLock::ExclusiveLockScope&, const std::filesystem::path& AccessTimesPath); //////// GcReferencer virtual std::string GetGcName(GcCtx& Ctx) override; @@ -67,22 +75,35 @@ private: #pragma pack(1) struct PayloadEntry { + PayloadEntry() {} + PayloadEntry(uint64_t Flags, uint64_t Size) + { + ZEN_ASSERT((Size & 0x00ffffffffffffffu) == Size); + ZEN_ASSERT((Flags & (kTombStone | kStandalone)) == Flags); + FlagsAndSize = (Size << 8) | Flags; + } static const uint8_t kTombStone = 0x10u; // Represents a deleted key/value static const uint8_t kStandalone = 0x20u; // This payload is stored as a standalone value - uint8_t Flags = 0; - uint8_t Reserved1 = 0; - uint8_t Reserved2 = 0; - uint8_t Reserved3 = 0; + uint64_t FlagsAndSize = 0; + uint64_t GetSize() const { return FlagsAndSize >> 8; } + uint8_t GetFlags() const { return uint8_t(FlagsAndSize & 0xff); } + void AddFlag(uint8_t Flag) { FlagsAndSize |= Flag; } + void SetSize(uint64_t Size) + { + ZEN_ASSERT((Size & 0x00ffffffffffffffu) == Size); + FlagsAndSize = (Size << 8) | (FlagsAndSize & 0xff); + } + void SetFlags(uint8_t Flags) { FlagsAndSize = (FlagsAndSize & 0xffffffffffffff00u) | Flags; } }; - static_assert(sizeof(PayloadEntry) == 4); + static_assert(sizeof(PayloadEntry) == 8); struct PayloadDiskEntry { - PayloadEntry Entry; // 4 bytes + PayloadEntry Entry; // 8 bytes IoHash BlobHash; // 20 bytes }; - static_assert(sizeof(PayloadDiskEntry) == 24); + static_assert(sizeof(PayloadDiskEntry) == 28); struct MetadataEntry { @@ -154,10 +175,11 @@ private: }; static_assert(sizeof(BlobEntry) == 12); + LoggerRef m_Log; const BuildStoreConfig m_Config; GcManager& m_Gc; - RwLock m_Lock; + mutable RwLock m_Lock; std::vector<PayloadEntry> m_PayloadEntries; std::vector<MetadataEntry> m_MetadataEntries; @@ -175,6 +197,7 @@ private: uint64_t m_MetaLogFlushPosition = 0; std::unique_ptr<HashSet> m_TrackedCacheKeys; + std::atomic<uint64_t> m_LastAccessTimeUpdateCount; friend class BuildStoreGcReferenceChecker; friend class BuildStoreGcReferencePruner; |