diff options
| author | Dan Engelbrecht <[email protected]> | 2022-05-09 13:51:52 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2022-05-09 13:51:52 +0200 |
| commit | 8265ceec30d7a8cc862928507c6eed12191e5ef5 (patch) | |
| tree | de40771ed96d013992eb0c502317ea76940827fe | |
| parent | Initialize upstream apply in background thread (#88) (diff) | |
| parent | Make sure blockstore owner and block store state does not get out of sync whe... (diff) | |
| download | zen-1.0.1.3.tar.xz zen-1.0.1.3.zip | |
Merge pull request #91 from EpicGames/de/block-store-gc-bugv1.0.1.3
Make sure block store owner and block store state does not get out of sync when fetching a chunk
| -rw-r--r-- | zencore/include/zencore/iobuffer.h | 3 | ||||
| -rw-r--r-- | zencore/iobuffer.cpp | 30 | ||||
| -rw-r--r-- | zenserver/cache/structuredcachestore.cpp | 48 | ||||
| -rw-r--r-- | zenstore/basicfile.cpp | 25 | ||||
| -rw-r--r-- | zenstore/blockstore.cpp | 75 | ||||
| -rw-r--r-- | zenstore/compactcas.cpp | 14 | ||||
| -rw-r--r-- | zenstore/include/zenstore/basicfile.h | 12 | ||||
| -rw-r--r-- | zenstore/include/zenstore/blockstore.h | 6 |
8 files changed, 93 insertions, 120 deletions
diff --git a/zencore/include/zencore/iobuffer.h b/zencore/include/zencore/iobuffer.h index bc8cfdc0f..5d9daa1c7 100644 --- a/zencore/include/zencore/iobuffer.h +++ b/zencore/include/zencore/iobuffer.h @@ -276,12 +276,14 @@ struct IoBufferExtendedCore : public IoBufferCore void Materialize() const; bool GetFileReference(IoBufferFileReference& OutRef) const; + void MarkAsDeleteOnClose(); private: void* m_FileHandle = nullptr; uint64_t m_FileOffset = 0; mutable void* m_MmapHandle = nullptr; mutable void* m_MappedPointer = nullptr; + bool m_DeleteOnClose = false; }; inline IoBufferExtendedCore* @@ -377,6 +379,7 @@ public: inline void SetContentType(ZenContentType ContentType) { m_Core->SetContentType(ContentType); } [[nodiscard]] inline ZenContentType GetContentType() const { return m_Core->GetContentType(); } [[nodiscard]] ZENCORE_API bool GetFileReference(IoBufferFileReference& OutRef) const; + void MarkAsDeleteOnClose(); inline MemoryView GetView() const { return MemoryView(m_Core->DataPointer(), m_Core->DataBytes()); } inline MutableMemoryView GetMutableView() { return MutableMemoryView(m_Core->MutableDataPointer(), m_Core->DataBytes()); } diff --git a/zencore/iobuffer.cpp b/zencore/iobuffer.cpp index 46b9ab336..c4b7f7bdf 100644 --- a/zencore/iobuffer.cpp +++ b/zencore/iobuffer.cpp @@ -211,6 +211,18 @@ IoBufferExtendedCore::~IoBufferExtendedCore() if (LocalFlags & kOwnsFile) { + if (m_DeleteOnClose) + { +#if ZEN_PLATFORM_WINDOWS + // Mark file for deletion when final handle is closed + FILE_DISPOSITION_INFO Fdi{.DeleteFile = TRUE}; + + SetFileInformationByHandle(m_FileHandle, FileDispositionInfo, &Fdi, sizeof Fdi); +#else + std::filesystem::path FilePath = zen::PathFromHandle(m_FileHandle); + unlink(FilePath.c_str()); +#endif + } #if ZEN_PLATFORM_WINDOWS BOOL Success = CloseHandle(m_FileHandle); #else @@ -298,6 +310,9 @@ IoBufferExtendedCore::Materialize() const if (MappedBase == nullptr) { +#if ZEN_PLATFORM_WINDOWS + CloseHandle(NewMmapHandle); +#endif // ZEN_PLATFORM_WINDOWS throw std::system_error(std::error_code(zen::GetLastError(), std::system_category()), fmt::format("MapViewOfFile failed (offset {:#x}, size {:#x}) file: '{}'", MapOffset, @@ -327,6 +342,12 @@ IoBufferExtendedCore::GetFileReference(IoBufferFileReference& OutRef) const return true; } +void +IoBufferExtendedCore::MarkAsDeleteOnClose() +{ + m_DeleteOnClose = true; +} + ////////////////////////////////////////////////////////////////////////// IoBuffer::IoBuffer(size_t InSize) : m_Core(new IoBufferCore(InSize)) @@ -389,6 +410,15 @@ IoBuffer::GetFileReference(IoBufferFileReference& OutRef) const return false; } +void +IoBuffer::MarkAsDeleteOnClose() +{ + if (IoBufferExtendedCore* ExtCore = m_Core->ExtendedCore()) + { + ExtCore->MarkAsDeleteOnClose(); + } +} + ////////////////////////////////////////////////////////////////////////// IoBuffer diff --git a/zenserver/cache/structuredcachestore.cpp b/zenserver/cache/structuredcachestore.cpp index 2869191fd..a929284b9 100644 --- a/zenserver/cache/structuredcachestore.cpp +++ b/zenserver/cache/structuredcachestore.cpp @@ -1126,13 +1126,11 @@ ZenCacheDiskLayer::CacheBucket::GetInlineCacheValue(const DiskLocation& Loc, Zen { BlockStoreLocation Location = Loc.GetBlockLocation(m_PayloadAlignment); - Ref<BlockStoreFile> ChunkBlock = m_BlockStore.GetChunkBlock(Location); - if (!ChunkBlock) + OutValue.Value = m_BlockStore.TryGetChunk(Location); + if (!OutValue.Value) { return false; } - - OutValue.Value = ChunkBlock->GetChunk(Location.Offset, Location.Size); OutValue.Value.SetContentType(Loc.GetContentType()); return true; @@ -1166,22 +1164,21 @@ ZenCacheDiskLayer::CacheBucket::Get(const IoHash& HashKey, ZenCacheValue& OutVal } RwLock::SharedLockScope _(m_IndexLock); - - if (auto It = m_Index.find(HashKey); It != m_Index.end()) + auto It = m_Index.find(HashKey); + if (It == m_Index.end()) + { + return false; + } + IndexEntry& Entry = It.value(); + Entry.LastAccess.store(GcClock::TickCount(), std::memory_order_relaxed); + DiskLocation Location = Entry.Location; + if (Location.IsFlagSet(DiskLocation::kStandaloneFile)) { - IndexEntry& Entry = It.value(); - Entry.LastAccess.store(GcClock::TickCount(), std::memory_order_relaxed); - DiskLocation Location = Entry.Location; + // We don't need to hold the index lock when we read a standalone file _.ReleaseNow(); - - if (Location.IsFlagSet(DiskLocation::kStandaloneFile)) - { - return GetStandaloneCacheValue(Location, HashKey, OutValue); - } - return GetInlineCacheValue(Location, OutValue); + return GetStandaloneCacheValue(Location, HashKey, OutValue); } - - return false; + return GetInlineCacheValue(Location, OutValue); } void @@ -1470,14 +1467,13 @@ ZenCacheDiskLayer::CacheBucket::CollectGarbage(GcContext& GcCtx) WriteBlockTimeUs += ElapsedUs; WriteBlockLongestTimeUs = std::max(ElapsedUs, WriteBlockLongestTimeUs); }); + if (m_Index.empty()) { - if (m_Index.empty()) - { - ZEN_INFO("garbage collect SKIPPED, for '{}', container is empty", m_BucketDir / m_BucketName); - return; - } - BlockStoreState = m_BlockStore.GetReclaimSnapshotState(); + ZEN_INFO("garbage collect SKIPPED, for '{}', container is empty", m_BucketDir / m_BucketName); + return; } + BlockStoreState = m_BlockStore.GetReclaimSnapshotState(); + SaveManifest(); Index = m_Index; @@ -1832,12 +1828,11 @@ ZenCacheDiskLayer::CacheBucket::PutInlineCacheValue(const IoHash& HashKey, const EntryFlags |= DiskLocation::kCompressed; } - m_BlockStore.WriteChunk(Value.Value.Data(), Value.Value.Size(), m_PayloadAlignment, [&](BlockStoreLocation BlockStoreLocation) { + m_BlockStore.WriteChunk(Value.Value.Data(), Value.Value.Size(), m_PayloadAlignment, [&](const BlockStoreLocation& BlockStoreLocation) { DiskLocation Location(BlockStoreLocation, m_PayloadAlignment, EntryFlags); const DiskIndexEntry DiskIndexEntry{.Key = HashKey, .Location = Location}; m_SlogFile.Append(DiskIndexEntry); - m_TotalSize.fetch_add(BlockStoreLocation.Size, std::memory_order::relaxed); - RwLock::ExclusiveLockScope __(m_IndexLock); + RwLock::ExclusiveLockScope _(m_IndexLock); if (auto It = m_Index.find(HashKey); It != m_Index.end()) { // TODO: should check if write is idempotent and bail out if it is? @@ -1852,6 +1847,7 @@ ZenCacheDiskLayer::CacheBucket::PutInlineCacheValue(const IoHash& HashKey, const m_Index.insert({HashKey, {Location, GcClock::TickCount()}}); } }); + m_TotalSize.fetch_add(Value.Value.Size(), std::memory_order::relaxed); } ////////////////////////////////////////////////////////////////////////// diff --git a/zenstore/basicfile.cpp b/zenstore/basicfile.cpp index 8eb172a1c..e5a2adc41 100644 --- a/zenstore/basicfile.cpp +++ b/zenstore/basicfile.cpp @@ -373,31 +373,6 @@ BasicFile::SetFileSize(uint64_t FileSize) #endif } -void -BasicFile::MarkAsDeleteOnClose(std::error_code& Ec) -{ - Ec.clear(); -#if ZEN_PLATFORM_WINDOWS - FILE_DISPOSITION_INFO Fdi{}; - Fdi.DeleteFile = TRUE; - BOOL Success = SetFileInformationByHandle(m_FileHandle, FileDispositionInfo, &Fdi, sizeof Fdi); - if (!Success) - { - Ec = MakeErrorCodeFromLastError(); - } -#elif ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC - std::filesystem::path SourcePath = PathFromHandle(m_FileHandle); - if (unlink(SourcePath.c_str()) < 0) - { - int UnlinkError = zen::GetLastError(); - if (UnlinkError != ENOENT) - { - Ec = MakeErrorCode(UnlinkError); - } - } -#endif -} - void* BasicFile::Detach() { diff --git a/zenstore/blockstore.cpp b/zenstore/blockstore.cpp index 54a8eb9df..1946169c4 100644 --- a/zenstore/blockstore.cpp +++ b/zenstore/blockstore.cpp @@ -71,9 +71,9 @@ BlockStoreFile::FileSize() } void -BlockStoreFile::MarkAsDeleteOnClose(std::error_code& Ec) +BlockStoreFile::MarkAsDeleteOnClose() { - m_File.MarkAsDeleteOnClose(Ec); + m_IoBuffer.MarkAsDeleteOnClose(); } IoBuffer @@ -262,26 +262,28 @@ BlockStore::WriteChunk(const void* Data, uint64_t Size, uint64_t Alignment, Writ BlockStore::ReclaimSnapshotState BlockStore::GetReclaimSnapshotState() { - ReclaimSnapshotState State; - RwLock::ExclusiveLockScope _(m_InsertLock); + ReclaimSnapshotState State; + RwLock::SharedLockScope _(m_InsertLock); for (uint32_t BlockIndex : m_ActiveWriteBlocks) { State.m_ActiveWriteBlocks.insert(BlockIndex); } State.BlockCount = m_ChunkBlocks.size(); - _.ReleaseNow(); return State; } -Ref<BlockStoreFile> -BlockStore::GetChunkBlock(const BlockStoreLocation& Location) +IoBuffer +BlockStore::TryGetChunk(const BlockStoreLocation& Location) { RwLock::SharedLockScope InsertLock(m_InsertLock); if (auto BlockIt = m_ChunkBlocks.find(Location.BlockIndex); BlockIt != m_ChunkBlocks.end()) { - return BlockIt->second; + if (const Ref<BlockStoreFile>& Block = BlockIt->second; Block) + { + return Block->GetChunk(Location.Offset, Location.Size); + } } - return {}; + return IoBuffer(); } void @@ -465,13 +467,8 @@ BlockStore::ReclaimSpace(const ReclaimSnapshotState& Snapshot, ReadBlockLongestTimeUs = std::max(ElapsedUs, ReadBlockLongestTimeUs); }); m_ChunkBlocks[BlockIndex] = nullptr; - } - ZEN_DEBUG("marking cas block store file '{}' for delete, block #{}", OldBlockFile->GetPath(), BlockIndex); - std::error_code Ec; - OldBlockFile->MarkAsDeleteOnClose(Ec); - if (Ec) - { - ZEN_WARN("Failed to flag file '{}' for deletion: '{}'", OldBlockFile->GetPath(), Ec.message()); + ZEN_DEBUG("marking cas block store file '{}' for delete, block #{}", OldBlockFile->GetPath(), BlockIndex); + OldBlockFile->MarkAsDeleteOnClose(); } continue; } @@ -589,15 +586,9 @@ BlockStore::ReclaimSpace(const ReclaimSnapshotState& Snapshot, ReadBlockLongestTimeUs = std::max(ElapsedUs, ReadBlockLongestTimeUs); }); m_ChunkBlocks[BlockIndex] = nullptr; + ZEN_DEBUG("marking cas block store file '{}' for delete, block #{}", OldBlockFile->GetPath(), BlockIndex); + OldBlockFile->MarkAsDeleteOnClose(); } - ZEN_DEBUG("marking cas block store file '{}' for delete, block #{}", OldBlockFile->GetPath(), BlockIndex); - std::error_code Ec; - OldBlockFile->MarkAsDeleteOnClose(Ec); - if (Ec) - { - ZEN_WARN("Failed to flag file '{}' for deletion: '{}'", OldBlockFile->GetPath(), Ec.message()); - } - OldBlockFile = nullptr; } } catch (std::exception& ex) @@ -606,12 +597,7 @@ BlockStore::ReclaimSpace(const ReclaimSnapshotState& Snapshot, if (NewBlockFile) { ZEN_DEBUG("dropping incomplete cas block store file '{}'", NewBlockFile->GetPath()); - std::error_code Ec; - NewBlockFile->MarkAsDeleteOnClose(Ec); - if (Ec) - { - ZEN_WARN("Failed to flag file '{}' for deletion: '{}'", NewBlockFile->GetPath(), Ec.message()); - } + NewBlockFile->MarkAsDeleteOnClose(); } } } @@ -1032,9 +1018,7 @@ TEST_CASE("blockstore.blockfile") { BlockStoreFile File1(RootDirectory / "1"); File1.Open(); - std::error_code Ec; - File1.MarkAsDeleteOnClose(Ec); - CHECK(!Ec); + File1.MarkAsDeleteOnClose(); DataChunk = File1.GetChunk(0, 5); BoopChunk = File1.GetChunk(5, 5); } @@ -1058,12 +1042,7 @@ namespace { std::string ReadChunkAsString(BlockStore& Store, const BlockStoreLocation& Location) { - Ref<BlockStoreFile> ChunkBlock(Store.GetChunkBlock(Location)); - if (!ChunkBlock) - { - return ""; - } - IoBuffer ChunkData = ChunkBlock->GetChunk(Location.Offset, Location.Size); + IoBuffer ChunkData = Store.TryGetChunk(Location); if (!ChunkData) { return ""; @@ -1129,7 +1108,7 @@ TEST_CASE("blockstore.chunks") BlockStore Store; Store.Initialize(RootDirectory, 128, 1024, {}); - Ref<BlockStoreFile> BadChunk = Store.GetChunkBlock({.BlockIndex = 0, .Offset = 0, .Size = 512}); + IoBuffer BadChunk = Store.TryGetChunk({.BlockIndex = 0, .Offset = 0, .Size = 512}); CHECK(!BadChunk); std::string FirstChunkData = "This is the data of the first chunk that we will write"; @@ -1202,7 +1181,7 @@ TEST_CASE("blockstore.iterate.chunks") BlockStore Store; Store.Initialize(RootDirectory / "store", ScrubSmallChunkWindowSize * 2, 1024, {}); - Ref<BlockStoreFile> BadChunk = Store.GetChunkBlock({.BlockIndex = 0, .Offset = 0, .Size = 512}); + IoBuffer BadChunk = Store.TryGetChunk({.BlockIndex = 0, .Offset = 0, .Size = 512}); CHECK(!BadChunk); std::string FirstChunkData = "This is the data of the first chunk that we will write"; @@ -1264,6 +1243,7 @@ TEST_CASE("blockstore.reclaim.space") for (size_t ChunkIndex = 0; ChunkIndex < ChunkCount; ++ChunkIndex) { IoBuffer Chunk = CreateChunk(57 + ChunkIndex); + Store.WriteChunk(Chunk.Data(), Chunk.Size(), Alignment, [&](const BlockStoreLocation& L) { ChunkLocations.push_back(L); }); ChunkHashes.push_back(IoHash::HashBuffer(Chunk.Data(), Chunk.Size())); } @@ -1331,11 +1311,10 @@ TEST_CASE("blockstore.reclaim.space") for (size_t ChunkIndex = 0; ChunkIndex < ChunkCount; ++ChunkIndex) { - Ref<BlockStoreFile> ChunkBlock = Store.GetChunkBlock(NewChunkLocations[ChunkIndex]); + IoBuffer ChunkBlock = Store.TryGetChunk(NewChunkLocations[ChunkIndex]); if (ChunkIndex >= DeleteChunkCount) { - CHECK(ChunkBlock); - IoBuffer VerifyChunk = ChunkBlock->GetChunk(NewChunkLocations[ChunkIndex].Offset, NewChunkLocations[ChunkIndex].Size); + IoBuffer VerifyChunk = Store.TryGetChunk(NewChunkLocations[ChunkIndex]); CHECK(VerifyChunk); IoHash VerifyHash = IoHash::HashBuffer(VerifyChunk.Data(), VerifyChunk.Size()); CHECK(VerifyHash == ChunkHashes[ChunkIndex]); @@ -1405,9 +1384,7 @@ TEST_CASE("blockstore.thread.read.write") for (size_t ChunkIndex = 0; ChunkIndex < ChunkCount; ++ChunkIndex) { WorkerPool.ScheduleWork([&Store, ChunkIndex, &ChunkLocations, &ChunkHashes, &WorkCompleted]() { - Ref<BlockStoreFile> ChunkBlock = Store.GetChunkBlock(ChunkLocations[ChunkIndex]); - CHECK(ChunkBlock); - IoBuffer VerifyChunk = ChunkBlock->GetChunk(ChunkLocations[ChunkIndex].Offset, ChunkLocations[ChunkIndex].Size); + IoBuffer VerifyChunk = Store.TryGetChunk(ChunkLocations[ChunkIndex]); CHECK(VerifyChunk); IoHash VerifyHash = IoHash::HashBuffer(VerifyChunk.Data(), VerifyChunk.Size()); CHECK(VerifyHash == ChunkHashes[ChunkIndex]); @@ -1432,9 +1409,7 @@ TEST_CASE("blockstore.thread.read.write") WorkCompleted.fetch_add(1); }); WorkerPool.ScheduleWork([&Store, ChunkIndex, &ChunkLocations, &ChunkHashes, &WorkCompleted]() { - Ref<BlockStoreFile> ChunkBlock = Store.GetChunkBlock(ChunkLocations[ChunkIndex]); - CHECK(ChunkBlock); - IoBuffer VerifyChunk = ChunkBlock->GetChunk(ChunkLocations[ChunkIndex].Offset, ChunkLocations[ChunkIndex].Size); + IoBuffer VerifyChunk = Store.TryGetChunk(ChunkLocations[ChunkIndex]); CHECK(VerifyChunk); IoHash VerifyHash = IoHash::HashBuffer(VerifyChunk.Data(), VerifyChunk.Size()); CHECK(VerifyHash == ChunkHashes[ChunkIndex]); diff --git a/zenstore/compactcas.cpp b/zenstore/compactcas.cpp index cc0e2241c..2d48265f7 100644 --- a/zenstore/compactcas.cpp +++ b/zenstore/compactcas.cpp @@ -258,8 +258,8 @@ CasContainerStrategy::InsertChunk(const void* ChunkData, size_t ChunkSize, const RwLock::ExclusiveLockScope _(m_LocationMapLock); m_LocationMap.emplace(ChunkHash, DiskLocation); } - m_TotalSize.fetch_add(static_cast<uint64_t>(ChunkSize), std::memory_order::relaxed); }); + m_TotalSize.fetch_add(static_cast<uint64_t>(ChunkSize), std::memory_order::relaxed); return CasStore::InsertResult{.New = true}; } @@ -279,16 +279,10 @@ CasContainerStrategy::FindChunk(const IoHash& ChunkHash) { return IoBuffer(); } - BlockStoreLocation Location = KeyIt->second.Get(m_PayloadAlignment); - _.ReleaseNow(); - - Ref<BlockStoreFile> ChunkBlock = m_BlockStore.GetChunkBlock(Location); - if (!ChunkBlock) - { - return IoBuffer(); - } + const BlockStoreLocation& Location = KeyIt->second.Get(m_PayloadAlignment); - return ChunkBlock->GetChunk(Location.Offset, Location.Size); + IoBuffer Chunk = m_BlockStore.TryGetChunk(Location); + return Chunk; } bool diff --git a/zenstore/include/zenstore/basicfile.h b/zenstore/include/zenstore/basicfile.h index 5a500c65f..ce9988776 100644 --- a/zenstore/include/zenstore/basicfile.h +++ b/zenstore/include/zenstore/basicfile.h @@ -33,11 +33,12 @@ public: enum class Mode : uint32_t { - kRead = 0, // Opens a existing file for read only - kWrite = 1, // Opens (or creates) a file for read and write - kTruncate = 2, // Opens (or creates) a file for read and write and sets the size to zero - kDelete = 3, // Opens (or creates) a file for read and write enabling MarkAsDeleteOnClose() - kTruncateDelete = 4 // Opens (or creates) a file for read and write and sets the size to zero enabling MarkAsDeleteOnClose() + kRead = 0, // Opens a existing file for read only + kWrite = 1, // Opens (or creates) a file for read and write + kTruncate = 2, // Opens (or creates) a file for read and write and sets the size to zero + kDelete = 3, // Opens (or creates) a file for read and write allowing .DeleteFile file disposition to be set + kTruncateDelete = + 4 // Opens (or creates) a file for read and write and sets the size to zero allowing .DeleteFile file disposition to be set }; void Open(const std::filesystem::path& FileName, Mode Mode); @@ -55,7 +56,6 @@ public: void SetFileSize(uint64_t FileSize); IoBuffer ReadAll(); void WriteAll(IoBuffer Data, std::error_code& Ec); - void MarkAsDeleteOnClose(std::error_code& Ec); void* Detach(); inline void* Handle() { return m_FileHandle; } diff --git a/zenstore/include/zenstore/blockstore.h b/zenstore/include/zenstore/blockstore.h index 9edfc36e8..34c475fb6 100644 --- a/zenstore/include/zenstore/blockstore.h +++ b/zenstore/include/zenstore/blockstore.h @@ -89,7 +89,7 @@ struct BlockStoreFile : public RefCounted const std::filesystem::path& GetPath() const; void Open(); void Create(uint64_t InitialSize); - void MarkAsDeleteOnClose(std::error_code& Ec); + void MarkAsDeleteOnClose(); uint64_t FileSize(); IoBuffer GetChunk(uint64_t Offset, uint64_t Size); void Read(void* Data, uint64_t Size, uint64_t FileOffset); @@ -133,8 +133,8 @@ public: void WriteChunk(const void* Data, uint64_t Size, uint64_t Alignment, WriteChunkCallback Callback); - Ref<BlockStoreFile> GetChunkBlock(const BlockStoreLocation& Location); - void Flush(); + IoBuffer TryGetChunk(const BlockStoreLocation& Location); + void Flush(); ReclaimSnapshotState GetReclaimSnapshotState(); void ReclaimSpace( |