aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/zen/cmds/builds_cmd.cpp464
-rw-r--r--src/zenremotestore/builds/buildsavedstate.cpp443
-rw-r--r--src/zenremotestore/builds/buildstorageoperations.cpp216
-rw-r--r--src/zenremotestore/chunking/chunkedcontent.cpp44
-rw-r--r--src/zenremotestore/include/zenremotestore/builds/buildsavedstate.h60
-rw-r--r--src/zenremotestore/include/zenremotestore/chunking/chunkedcontent.h2
6 files changed, 604 insertions, 625 deletions
diff --git a/src/zen/cmds/builds_cmd.cpp b/src/zen/cmds/builds_cmd.cpp
index a911d1e44..76322013d 100644
--- a/src/zen/cmds/builds_cmd.cpp
+++ b/src/zen/cmds/builds_cmd.cpp
@@ -23,6 +23,7 @@
#include <zenhttp/httpclient.h>
#include <zenhttp/httpclientauth.h>
#include <zenhttp/httpcommon.h>
+#include <zenremotestore/builds/buildsavedstate.h>
#include <zenremotestore/builds/buildstoragecache.h>
#include <zenremotestore/builds/buildstorageoperations.h>
#include <zenremotestore/builds/filebuildstorage.h>
@@ -475,353 +476,6 @@ namespace {
return Count * 1000000 / ElapsedWallTimeUS;
}
- void CalculateLocalChunkOrders(const std::span<const uint32_t>& AbsoluteChunkOrders,
- const std::span<const IoHash> LooseChunkHashes,
- const std::span<const uint64_t> LooseChunkRawSizes,
- const std::span<const ChunkBlockDescription>& BlockDescriptions,
- std::vector<IoHash>& OutLocalChunkHashes,
- std::vector<uint64_t>& OutLocalChunkRawSizes,
- std::vector<uint32_t>& OutLocalChunkOrders)
- {
- ZEN_TRACE_CPU("CalculateLocalChunkOrders");
-
- std::vector<IoHash> AbsoluteChunkHashes;
- std::vector<uint64_t> AbsoluteChunkRawSizes;
- AbsoluteChunkHashes.insert(AbsoluteChunkHashes.end(), LooseChunkHashes.begin(), LooseChunkHashes.end());
- AbsoluteChunkRawSizes.insert(AbsoluteChunkRawSizes.end(), LooseChunkRawSizes.begin(), LooseChunkRawSizes.end());
- for (const ChunkBlockDescription& Block : BlockDescriptions)
- {
- AbsoluteChunkHashes.insert(AbsoluteChunkHashes.end(), Block.ChunkRawHashes.begin(), Block.ChunkRawHashes.end());
- AbsoluteChunkRawSizes.insert(AbsoluteChunkRawSizes.end(), Block.ChunkRawLengths.begin(), Block.ChunkRawLengths.end());
- }
- OutLocalChunkHashes.reserve(AbsoluteChunkHashes.size());
- OutLocalChunkRawSizes.reserve(AbsoluteChunkRawSizes.size());
- OutLocalChunkOrders.reserve(AbsoluteChunkOrders.size());
-
- tsl::robin_map<IoHash, uint32_t, IoHash::Hasher> ChunkHashToChunkIndex;
- ChunkHashToChunkIndex.reserve(AbsoluteChunkHashes.size());
-
- for (uint32_t AbsoluteChunkOrderIndex = 0; AbsoluteChunkOrderIndex < AbsoluteChunkOrders.size(); AbsoluteChunkOrderIndex++)
- {
- const uint32_t AbsoluteChunkIndex = AbsoluteChunkOrders[AbsoluteChunkOrderIndex];
- const IoHash& AbsoluteChunkHash = AbsoluteChunkHashes[AbsoluteChunkIndex];
- const uint64_t AbsoluteChunkRawSize = AbsoluteChunkRawSizes[AbsoluteChunkIndex];
-
- if (auto It = ChunkHashToChunkIndex.find(AbsoluteChunkHash); It != ChunkHashToChunkIndex.end())
- {
- const uint32_t LocalChunkIndex = It->second;
- OutLocalChunkOrders.push_back(LocalChunkIndex);
- }
- else
- {
- uint32_t LocalChunkIndex = gsl::narrow<uint32_t>(OutLocalChunkHashes.size());
- OutLocalChunkHashes.push_back(AbsoluteChunkHash);
- OutLocalChunkRawSizes.push_back(AbsoluteChunkRawSize);
- OutLocalChunkOrders.push_back(LocalChunkIndex);
- ChunkHashToChunkIndex.insert_or_assign(AbsoluteChunkHash, LocalChunkIndex);
- }
-#if EXTRA_VERIFY
- const uint32_t LocalChunkIndex = OutLocalChunkOrders[AbsoluteChunkOrderIndex];
- const IoHash& LocalChunkHash = OutLocalChunkHashes[LocalChunkIndex];
- const uint64_t& LocalChunkRawSize = OutLocalChunkRawSizes[LocalChunkIndex];
- ZEN_ASSERT(LocalChunkHash == AbsoluteChunkHash);
- ZEN_ASSERT(LocalChunkRawSize == AbsoluteChunkRawSize);
-#endif // EXTRA_VERIFY
- }
-#if EXTRA_VERIFY
- for (uint32_t OrderIndex = 0; OrderIndex < OutLocalChunkOrders.size(); OrderIndex++)
- {
- uint32_t LocalChunkIndex = OutLocalChunkOrders[OrderIndex];
- const IoHash LocalChunkHash = OutLocalChunkHashes[LocalChunkIndex];
- uint64_t LocalChunkRawSize = OutLocalChunkRawSizes[LocalChunkIndex];
-
- uint32_t VerifyChunkIndex = AbsoluteChunkOrders[OrderIndex];
- const IoHash VerifyChunkHash = AbsoluteChunkHashes[VerifyChunkIndex];
- uint64_t VerifyChunkRawSize = AbsoluteChunkRawSizes[VerifyChunkIndex];
-
- ZEN_ASSERT(LocalChunkHash == VerifyChunkHash);
- ZEN_ASSERT(LocalChunkRawSize == VerifyChunkRawSize);
- }
-#endif // EXTRA_VERIFY
- }
-
- void ReadBuildContentFromCompactBinary(CbObjectView BuildPartManifest,
- SourcePlatform& OutPlatform,
- std::vector<std::filesystem::path>& OutPaths,
- std::vector<IoHash>& OutRawHashes,
- std::vector<uint64_t>& OutRawSizes,
- std::vector<uint32_t>& OutAttributes,
- std::vector<IoHash>& OutSequenceRawHashes,
- std::vector<uint32_t>& OutChunkCounts,
- std::vector<uint32_t>& OutAbsoluteChunkOrders,
- std::vector<IoHash>& OutLooseChunkHashes,
- std::vector<uint64_t>& OutLooseChunkRawSizes,
- std::vector<IoHash>& OutBlockRawHashes)
- {
- OutPlatform = FromString(BuildPartManifest["platform"sv].AsString(), SourcePlatform::_Count);
-
- CbObjectView FilesObject = BuildPartManifest["files"sv].AsObjectView();
-
- compactbinary_helpers::ReadArray("paths"sv, FilesObject, OutPaths);
- compactbinary_helpers::ReadArray("rawhashes"sv, FilesObject, OutRawHashes);
- compactbinary_helpers::ReadArray("rawsizes"sv, FilesObject, OutRawSizes);
-
- uint64_t PathCount = OutPaths.size();
- if (OutRawHashes.size() != PathCount)
- {
- throw std::runtime_error(fmt::format("Number of raw hashes entries does not match number of paths"));
- }
- if (OutRawSizes.size() != PathCount)
- {
- throw std::runtime_error(fmt::format("Number of raw sizes entries does not match number of paths"));
- }
-
- std::vector<uint32_t> ModeArray;
- compactbinary_helpers::ReadArray("mode"sv, FilesObject, ModeArray);
- if (ModeArray.size() != PathCount && ModeArray.size() != 0)
- {
- throw std::runtime_error(fmt::format("Number of attribute entries does not match number of paths"));
- }
-
- std::vector<uint32_t> AttributeArray;
- compactbinary_helpers::ReadArray("attributes"sv, FilesObject, ModeArray);
- if (AttributeArray.size() != PathCount && AttributeArray.size() != 0)
- {
- throw std::runtime_error(fmt::format("Number of attribute entries does not match number of paths"));
- }
-
- if (ModeArray.size() > 0)
- {
- if (OutPlatform == SourcePlatform::_Count)
- {
- OutPlatform = SourcePlatform::Linux; // Best guess - under dev format
- }
- OutAttributes = std::move(ModeArray);
- }
- else if (AttributeArray.size() > 0)
- {
- if (OutPlatform == SourcePlatform::_Count)
- {
- OutPlatform = SourcePlatform::Windows;
- }
- OutAttributes = std::move(AttributeArray);
- }
- else
- {
- if (OutPlatform == SourcePlatform::_Count)
- {
- OutPlatform = GetSourceCurrentPlatform();
- }
- }
-
- if (CbObjectView ChunkContentView = BuildPartManifest["chunkedContent"sv].AsObjectView(); ChunkContentView)
- {
- compactbinary_helpers::ReadArray("sequenceRawHashes"sv, ChunkContentView, OutSequenceRawHashes);
- compactbinary_helpers::ReadArray("chunkcounts"sv, ChunkContentView, OutChunkCounts);
- if (OutChunkCounts.size() != OutSequenceRawHashes.size())
- {
- throw std::runtime_error(fmt::format("Number of chunk count entries does not match number of paths"));
- }
- compactbinary_helpers::ReadArray("chunkorders"sv, ChunkContentView, OutAbsoluteChunkOrders);
- }
- else if (FilesObject["chunkcounts"sv])
- {
- // Legacy zen style
-
- std::vector<uint32_t> LegacyChunkCounts;
- compactbinary_helpers::ReadArray("chunkcounts"sv, FilesObject, LegacyChunkCounts);
- if (LegacyChunkCounts.size() != PathCount)
- {
- throw std::runtime_error(fmt::format("Number of chunk count entries does not match number of paths"));
- }
- std::vector<uint32_t> LegacyAbsoluteChunkOrders;
- compactbinary_helpers::ReadArray("chunkorders"sv, FilesObject, LegacyAbsoluteChunkOrders);
-
- CbArrayView ChunkOrdersArray = BuildPartManifest["chunkorders"sv].AsArrayView();
- const uint64_t ChunkOrdersCount = ChunkOrdersArray.Num();
-
- tsl::robin_set<IoHash, IoHash::Hasher> FoundRawHashes;
- FoundRawHashes.reserve(PathCount);
-
- OutChunkCounts.reserve(PathCount);
- OutAbsoluteChunkOrders.reserve(ChunkOrdersCount);
-
- uint32_t OrderIndexOffset = 0;
- for (uint32_t PathIndex = 0; PathIndex < OutPaths.size(); PathIndex++)
- {
- const IoHash& PathRawHash = OutRawHashes[PathIndex];
- uint32_t LegacyChunkCount = LegacyChunkCounts[PathIndex];
-
- if (FoundRawHashes.insert(PathRawHash).second)
- {
- OutSequenceRawHashes.push_back(PathRawHash);
- OutChunkCounts.push_back(LegacyChunkCount);
- std::span<uint32_t> AbsoluteChunkOrder =
- std::span<uint32_t>(LegacyAbsoluteChunkOrders).subspan(OrderIndexOffset, LegacyChunkCount);
- OutAbsoluteChunkOrders.insert(OutAbsoluteChunkOrders.end(), AbsoluteChunkOrder.begin(), AbsoluteChunkOrder.end());
- }
- OrderIndexOffset += LegacyChunkCounts[PathIndex];
- }
- }
- else
- {
- // Legacy C# style
-
- tsl::robin_set<IoHash, IoHash::Hasher> FoundRawHashes;
- FoundRawHashes.reserve(PathCount);
- uint32_t OrderIndexOffset = 0;
- for (uint32_t PathIndex = 0; PathIndex < OutPaths.size(); PathIndex++)
- {
- if (OutRawSizes[PathIndex] > 0)
- {
- const IoHash& PathRawHash = OutRawHashes[PathIndex];
- if (FoundRawHashes.insert(PathRawHash).second)
- {
- OutSequenceRawHashes.push_back(PathRawHash);
- OutChunkCounts.push_back(1);
- OutAbsoluteChunkOrders.push_back(OrderIndexOffset);
- OutLooseChunkHashes.push_back(PathRawHash);
- OutLooseChunkRawSizes.push_back(OutRawSizes[PathIndex]);
- OrderIndexOffset += 1;
- }
- }
- }
- }
-
- CbObjectView ChunkAttachmentsView = BuildPartManifest["chunkAttachments"sv].AsObjectView();
- {
- compactbinary_helpers::ReadBinaryAttachmentArray("rawHashes"sv, ChunkAttachmentsView, OutLooseChunkHashes);
- compactbinary_helpers::ReadArray("chunkRawSizes"sv, ChunkAttachmentsView, OutLooseChunkRawSizes);
- if (OutLooseChunkHashes.size() != OutLooseChunkRawSizes.size())
- {
- throw std::runtime_error(
- fmt::format("Number of attachment chunk hashes does not match number of attachemnt chunk raw sizes"));
- }
- }
-
- CbObjectView BlocksView = BuildPartManifest["blockAttachments"sv].AsObjectView();
- {
- compactbinary_helpers::ReadBinaryAttachmentArray("rawHashes"sv, BlocksView, OutBlockRawHashes);
- }
- }
-
- bool ReadStateObject(CbObjectView StateView,
- Oid& OutBuildId,
- std::vector<Oid>& BuildPartsIds,
- std::vector<std::string>& BuildPartsNames,
- std::vector<ChunkedFolderContent>& OutPartContents,
- FolderContent& OutLocalFolderState)
- {
- try
- {
- CbObjectView BuildView = StateView["builds"sv].AsArrayView().CreateViewIterator().AsObjectView();
- OutBuildId = BuildView["buildId"sv].AsObjectId();
- for (CbFieldView PartView : BuildView["parts"sv].AsArrayView())
- {
- CbObjectView PartObjectView = PartView.AsObjectView();
- BuildPartsIds.push_back(PartObjectView["partId"sv].AsObjectId());
- BuildPartsNames.push_back(std::string(PartObjectView["partName"sv].AsString()));
- OutPartContents.push_back(LoadChunkedFolderContentToCompactBinary(PartObjectView["content"sv].AsObjectView()));
- }
- OutLocalFolderState = LoadFolderContentToCompactBinary(StateView["localFolderState"sv].AsObjectView());
- return true;
- }
- catch (const std::exception& Ex)
- {
- ZEN_CONSOLE_WARN("Unable to read local state: ", Ex.what());
- return false;
- }
- }
-
- CbObject CreateStateObject(const Oid& BuildId,
- const std::vector<std::pair<Oid, std::string>>& AllBuildParts,
- std::span<const ChunkedFolderContent> PartContents,
- const FolderContent& LocalFolderState,
- const std::filesystem::path& LocalPath)
- {
- CbObjectWriter CurrentStateWriter;
- CurrentStateWriter.AddString("path", (const char*)LocalPath.u8string().c_str());
- CurrentStateWriter.BeginArray("builds"sv);
- {
- CurrentStateWriter.BeginObject();
- {
- CurrentStateWriter.AddObjectId("buildId"sv, BuildId);
- CurrentStateWriter.BeginArray("parts"sv);
- for (size_t PartIndex = 0; PartIndex < AllBuildParts.size(); PartIndex++)
- {
- const Oid BuildPartId = AllBuildParts[PartIndex].first;
- CurrentStateWriter.BeginObject();
- {
- CurrentStateWriter.AddObjectId("partId"sv, BuildPartId);
- CurrentStateWriter.AddString("partName"sv, AllBuildParts[PartIndex].second);
- CurrentStateWriter.BeginObject("content");
- {
- SaveChunkedFolderContentToCompactBinary(PartContents[PartIndex], CurrentStateWriter);
- }
- CurrentStateWriter.EndObject();
- }
- CurrentStateWriter.EndObject();
- }
- CurrentStateWriter.EndArray(); // parts
- }
- CurrentStateWriter.EndObject();
- }
- CurrentStateWriter.EndArray(); // builds
-
- CurrentStateWriter.BeginObject("localFolderState"sv);
- {
- SaveFolderContentToCompactBinary(LocalFolderState, CurrentStateWriter);
- }
- CurrentStateWriter.EndObject(); // localFolderState
-
- return CurrentStateWriter.Save();
- }
-
- void AddDownloadedPath(const std::filesystem::path& SystemRootDir,
- const Oid& BuildId,
- const std::vector<std::pair<Oid, std::string>>& BuildParts,
- const std::filesystem::path& StateFilePath,
- const std::filesystem::path& LocalPath)
- {
- ZEN_ASSERT(!SystemRootDir.empty());
- ZEN_ASSERT(!StateFilePath.empty());
- ZEN_ASSERT(!LocalPath.empty());
- const std::u8string LocalPathString = LocalPath.generic_u8string();
- IoHash PathHash = IoHash::HashBuffer(LocalPathString.data(), LocalPathString.length());
- std::filesystem::path WritePath = SystemRootDir / "builds" / "downloads" / (PathHash.ToHexString() + ".json");
- CreateDirectories(WritePath.parent_path());
- CbObjectWriter Writer;
- Writer.AddString("path", (const char*)LocalPath.u8string().c_str());
- Writer.AddString("statePath", (const char*)StateFilePath.u8string().c_str());
- Writer.AddDateTime("date", DateTime::Now());
- Writer.BeginArray("builds"sv);
- {
- Writer.BeginObject();
- {
- Writer.AddObjectId("buildId", BuildId);
- Writer.BeginArray("parts");
- for (const auto& It : BuildParts)
- {
- Writer.BeginObject();
- {
- Writer.AddObjectId("partId", It.first);
- Writer.AddString("partName", It.second);
- }
- Writer.EndObject();
- }
- Writer.EndArray(); // parts
- }
- Writer.EndObject();
- }
- Writer.EndArray(); // builds
-
- CbObject Payload = Writer.Save();
- ExtendableStringBuilder<512> SB;
- CompactBinaryToJson(Payload.GetView(), SB);
- MemoryView JsonPayload(SB.Data(), SB.Size());
- TemporaryFile::SafeWriteFile(WritePath, JsonPayload);
- }
-
void ValidateBuildPart(BuildStorage& Storage, const Oid& BuildId, Oid BuildPartId, const std::string_view BuildPartName)
{
ZEN_TRACE_CPU("ValidateBuildPart");
@@ -1294,53 +948,6 @@ namespace {
}
}
- // TODO: Move out of this file and make it one with implementation in buildstorageoperations.cpp
- bool ReadStateFile(const std::filesystem::path& StateFilePath,
- FolderContent& OutLocalFolderState,
- ChunkedFolderContent& OutLocalContent)
- {
- ZEN_TRACE_CPU("ReadStateFile");
- bool HasLocalState = false;
- try
- {
- CbObject CurrentStateObject = LoadCompactBinaryObject(StateFilePath).Object;
- if (CurrentStateObject)
- {
- Oid CurrentBuildId;
- std::vector<Oid> SavedBuildPartIds;
- std::vector<std::string> SavedBuildPartsNames;
- std::vector<ChunkedFolderContent> SavedPartContents;
- if (ReadStateObject(CurrentStateObject,
- CurrentBuildId,
- SavedBuildPartIds,
- SavedBuildPartsNames,
- SavedPartContents,
- OutLocalFolderState))
- {
- if (!SavedPartContents.empty())
- {
- if (SavedPartContents.size() == 1)
- {
- OutLocalContent = std::move(SavedPartContents[0]);
- }
- else
- {
- OutLocalContent =
- MergeChunkedFolderContents(SavedPartContents[0],
- std::span<const ChunkedFolderContent>(SavedPartContents).subspan(1));
- }
- HasLocalState = true;
- }
- }
- }
- }
- catch (const std::exception& Ex)
- {
- ZEN_CONSOLE_WARN("Failed reading state file {}, falling back to scannning. Reason: {}", StateFilePath, Ex.what());
- }
- return HasLocalState;
- }
-
FolderContent GetValidFolderContent(GetFolderContentStatistics& LocalFolderScanStats,
const std::filesystem::path& Path,
std::span<const std::filesystem::path> PathsToCheck,
@@ -2004,12 +1611,26 @@ namespace {
FolderContent LocalFolderState;
ChunkedFolderContent LocalContent;
- Stopwatch ReadStateTimer;
- const bool HasLocalState = IsFile(StateFilePath) && ReadStateFile(StateFilePath, LocalFolderState, LocalContent);
- if (HasLocalState && !IsQuiet)
+ Stopwatch ReadStateTimer;
+ bool HasLocalState = false;
+ bool FileExists = IsFile(StateFilePath);
+ if (FileExists)
{
- ZEN_CONSOLE("Read local state file {} in {}", StateFilePath, NiceTimeSpanMs(ReadStateTimer.GetElapsedTimeMs()));
+ try
+ {
+ ReadStateFile(StateFilePath, LocalFolderState, LocalContent);
+ if (IsQuiet)
+ {
+ ZEN_CONSOLE("Read local state file {} in {}", StateFilePath, NiceTimeSpanMs(ReadStateTimer.GetElapsedTimeMs()));
+ }
+ HasLocalState = true;
+ }
+ catch (const std::exception& Ex)
+ {
+ ZEN_CONSOLE_WARN("Failed reading state file {}, falling back to scannning. Reason: {}", StateFilePath, Ex.what());
+ }
}
+
{
const uint32_t LocalPathCount = gsl::narrow<uint32_t>(LocalFolderState.Paths.size());
const uint32_t RemotePathCount = gsl::narrow<uint32_t>(ReferencePaths.size());
@@ -2446,49 +2067,8 @@ namespace {
{
return;
}
- auto CompareContent = [](const ChunkedFolderContent& Lhs, const ChunkedFolderContent& Rhs) {
- tsl::robin_map<std::string, size_t> RhsPathToIndex;
- const size_t RhsPathCount = Rhs.Paths.size();
- RhsPathToIndex.reserve(RhsPathCount);
- for (size_t RhsPathIndex = 0; RhsPathIndex < RhsPathCount; RhsPathIndex++)
- {
- RhsPathToIndex.insert({Rhs.Paths[RhsPathIndex].generic_string(), RhsPathIndex});
- }
- const size_t LhsPathCount = Lhs.Paths.size();
- for (size_t LhsPathIndex = 0; LhsPathIndex < LhsPathCount; LhsPathIndex++)
- {
- if (auto It = RhsPathToIndex.find(Lhs.Paths[LhsPathIndex].generic_string()); It != RhsPathToIndex.end())
- {
- const size_t RhsPathIndex = It->second;
- if ((Lhs.RawHashes[LhsPathIndex] != Rhs.RawHashes[RhsPathIndex]) ||
- (!FolderContent::AreFileAttributesEqual(Lhs.Attributes[LhsPathIndex], Rhs.Attributes[RhsPathIndex])))
- {
- return false;
- }
- }
- else
- {
- return false;
- }
- }
- tsl::robin_set<std::string> LhsPathExists;
- LhsPathExists.reserve(LhsPathCount);
- for (size_t LhsPathIndex = 0; LhsPathIndex < LhsPathCount; LhsPathIndex++)
- {
- LhsPathExists.insert({Lhs.Paths[LhsPathIndex].generic_string()});
- }
- for (size_t RhsPathIndex = 0; RhsPathIndex < RhsPathCount; RhsPathIndex++)
- {
- if (!LhsPathExists.contains(Rhs.Paths[RhsPathIndex].generic_string()))
- {
- return false;
- }
- }
- return true;
- };
-
- if (Options.EnableTargetFolderScavenging && !Options.CleanTargetFolder && CompareContent(RemoteContent, LocalContent))
+ if (Options.EnableTargetFolderScavenging && !Options.CleanTargetFolder && CompareChunkedContent(RemoteContent, LocalContent))
{
if (!IsQuiet)
{
@@ -2566,6 +2146,8 @@ namespace {
{
if (!Options.PrimeCacheOnly)
{
+ AddDownloadedPath(Options.SystemRootDir, BuildId, AllBuildParts, ZenStateFilePath(Options.ZenFolderPath), Path);
+
ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::Verify, TaskSteps::StepCount);
VerifyFolder(RemoteContent, Path, Options.PostDownloadVerify, VerifyFolderStats);
@@ -2580,8 +2162,6 @@ namespace {
ZEN_CONSOLE("Wrote local state in {}", NiceTimeSpanMs(WriteStateTimer.GetElapsedTimeMs()));
}
- AddDownloadedPath(Options.SystemRootDir, BuildId, AllBuildParts, ZenStateFilePath(Options.ZenFolderPath), Path);
-
#if 0
ExtendableStringBuilder<1024> SB;
CompactBinaryToJson(StateObject, SB);
diff --git a/src/zenremotestore/builds/buildsavedstate.cpp b/src/zenremotestore/builds/buildsavedstate.cpp
new file mode 100644
index 000000000..933616856
--- /dev/null
+++ b/src/zenremotestore/builds/buildsavedstate.cpp
@@ -0,0 +1,443 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#include <zenremotestore/builds/buildsavedstate.h>
+
+#include <zencore/basicfile.h>
+#include <zencore/compactbinaryutil.h>
+#include <zencore/compactbinaryvalidation.h>
+#include <zencore/filesystem.h>
+#include <zencore/fmtutils.h>
+#include <zencore/trace.h>
+
+ZEN_THIRD_PARTY_INCLUDES_START
+#include <tsl/robin_set.h>
+ZEN_THIRD_PARTY_INCLUDES_END
+
+#define EXTRA_VERIFY 0
+
+namespace zen {
+
+using namespace std::literals;
+
+namespace {
+ std::filesystem::path ZenStateDownloadFolder(const std::filesystem::path& SystemRootDir)
+ {
+ return SystemRootDir / "builds" / "downloads";
+ }
+} // namespace
+
+CbObject
+CreateStateObject(const Oid& BuildId,
+ const std::vector<std::pair<Oid, std::string>>& AllBuildParts,
+ std::span<const ChunkedFolderContent> PartContents,
+ const FolderContent& LocalFolderState,
+ const std::filesystem::path& LocalPath)
+{
+ CbObjectWriter CurrentStateWriter;
+ CurrentStateWriter.AddString("path", (const char*)LocalPath.u8string().c_str());
+ CurrentStateWriter.BeginArray("builds"sv);
+ {
+ CurrentStateWriter.BeginObject();
+ {
+ CurrentStateWriter.AddObjectId("buildId"sv, BuildId);
+ CurrentStateWriter.BeginArray("parts"sv);
+ for (size_t PartIndex = 0; PartIndex < AllBuildParts.size(); PartIndex++)
+ {
+ const Oid BuildPartId = AllBuildParts[PartIndex].first;
+ CurrentStateWriter.BeginObject();
+ {
+ CurrentStateWriter.AddObjectId("partId"sv, BuildPartId);
+ CurrentStateWriter.AddString("partName"sv, AllBuildParts[PartIndex].second);
+ CurrentStateWriter.BeginObject("content");
+ {
+ SaveChunkedFolderContentToCompactBinary(PartContents[PartIndex], CurrentStateWriter);
+ }
+ CurrentStateWriter.EndObject();
+ }
+ CurrentStateWriter.EndObject();
+ }
+ CurrentStateWriter.EndArray(); // parts
+ }
+ CurrentStateWriter.EndObject();
+ }
+ CurrentStateWriter.EndArray(); // builds
+
+ CurrentStateWriter.BeginObject("localFolderState"sv);
+ {
+ SaveFolderContentToCompactBinary(LocalFolderState, CurrentStateWriter);
+ }
+ CurrentStateWriter.EndObject(); // localFolderState
+
+ return CurrentStateWriter.Save();
+}
+
+void
+ReadBuildContentFromCompactBinary(CbObjectView BuildPartManifest,
+ SourcePlatform& OutPlatform,
+ std::vector<std::filesystem::path>& OutPaths,
+ std::vector<IoHash>& OutRawHashes,
+ std::vector<uint64_t>& OutRawSizes,
+ std::vector<uint32_t>& OutAttributes,
+ std::vector<IoHash>& OutSequenceRawHashes,
+ std::vector<uint32_t>& OutChunkCounts,
+ std::vector<uint32_t>& OutAbsoluteChunkOrders,
+ std::vector<IoHash>& OutLooseChunkHashes,
+ std::vector<uint64_t>& OutLooseChunkRawSizes,
+ std::vector<IoHash>& OutBlockRawHashes)
+{
+ OutPlatform = FromString(BuildPartManifest["platform"sv].AsString(), SourcePlatform::_Count);
+
+ CbObjectView FilesObject = BuildPartManifest["files"sv].AsObjectView();
+
+ compactbinary_helpers::ReadArray("paths"sv, FilesObject, OutPaths);
+ compactbinary_helpers::ReadArray("rawhashes"sv, FilesObject, OutRawHashes);
+ compactbinary_helpers::ReadArray("rawsizes"sv, FilesObject, OutRawSizes);
+
+ uint64_t PathCount = OutPaths.size();
+ if (OutRawHashes.size() != PathCount)
+ {
+ throw std::runtime_error(fmt::format("Number of raw hashes entries does not match number of paths"));
+ }
+ if (OutRawSizes.size() != PathCount)
+ {
+ throw std::runtime_error(fmt::format("Number of raw sizes entries does not match number of paths"));
+ }
+
+ std::vector<uint32_t> ModeArray;
+ compactbinary_helpers::ReadArray("mode"sv, FilesObject, ModeArray);
+ if (ModeArray.size() != PathCount && ModeArray.size() != 0)
+ {
+ throw std::runtime_error(fmt::format("Number of attribute entries does not match number of paths"));
+ }
+
+ std::vector<uint32_t> AttributeArray;
+ compactbinary_helpers::ReadArray("attributes"sv, FilesObject, ModeArray);
+ if (AttributeArray.size() != PathCount && AttributeArray.size() != 0)
+ {
+ throw std::runtime_error(fmt::format("Number of attribute entries does not match number of paths"));
+ }
+
+ if (ModeArray.size() > 0)
+ {
+ if (OutPlatform == SourcePlatform::_Count)
+ {
+ OutPlatform = SourcePlatform::Linux; // Best guess - under dev format
+ }
+ OutAttributes = std::move(ModeArray);
+ }
+ else if (AttributeArray.size() > 0)
+ {
+ if (OutPlatform == SourcePlatform::_Count)
+ {
+ OutPlatform = SourcePlatform::Windows;
+ }
+ OutAttributes = std::move(AttributeArray);
+ }
+ else
+ {
+ if (OutPlatform == SourcePlatform::_Count)
+ {
+ OutPlatform = GetSourceCurrentPlatform();
+ }
+ }
+
+ if (CbObjectView ChunkContentView = BuildPartManifest["chunkedContent"sv].AsObjectView(); ChunkContentView)
+ {
+ compactbinary_helpers::ReadArray("sequenceRawHashes"sv, ChunkContentView, OutSequenceRawHashes);
+ compactbinary_helpers::ReadArray("chunkcounts"sv, ChunkContentView, OutChunkCounts);
+ if (OutChunkCounts.size() != OutSequenceRawHashes.size())
+ {
+ throw std::runtime_error(fmt::format("Number of chunk count entries does not match number of paths"));
+ }
+ compactbinary_helpers::ReadArray("chunkorders"sv, ChunkContentView, OutAbsoluteChunkOrders);
+ }
+ else if (FilesObject["chunkcounts"sv])
+ {
+ // Legacy zen style
+
+ std::vector<uint32_t> LegacyChunkCounts;
+ compactbinary_helpers::ReadArray("chunkcounts"sv, FilesObject, LegacyChunkCounts);
+ if (LegacyChunkCounts.size() != PathCount)
+ {
+ throw std::runtime_error(fmt::format("Number of chunk count entries does not match number of paths"));
+ }
+ std::vector<uint32_t> LegacyAbsoluteChunkOrders;
+ compactbinary_helpers::ReadArray("chunkorders"sv, FilesObject, LegacyAbsoluteChunkOrders);
+
+ CbArrayView ChunkOrdersArray = BuildPartManifest["chunkorders"sv].AsArrayView();
+ const uint64_t ChunkOrdersCount = ChunkOrdersArray.Num();
+
+ tsl::robin_set<IoHash, IoHash::Hasher> FoundRawHashes;
+ FoundRawHashes.reserve(PathCount);
+
+ OutChunkCounts.reserve(PathCount);
+ OutAbsoluteChunkOrders.reserve(ChunkOrdersCount);
+
+ uint32_t OrderIndexOffset = 0;
+ for (uint32_t PathIndex = 0; PathIndex < OutPaths.size(); PathIndex++)
+ {
+ const IoHash& PathRawHash = OutRawHashes[PathIndex];
+ uint32_t LegacyChunkCount = LegacyChunkCounts[PathIndex];
+
+ if (FoundRawHashes.insert(PathRawHash).second)
+ {
+ OutSequenceRawHashes.push_back(PathRawHash);
+ OutChunkCounts.push_back(LegacyChunkCount);
+ std::span<uint32_t> AbsoluteChunkOrder =
+ std::span<uint32_t>(LegacyAbsoluteChunkOrders).subspan(OrderIndexOffset, LegacyChunkCount);
+ OutAbsoluteChunkOrders.insert(OutAbsoluteChunkOrders.end(), AbsoluteChunkOrder.begin(), AbsoluteChunkOrder.end());
+ }
+ OrderIndexOffset += LegacyChunkCounts[PathIndex];
+ }
+ }
+ else
+ {
+ // Legacy C# style
+
+ tsl::robin_set<IoHash, IoHash::Hasher> FoundRawHashes;
+ FoundRawHashes.reserve(PathCount);
+ uint32_t OrderIndexOffset = 0;
+ for (uint32_t PathIndex = 0; PathIndex < OutPaths.size(); PathIndex++)
+ {
+ if (OutRawSizes[PathIndex] > 0)
+ {
+ const IoHash& PathRawHash = OutRawHashes[PathIndex];
+ if (FoundRawHashes.insert(PathRawHash).second)
+ {
+ OutSequenceRawHashes.push_back(PathRawHash);
+ OutChunkCounts.push_back(1);
+ OutAbsoluteChunkOrders.push_back(OrderIndexOffset);
+ OutLooseChunkHashes.push_back(PathRawHash);
+ OutLooseChunkRawSizes.push_back(OutRawSizes[PathIndex]);
+ OrderIndexOffset += 1;
+ }
+ }
+ }
+ }
+
+ CbObjectView ChunkAttachmentsView = BuildPartManifest["chunkAttachments"sv].AsObjectView();
+ {
+ compactbinary_helpers::ReadBinaryAttachmentArray("rawHashes"sv, ChunkAttachmentsView, OutLooseChunkHashes);
+ compactbinary_helpers::ReadArray("chunkRawSizes"sv, ChunkAttachmentsView, OutLooseChunkRawSizes);
+ if (OutLooseChunkHashes.size() != OutLooseChunkRawSizes.size())
+ {
+ throw std::runtime_error(fmt::format("Number of attachment chunk hashes does not match number of attachemnt chunk raw sizes"));
+ }
+ }
+
+ CbObjectView BlocksView = BuildPartManifest["blockAttachments"sv].AsObjectView();
+ {
+ compactbinary_helpers::ReadBinaryAttachmentArray("rawHashes"sv, BlocksView, OutBlockRawHashes);
+ }
+}
+
+void
+CalculateLocalChunkOrders(const std::span<const uint32_t>& AbsoluteChunkOrders,
+ const std::span<const IoHash> LooseChunkHashes,
+ const std::span<const uint64_t> LooseChunkRawSizes,
+ const std::span<const ChunkBlockDescription>& BlockDescriptions,
+ std::vector<IoHash>& OutLocalChunkHashes,
+ std::vector<uint64_t>& OutLocalChunkRawSizes,
+ std::vector<uint32_t>& OutLocalChunkOrders)
+{
+ ZEN_TRACE_CPU("CalculateLocalChunkOrders");
+
+ std::vector<IoHash> AbsoluteChunkHashes;
+ std::vector<uint64_t> AbsoluteChunkRawSizes;
+ AbsoluteChunkHashes.insert(AbsoluteChunkHashes.end(), LooseChunkHashes.begin(), LooseChunkHashes.end());
+ AbsoluteChunkRawSizes.insert(AbsoluteChunkRawSizes.end(), LooseChunkRawSizes.begin(), LooseChunkRawSizes.end());
+ for (const ChunkBlockDescription& Block : BlockDescriptions)
+ {
+ AbsoluteChunkHashes.insert(AbsoluteChunkHashes.end(), Block.ChunkRawHashes.begin(), Block.ChunkRawHashes.end());
+ AbsoluteChunkRawSizes.insert(AbsoluteChunkRawSizes.end(), Block.ChunkRawLengths.begin(), Block.ChunkRawLengths.end());
+ }
+ OutLocalChunkHashes.reserve(AbsoluteChunkHashes.size());
+ OutLocalChunkRawSizes.reserve(AbsoluteChunkRawSizes.size());
+ OutLocalChunkOrders.reserve(AbsoluteChunkOrders.size());
+
+ tsl::robin_map<IoHash, uint32_t, IoHash::Hasher> ChunkHashToChunkIndex;
+ ChunkHashToChunkIndex.reserve(AbsoluteChunkHashes.size());
+
+ for (uint32_t AbsoluteChunkOrderIndex = 0; AbsoluteChunkOrderIndex < AbsoluteChunkOrders.size(); AbsoluteChunkOrderIndex++)
+ {
+ const uint32_t AbsoluteChunkIndex = AbsoluteChunkOrders[AbsoluteChunkOrderIndex];
+ const IoHash& AbsoluteChunkHash = AbsoluteChunkHashes[AbsoluteChunkIndex];
+ const uint64_t AbsoluteChunkRawSize = AbsoluteChunkRawSizes[AbsoluteChunkIndex];
+
+ if (auto It = ChunkHashToChunkIndex.find(AbsoluteChunkHash); It != ChunkHashToChunkIndex.end())
+ {
+ const uint32_t LocalChunkIndex = It->second;
+ OutLocalChunkOrders.push_back(LocalChunkIndex);
+ }
+ else
+ {
+ uint32_t LocalChunkIndex = gsl::narrow<uint32_t>(OutLocalChunkHashes.size());
+ OutLocalChunkHashes.push_back(AbsoluteChunkHash);
+ OutLocalChunkRawSizes.push_back(AbsoluteChunkRawSize);
+ OutLocalChunkOrders.push_back(LocalChunkIndex);
+ ChunkHashToChunkIndex.insert_or_assign(AbsoluteChunkHash, LocalChunkIndex);
+ }
+#if EXTRA_VERIFY
+ const uint32_t LocalChunkIndex = OutLocalChunkOrders[AbsoluteChunkOrderIndex];
+ const IoHash& LocalChunkHash = OutLocalChunkHashes[LocalChunkIndex];
+ const uint64_t& LocalChunkRawSize = OutLocalChunkRawSizes[LocalChunkIndex];
+ ZEN_ASSERT(LocalChunkHash == AbsoluteChunkHash);
+ ZEN_ASSERT(LocalChunkRawSize == AbsoluteChunkRawSize);
+#endif // EXTRA_VERIFY
+ }
+#if EXTRA_VERIFY
+ for (uint32_t OrderIndex = 0; OrderIndex < OutLocalChunkOrders.size(); OrderIndex++)
+ {
+ uint32_t LocalChunkIndex = OutLocalChunkOrders[OrderIndex];
+ const IoHash LocalChunkHash = OutLocalChunkHashes[LocalChunkIndex];
+ uint64_t LocalChunkRawSize = OutLocalChunkRawSizes[LocalChunkIndex];
+
+ uint32_t VerifyChunkIndex = AbsoluteChunkOrders[OrderIndex];
+ const IoHash VerifyChunkHash = AbsoluteChunkHashes[VerifyChunkIndex];
+ uint64_t VerifyChunkRawSize = AbsoluteChunkRawSizes[VerifyChunkIndex];
+
+ ZEN_ASSERT(LocalChunkHash == VerifyChunkHash);
+ ZEN_ASSERT(LocalChunkRawSize == VerifyChunkRawSize);
+ }
+#endif // EXTRA_VERIFY
+}
+
+void
+ReadStateObject(CbObjectView StateView,
+ Oid& OutBuildId,
+ std::vector<Oid>& BuildPartsIds,
+ std::vector<std::string>& BuildPartsNames,
+ std::vector<ChunkedFolderContent>& OutPartContents,
+ FolderContent& OutLocalFolderState)
+{
+ CbObjectView BuildView = StateView["builds"sv].AsArrayView().CreateViewIterator().AsObjectView();
+ OutBuildId = BuildView["buildId"sv].AsObjectId();
+ for (CbFieldView PartView : BuildView["parts"sv].AsArrayView())
+ {
+ CbObjectView PartObjectView = PartView.AsObjectView();
+ BuildPartsIds.push_back(PartObjectView["partId"sv].AsObjectId());
+ BuildPartsNames.push_back(std::string(PartObjectView["partName"sv].AsString()));
+ OutPartContents.push_back(LoadChunkedFolderContentToCompactBinary(PartObjectView["content"sv].AsObjectView()));
+ }
+ OutLocalFolderState = LoadFolderContentToCompactBinary(StateView["localFolderState"sv].AsObjectView());
+}
+void
+ReadStateFile(const std::filesystem::path& StateFilePath, FolderContent& OutLocalFolderState, ChunkedFolderContent& OutLocalContent)
+{
+ ZEN_TRACE_CPU("ReadStateFile");
+
+ FileContents FileData = ReadFile(StateFilePath);
+ CbValidateError ValidateError;
+ if (CbObject CurrentStateObject = ValidateAndReadCompactBinaryObject(FileData.Flatten(), ValidateError);
+ ValidateError == CbValidateError::None)
+ {
+ if (CurrentStateObject)
+ {
+ Oid CurrentBuildId;
+ std::vector<Oid> SavedBuildPartIds;
+ std::vector<std::string> SavedBuildPartsNames;
+ std::vector<ChunkedFolderContent> SavedPartContents;
+ ReadStateObject(CurrentStateObject,
+ CurrentBuildId,
+ SavedBuildPartIds,
+ SavedBuildPartsNames,
+ SavedPartContents,
+ OutLocalFolderState);
+ if (!SavedPartContents.empty())
+ {
+ if (SavedPartContents.size() == 1)
+ {
+ OutLocalContent = std::move(SavedPartContents[0]);
+ }
+ else
+ {
+ OutLocalContent = MergeChunkedFolderContents(SavedPartContents[0],
+ std::span<const ChunkedFolderContent>(SavedPartContents).subspan(1));
+ }
+ }
+ }
+ }
+ else
+ {
+ throw std::runtime_error(
+ fmt::format("Failed to read compact binary object from '{}', reason: {}", StateFilePath, ToString(ValidateError)));
+ }
+}
+
+void
+AddDownloadedPath(const std::filesystem::path& SystemRootDir,
+ const Oid& BuildId,
+ const std::vector<std::pair<Oid, std::string>>& BuildParts,
+ const std::filesystem::path& StateFilePath,
+ const std::filesystem::path& LocalPath)
+{
+ ZEN_TRACE_CPU("AddDownloadedPath");
+
+ ZEN_ASSERT(!SystemRootDir.empty());
+ ZEN_ASSERT(!StateFilePath.empty());
+ ZEN_ASSERT(!LocalPath.empty());
+ const std::u8string LocalPathString = LocalPath.generic_u8string();
+ IoHash PathHash = IoHash::HashBuffer(LocalPathString.data(), LocalPathString.length());
+ const std::filesystem::path StateDownloadFolder = ZenStateDownloadFolder(SystemRootDir);
+ CreateDirectories(StateDownloadFolder);
+ std::filesystem::path WritePath = StateDownloadFolder / (PathHash.ToHexString() + ".json");
+
+ CbObjectWriter Writer;
+ Writer.AddString("path", (const char*)LocalPath.u8string().c_str());
+ Writer.AddString("statePath", (const char*)StateFilePath.u8string().c_str());
+ Writer.AddDateTime("date", DateTime::Now());
+ Writer.BeginArray("builds"sv);
+ {
+ Writer.BeginObject();
+ {
+ Writer.AddObjectId("buildId", BuildId);
+ Writer.BeginArray("parts");
+ for (const auto& It : BuildParts)
+ {
+ Writer.BeginObject();
+ {
+ Writer.AddObjectId("partId", It.first);
+ Writer.AddString("partName", It.second);
+ }
+ Writer.EndObject();
+ }
+ Writer.EndArray(); // parts
+ }
+ Writer.EndObject();
+ }
+ Writer.EndArray(); // builds
+
+ CbObject Payload = Writer.Save();
+ ExtendableStringBuilder<512> SB;
+ CompactBinaryToJson(Payload.GetView(), SB);
+ MemoryView JsonPayload(SB.Data(), SB.Size());
+ TemporaryFile::SafeWriteFile(WritePath, JsonPayload);
+}
+
+std::vector<std::filesystem::path>
+GetDownloadedStatePaths(const std::filesystem::path& SystemRootDir)
+{
+ ZEN_ASSERT(!SystemRootDir.empty());
+
+ ZEN_TRACE_CPU("GetDownloadedPaths");
+
+ std::vector<std::filesystem::path> Result;
+
+ const std::filesystem::path StateDownloadFolder = ZenStateDownloadFolder(SystemRootDir);
+ if (IsDir(StateDownloadFolder))
+ {
+ DirectoryContent Content;
+ GetDirectoryContent(ZenStateDownloadFolder(SystemRootDir), DirectoryContentFlags::IncludeFiles, Content);
+ for (const std::filesystem::path& EntryPath : Content.Files)
+ {
+ IoHash EntryPathHash;
+ if (IoHash::TryParse(EntryPath.stem().string(), EntryPathHash))
+ {
+ Result.push_back(EntryPath);
+ }
+ }
+ }
+ return Result;
+}
+
+} // namespace zen
diff --git a/src/zenremotestore/builds/buildstorageoperations.cpp b/src/zenremotestore/builds/buildstorageoperations.cpp
index ca927e643..469d29e0d 100644
--- a/src/zenremotestore/builds/buildstorageoperations.cpp
+++ b/src/zenremotestore/builds/buildstorageoperations.cpp
@@ -2,6 +2,7 @@
#include <zenremotestore/builds/buildstorageoperations.h>
+#include <zenremotestore/builds/buildsavedstate.h>
#include <zenremotestore/builds/buildstorage.h>
#include <zenremotestore/builds/buildstoragecache.h>
#include <zenremotestore/chunking/chunkblock.h>
@@ -343,84 +344,6 @@ namespace {
} // namespace
-bool
-ReadStateObject(BuildOpLogOutput& LogOutput,
- CbObjectView StateView,
- Oid& OutBuildId,
- std::vector<Oid>& BuildPartsIds,
- std::vector<std::string>& BuildPartsNames,
- std::vector<ChunkedFolderContent>& OutPartContents,
- FolderContent& OutLocalFolderState)
-{
- try
- {
- CbObjectView BuildView = StateView["builds"sv].AsArrayView().CreateViewIterator().AsObjectView();
- OutBuildId = BuildView["buildId"sv].AsObjectId();
- for (CbFieldView PartView : BuildView["parts"sv].AsArrayView())
- {
- CbObjectView PartObjectView = PartView.AsObjectView();
- BuildPartsIds.push_back(PartObjectView["partId"sv].AsObjectId());
- BuildPartsNames.push_back(std::string(PartObjectView["partName"sv].AsString()));
- OutPartContents.push_back(LoadChunkedFolderContentToCompactBinary(PartObjectView["content"sv].AsObjectView()));
- }
- OutLocalFolderState = LoadFolderContentToCompactBinary(StateView["localFolderState"sv].AsObjectView());
- return true;
- }
- catch (const std::exception& Ex)
- {
- LOG_OUTPUT_WARN(LogOutput, "Unable to read local state: ", Ex.what());
- return false;
- }
-}
-
-bool
-ReadStateFile(BuildOpLogOutput& LogOutput,
- const std::filesystem::path& StateFilePath,
- FolderContent& OutLocalFolderState,
- ChunkedFolderContent& OutLocalContent)
-{
- ZEN_TRACE_CPU("ReadStateFile");
- bool HasLocalState = false;
- try
- {
- CbObject CurrentStateObject = LoadCompactBinaryObject(StateFilePath).Object;
- if (CurrentStateObject)
- {
- Oid CurrentBuildId;
- std::vector<Oid> SavedBuildPartIds;
- std::vector<std::string> SavedBuildPartsNames;
- std::vector<ChunkedFolderContent> SavedPartContents;
- if (ReadStateObject(LogOutput,
- CurrentStateObject,
- CurrentBuildId,
- SavedBuildPartIds,
- SavedBuildPartsNames,
- SavedPartContents,
- OutLocalFolderState))
- {
- if (!SavedPartContents.empty())
- {
- if (SavedPartContents.size() == 1)
- {
- OutLocalContent = std::move(SavedPartContents[0]);
- }
- else
- {
- OutLocalContent = MergeChunkedFolderContents(SavedPartContents[0],
- std::span<const ChunkedFolderContent>(SavedPartContents).subspan(1));
- }
- HasLocalState = true;
- }
- }
- }
- }
- catch (const std::exception& Ex)
- {
- LOG_OUTPUT_WARN(LogOutput, "Failed reading state file {}, falling back to scannning. Reason: {}", StateFilePath, Ex.what());
- }
- return HasLocalState;
-}
-
struct BlockRangeDescriptor
{
uint32_t BlockIndex = (uint32_t)-1;
@@ -3405,49 +3328,46 @@ BuildsOperationUpdateFolder::FindScavengeSources()
{
ZEN_TRACE_CPU("FindScavengeSources");
+ std::vector<std::filesystem::path> StatePaths = GetDownloadedStatePaths(m_Options.SystemRootDir);
+
std::vector<ScavengeSource> Result;
- DirectoryContent Content;
- GetDirectoryContent(m_Options.SystemRootDir / "builds" / "downloads", DirectoryContentFlags::IncludeFiles, Content);
- for (const std::filesystem::path& EntryPath : Content.Files)
- {
- bool DeleteEntry = false;
- IoHash EntryPathHash;
- if (IoHash::TryParse(EntryPath.stem().string(), EntryPathHash))
- {
- // Read state and verify that it is valid
- IoBuffer MetaDataJson = ReadFile(EntryPath).Flatten();
- std::string_view Json(reinterpret_cast<const char*>(MetaDataJson.GetData()), MetaDataJson.GetSize());
- std::string JsonError;
- CbObject DownloadInfo = LoadCompactBinaryFromJson(Json, JsonError).AsObject();
- if (JsonError.empty())
+ for (const std::filesystem::path& EntryPath : StatePaths)
+ {
+ bool DeleteEntry = false;
+
+ // Read state and verify that it is valid
+ IoBuffer MetaDataJson = ReadFile(EntryPath).Flatten();
+ std::string_view Json(reinterpret_cast<const char*>(MetaDataJson.GetData()), MetaDataJson.GetSize());
+ std::string JsonError;
+ CbObject DownloadInfo = LoadCompactBinaryFromJson(Json, JsonError).AsObject();
+ if (JsonError.empty())
+ {
+ std::filesystem::path StateFilePath = DownloadInfo["statePath"].AsU8String();
+ if (IsFile(StateFilePath))
{
- std::filesystem::path StateFilePath = DownloadInfo["statePath"].AsU8String();
- if (IsFile(StateFilePath))
+ std::filesystem::path Path = DownloadInfo["path"].AsU8String();
+ if (!std::filesystem::equivalent(Path, m_Path))
{
- std::filesystem::path Path = DownloadInfo["path"].AsU8String();
- if (!std::filesystem::equivalent(Path, m_Path))
+ if (IsDir(Path))
{
- if (IsDir(Path))
- {
- Result.push_back({.StateFilePath = std::move(StateFilePath), .Path = std::move(Path)});
- }
- else
- {
- DeleteEntry = true;
- }
+ Result.push_back({.StateFilePath = std::move(StateFilePath), .Path = std::move(Path)});
+ }
+ else
+ {
+ DeleteEntry = true;
}
- }
- else
- {
- DeleteEntry = true;
}
}
else
{
- LOG_OUTPUT_WARN(m_LogOutput, "Invalid download state file at {}. '{}'", EntryPath, JsonError);
DeleteEntry = true;
}
}
+ else
+ {
+ LOG_OUTPUT_WARN(m_LogOutput, "Invalid download state file at {}. '{}'", EntryPath, JsonError);
+ DeleteEntry = true;
+ }
if (DeleteEntry)
{
@@ -3525,12 +3445,13 @@ BuildsOperationUpdateFolder::FindScavengeContent(const ScavengeSource& Source,
ZEN_TRACE_CPU("FindScavengeContent");
FolderContent LocalFolderState;
- if (!ReadStateFile(m_LogOutput, Source.StateFilePath, LocalFolderState, OutScavengedLocalContent))
+ try
{
- return false;
+ ReadStateFile(Source.StateFilePath, LocalFolderState, OutScavengedLocalContent);
}
- if (!IsDir(Source.Path))
+ catch (const std::exception& Ex)
{
+ ZEN_CONSOLE_DEBUG("Skipping invalid build state at '{}', reason: {}", Source.StateFilePath, Ex.what());
return false;
}
@@ -6066,77 +5987,6 @@ BuildsOperationUploadFolder::CalculateAbsoluteChunkOrders(
}
void
-BuildsOperationUploadFolder::CalculateLocalChunkOrders(const std::span<const uint32_t>& AbsoluteChunkOrders,
- const std::span<const IoHash> LooseChunkHashes,
- const std::span<const uint64_t> LooseChunkRawSizes,
- const std::span<const ChunkBlockDescription>& BlockDescriptions,
- std::vector<IoHash>& OutLocalChunkHashes,
- std::vector<uint64_t>& OutLocalChunkRawSizes,
- std::vector<uint32_t>& OutLocalChunkOrders)
-{
- ZEN_TRACE_CPU("CalculateLocalChunkOrders");
-
- std::vector<IoHash> AbsoluteChunkHashes;
- std::vector<uint64_t> AbsoluteChunkRawSizes;
- AbsoluteChunkHashes.insert(AbsoluteChunkHashes.end(), LooseChunkHashes.begin(), LooseChunkHashes.end());
- AbsoluteChunkRawSizes.insert(AbsoluteChunkRawSizes.end(), LooseChunkRawSizes.begin(), LooseChunkRawSizes.end());
- for (const ChunkBlockDescription& Block : BlockDescriptions)
- {
- AbsoluteChunkHashes.insert(AbsoluteChunkHashes.end(), Block.ChunkRawHashes.begin(), Block.ChunkRawHashes.end());
- AbsoluteChunkRawSizes.insert(AbsoluteChunkRawSizes.end(), Block.ChunkRawLengths.begin(), Block.ChunkRawLengths.end());
- }
- OutLocalChunkHashes.reserve(AbsoluteChunkHashes.size());
- OutLocalChunkRawSizes.reserve(AbsoluteChunkRawSizes.size());
- OutLocalChunkOrders.reserve(AbsoluteChunkOrders.size());
-
- tsl::robin_map<IoHash, uint32_t, IoHash::Hasher> ChunkHashToChunkIndex;
- ChunkHashToChunkIndex.reserve(AbsoluteChunkHashes.size());
-
- for (uint32_t AbsoluteChunkOrderIndex = 0; AbsoluteChunkOrderIndex < AbsoluteChunkOrders.size(); AbsoluteChunkOrderIndex++)
- {
- const uint32_t AbsoluteChunkIndex = AbsoluteChunkOrders[AbsoluteChunkOrderIndex];
- const IoHash& AbsoluteChunkHash = AbsoluteChunkHashes[AbsoluteChunkIndex];
- const uint64_t AbsoluteChunkRawSize = AbsoluteChunkRawSizes[AbsoluteChunkIndex];
-
- if (auto It = ChunkHashToChunkIndex.find(AbsoluteChunkHash); It != ChunkHashToChunkIndex.end())
- {
- const uint32_t LocalChunkIndex = It->second;
- OutLocalChunkOrders.push_back(LocalChunkIndex);
- }
- else
- {
- uint32_t LocalChunkIndex = gsl::narrow<uint32_t>(OutLocalChunkHashes.size());
- OutLocalChunkHashes.push_back(AbsoluteChunkHash);
- OutLocalChunkRawSizes.push_back(AbsoluteChunkRawSize);
- OutLocalChunkOrders.push_back(LocalChunkIndex);
- ChunkHashToChunkIndex.insert_or_assign(AbsoluteChunkHash, LocalChunkIndex);
- }
-#if EXTRA_VERIFY
- const uint32_t LocalChunkIndex = OutLocalChunkOrders[AbsoluteChunkOrderIndex];
- const IoHash& LocalChunkHash = OutLocalChunkHashes[LocalChunkIndex];
- const uint64_t& LocalChunkRawSize = OutLocalChunkRawSizes[LocalChunkIndex];
- ZEN_ASSERT(LocalChunkHash == AbsoluteChunkHash);
- ZEN_ASSERT(LocalChunkRawSize == AbsoluteChunkRawSize);
-#endif // EXTRA_VERIFY
- }
-#if EXTRA_VERIFY
- for (uint32_t OrderIndex = 0; OrderIndex < OutLocalChunkOrders.size(); OrderIndex++)
- {
- uint32_t LocalChunkIndex = OutLocalChunkOrders[OrderIndex];
- const IoHash LocalChunkHash = OutLocalChunkHashes[LocalChunkIndex];
- uint64_t LocalChunkRawSize = OutLocalChunkRawSizes[LocalChunkIndex];
-
- uint32_t VerifyChunkIndex = AbsoluteChunkOrders[OrderIndex];
- const IoHash VerifyChunkHash = AbsoluteChunkHashes[VerifyChunkIndex];
- uint64_t VerifyChunkRawSize = AbsoluteChunkRawSizes[VerifyChunkIndex];
-
- ZEN_ASSERT(LocalChunkHash == VerifyChunkHash);
- ZEN_ASSERT(LocalChunkRawSize == VerifyChunkRawSize);
- }
-#endif // EXTRA_VERIFY
-}
-
-void
BuildsOperationUploadFolder::WriteBuildContentToCompactBinary(CbObjectWriter& PartManifestWriter,
const SourcePlatform Platform,
std::span<const std::filesystem::path> Paths,
diff --git a/src/zenremotestore/chunking/chunkedcontent.cpp b/src/zenremotestore/chunking/chunkedcontent.cpp
index 9df7725db..eb0e8bdc9 100644
--- a/src/zenremotestore/chunking/chunkedcontent.cpp
+++ b/src/zenremotestore/chunking/chunkedcontent.cpp
@@ -755,6 +755,50 @@ DeletePathsFromChunkedContent(const ChunkedFolderContent& BaseContent, std::span
return DeletePathsFromChunkedContent(BaseContent, BaseLookup, DeletedPaths);
}
+bool
+CompareChunkedContent(const ChunkedFolderContent& Lhs, const ChunkedFolderContent& Rhs)
+{
+ tsl::robin_map<std::string, size_t> RhsPathToIndex;
+ const size_t RhsPathCount = Rhs.Paths.size();
+ RhsPathToIndex.reserve(RhsPathCount);
+ for (size_t RhsPathIndex = 0; RhsPathIndex < RhsPathCount; RhsPathIndex++)
+ {
+ RhsPathToIndex.insert({Rhs.Paths[RhsPathIndex].generic_string(), RhsPathIndex});
+ }
+ const size_t LhsPathCount = Lhs.Paths.size();
+ for (size_t LhsPathIndex = 0; LhsPathIndex < LhsPathCount; LhsPathIndex++)
+ {
+ if (auto It = RhsPathToIndex.find(Lhs.Paths[LhsPathIndex].generic_string()); It != RhsPathToIndex.end())
+ {
+ const size_t RhsPathIndex = It->second;
+ if ((Lhs.RawHashes[LhsPathIndex] != Rhs.RawHashes[RhsPathIndex]) ||
+ (!FolderContent::AreFileAttributesEqual(Lhs.Attributes[LhsPathIndex], Rhs.Attributes[RhsPathIndex])))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+ tsl::robin_set<std::string> LhsPathExists;
+ LhsPathExists.reserve(LhsPathCount);
+ for (size_t LhsPathIndex = 0; LhsPathIndex < LhsPathCount; LhsPathIndex++)
+ {
+ LhsPathExists.insert({Lhs.Paths[LhsPathIndex].generic_string()});
+ }
+ for (size_t RhsPathIndex = 0; RhsPathIndex < RhsPathCount; RhsPathIndex++)
+ {
+ if (!LhsPathExists.contains(Rhs.Paths[RhsPathIndex].generic_string()))
+ {
+ return false;
+ }
+ }
+
+ return true;
+};
+
ChunkedFolderContent
ChunkFolderContent(ChunkingStatistics& Stats,
WorkerThreadPool& WorkerPool,
diff --git a/src/zenremotestore/include/zenremotestore/builds/buildsavedstate.h b/src/zenremotestore/include/zenremotestore/builds/buildsavedstate.h
new file mode 100644
index 000000000..a11641b0d
--- /dev/null
+++ b/src/zenremotestore/include/zenremotestore/builds/buildsavedstate.h
@@ -0,0 +1,60 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#pragma once
+
+#include <zenremotestore/chunking/chunkblock.h>
+#include <zenremotestore/chunking/chunkedcontent.h>
+
+ZEN_THIRD_PARTY_INCLUDES_START
+#include <tsl/robin_map.h>
+ZEN_THIRD_PARTY_INCLUDES_END
+
+namespace zen {
+
+class CbObjectView;
+
+CbObject CreateStateObject(const Oid& BuildId,
+ const std::vector<std::pair<Oid, std::string>>& AllBuildParts,
+ std::span<const ChunkedFolderContent> PartContents,
+ const FolderContent& LocalFolderState,
+ const std::filesystem::path& LocalPath);
+
+void ReadBuildContentFromCompactBinary(CbObjectView BuildPartManifest,
+ SourcePlatform& OutPlatform,
+ std::vector<std::filesystem::path>& OutPaths,
+ std::vector<IoHash>& OutRawHashes,
+ std::vector<uint64_t>& OutRawSizes,
+ std::vector<uint32_t>& OutAttributes,
+ std::vector<IoHash>& OutSequenceRawHashes,
+ std::vector<uint32_t>& OutChunkCounts,
+ std::vector<uint32_t>& OutAbsoluteChunkOrders,
+ std::vector<IoHash>& OutLooseChunkHashes,
+ std::vector<uint64_t>& OutLooseChunkRawSizes,
+ std::vector<IoHash>& OutBlockRawHashes);
+
+void CalculateLocalChunkOrders(const std::span<const uint32_t>& AbsoluteChunkOrders,
+ const std::span<const IoHash> LooseChunkHashes,
+ const std::span<const uint64_t> LooseChunkRawSizes,
+ const std::span<const ChunkBlockDescription>& BlockDescriptions,
+ std::vector<IoHash>& OutLocalChunkHashes,
+ std::vector<uint64_t>& OutLocalChunkRawSizes,
+ std::vector<uint32_t>& OutLocalChunkOrders);
+
+void ReadStateObject(CbObjectView StateView,
+ Oid& OutBuildId,
+ std::vector<Oid>& BuildPartsIds,
+ std::vector<std::string>& BuildPartsNames,
+ std::vector<ChunkedFolderContent>& OutPartContents,
+ FolderContent& OutLocalFolderState);
+
+void ReadStateFile(const std::filesystem::path& StateFilePath, FolderContent& OutLocalFolderState, ChunkedFolderContent& OutLocalContent);
+
+void AddDownloadedPath(const std::filesystem::path& SystemRootDir,
+ const Oid& BuildId,
+ const std::vector<std::pair<Oid, std::string>>& BuildParts,
+ const std::filesystem::path& StateFilePath,
+ const std::filesystem::path& LocalPath);
+
+std::vector<std::filesystem::path> GetDownloadedStatePaths(const std::filesystem::path& SystemRootDir);
+
+} // namespace zen
diff --git a/src/zenremotestore/include/zenremotestore/chunking/chunkedcontent.h b/src/zenremotestore/include/zenremotestore/chunking/chunkedcontent.h
index 306a5d990..2a56d14d3 100644
--- a/src/zenremotestore/include/zenremotestore/chunking/chunkedcontent.h
+++ b/src/zenremotestore/include/zenremotestore/chunking/chunkedcontent.h
@@ -121,6 +121,8 @@ ChunkedFolderContent DeletePathsFromChunkedContent(const ChunkedFolderContent&
std::span<const std::filesystem::path> DeletedPaths);
ChunkedFolderContent DeletePathsFromChunkedContent(const ChunkedFolderContent& Base, std::span<const std::filesystem::path> DeletedPaths);
+bool CompareChunkedContent(const ChunkedFolderContent& Lhs, const ChunkedFolderContent& Rhs);
+
struct ChunkingStatistics
{
std::atomic<uint64_t> FilesProcessed = 0;