aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDan Engelbrecht <[email protected]>2025-09-22 10:40:22 +0200
committerGitHub Enterprise <[email protected]>2025-09-22 10:40:22 +0200
commit8ec953bc17e4c13ff1811f4df26d722e5913e571 (patch)
tree68940b439a492db78e6e0f550f161b4735ee7a7f /src
parentfix quoted wildcard options (#500) (diff)
downloadzen-8ec953bc17e4c13ff1811f4df26d722e5913e571.tar.xz
zen-8ec953bc17e4c13ff1811f4df26d722e5913e571.zip
improve builds download partial logic (#501)
- Improvement: `zen command` help now uses available horizontal space for output - Improvement: Partial block request analisys has been improved with reduced download size at lower number of requests - Improvement: Validate that `--zen-cache-host` exists and is responsive before begin used - Feature: Options `--allow-partial-block-requests` for `zen builds download` command has been augmented with two new modes, `zencacheonly` and `mixed`. Defaults to `mixed`. - `false` only full block requests allowed - `mixed` multiple partial block ranges requests per block allowed to zen cache, single partial block range request per block to host - `zencacheonly` multiple partial block ranges requests per block allowed to zen cache, only full block requests allowed to host - `true` multiple partial block ranges requests per block allowed to zen cache and host
Diffstat (limited to 'src')
-rw-r--r--src/zen/cmds/builds_cmd.cpp877
-rw-r--r--src/zen/cmds/builds_cmd.h2
-rw-r--r--src/zen/zen.cpp168
3 files changed, 671 insertions, 376 deletions
diff --git a/src/zen/cmds/builds_cmd.cpp b/src/zen/cmds/builds_cmd.cpp
index 949e40085..1188f7cfd 100644
--- a/src/zen/cmds/builds_cmd.cpp
+++ b/src/zen/cmds/builds_cmd.cpp
@@ -1721,6 +1721,275 @@ namespace {
return Result;
}
+ struct BlockRangeDescriptor
+ {
+ uint32_t BlockIndex = (uint32_t)-1;
+ uint64_t RangeStart = 0;
+ uint64_t RangeLength = 0;
+ uint32_t ChunkBlockIndexStart = 0;
+ uint32_t ChunkBlockIndexCount = 0;
+ };
+
+ BlockRangeDescriptor MergeBlockRanges(std::span<const BlockRangeDescriptor> Ranges)
+ {
+ ZEN_ASSERT(Ranges.size() > 1);
+ const BlockRangeDescriptor& First = Ranges.front();
+ const BlockRangeDescriptor& Last = Ranges.back();
+
+ return BlockRangeDescriptor{
+ .BlockIndex = First.BlockIndex,
+ .RangeStart = First.RangeStart,
+ .RangeLength = Last.RangeStart + Last.RangeLength - First.RangeStart,
+ .ChunkBlockIndexStart = First.ChunkBlockIndexStart,
+ .ChunkBlockIndexCount = Last.ChunkBlockIndexStart + Last.ChunkBlockIndexCount - First.ChunkBlockIndexStart};
+ }
+
+ std::optional<std::vector<BlockRangeDescriptor>> MakeOptionalBlockRangeVector(uint64_t TotalBlockSize,
+ const BlockRangeDescriptor& Range)
+ {
+ if (Range.RangeLength == TotalBlockSize)
+ {
+ return {};
+ }
+ else
+ {
+ return std::vector<BlockRangeDescriptor>{Range};
+ }
+ };
+
+ struct BlockRangeLimit
+ {
+ uint16_t SizePercent;
+ uint16_t MaxRangeCount;
+ };
+
+ static const uint16_t FullBlockRangePercentLimit = 95;
+
+ static const std::vector<BlockRangeLimit> ForceMergeLimits = {{.SizePercent = FullBlockRangePercentLimit, .MaxRangeCount = 1},
+ {.SizePercent = 90, .MaxRangeCount = 2},
+ {.SizePercent = 85, .MaxRangeCount = 8},
+ {.SizePercent = 80, .MaxRangeCount = 16},
+ {.SizePercent = 70, .MaxRangeCount = 32},
+ {.SizePercent = 60, .MaxRangeCount = 48},
+ {.SizePercent = 2, .MaxRangeCount = 56},
+ {.SizePercent = 0, .MaxRangeCount = 64}};
+
+ const BlockRangeLimit* GetBlockRangeLimitForRange(std::span<const BlockRangeLimit> Limits,
+ uint64_t TotalBlockSize,
+ std::span<const BlockRangeDescriptor> Ranges)
+ {
+ if (Ranges.size() > 1)
+ {
+ const std::uint64_t WantedSize =
+ std::accumulate(Ranges.begin(), Ranges.end(), uint64_t(0), [](uint64_t Current, const BlockRangeDescriptor& Range) {
+ return Current + Range.RangeLength;
+ });
+
+ const double RangeRequestedPercent = (WantedSize * 100.0) / TotalBlockSize;
+
+ for (const BlockRangeLimit& Limit : Limits)
+ {
+ if (RangeRequestedPercent >= Limit.SizePercent && Ranges.size() > Limit.MaxRangeCount)
+ {
+ return &Limit;
+ }
+ }
+ }
+ return nullptr;
+ };
+
+ std::vector<BlockRangeDescriptor> CollapseBlockRanges(const uint64_t AlwaysAcceptableGap,
+ std::span<const BlockRangeDescriptor> BlockRanges)
+ {
+ ZEN_ASSERT(BlockRanges.size() > 1);
+ std::vector<BlockRangeDescriptor> CollapsedBlockRanges;
+
+ auto BlockRangesIt = BlockRanges.begin();
+ CollapsedBlockRanges.push_back(*BlockRangesIt++);
+ for (; BlockRangesIt != BlockRanges.end(); BlockRangesIt++)
+ {
+ BlockRangeDescriptor& LastRange = CollapsedBlockRanges.back();
+
+ const uint64_t BothRangeSize = BlockRangesIt->RangeLength + LastRange.RangeLength;
+
+ const uint64_t Gap = BlockRangesIt->RangeStart - (LastRange.RangeStart + LastRange.RangeLength);
+ if (Gap <= Max(BothRangeSize / 16, AlwaysAcceptableGap))
+ {
+ LastRange.ChunkBlockIndexCount =
+ (BlockRangesIt->ChunkBlockIndexStart + BlockRangesIt->ChunkBlockIndexCount) - LastRange.ChunkBlockIndexStart;
+ LastRange.RangeLength = (BlockRangesIt->RangeStart + BlockRangesIt->RangeLength) - LastRange.RangeStart;
+ }
+ else
+ {
+ CollapsedBlockRanges.push_back(*BlockRangesIt);
+ }
+ }
+
+ return CollapsedBlockRanges;
+ };
+
+ uint64_t CalculateNextGap(std::span<const BlockRangeDescriptor> BlockRanges)
+ {
+ ZEN_ASSERT(BlockRanges.size() > 1);
+ uint64_t AcceptableGap = (uint64_t)-1;
+ for (size_t RangeIndex = 0; RangeIndex < BlockRanges.size() - 1; RangeIndex++)
+ {
+ const BlockRangeDescriptor& Range = BlockRanges[RangeIndex];
+ const BlockRangeDescriptor& NextRange = BlockRanges[RangeIndex + 1];
+
+ const uint64_t Gap = NextRange.RangeStart - (Range.RangeStart + Range.RangeLength);
+ AcceptableGap = Min(Gap, AcceptableGap);
+ }
+ AcceptableGap = RoundUp(AcceptableGap, 16u * 1024u);
+ return AcceptableGap;
+ };
+
+ std::optional<std::vector<BlockRangeDescriptor>> CalculateBlockRanges(uint32_t BlockIndex,
+ const ChunkBlockDescription& BlockDescription,
+ std::span<const uint32_t> BlockChunkIndexNeeded,
+ bool LimitToSingleRange,
+ const uint64_t ChunkStartOffsetInBlock,
+ const uint64_t TotalBlockSize,
+ uint64_t& OutTotalWantedChunksSize)
+ {
+ ZEN_TRACE_CPU("UpdateFolder_HandleBlocks_PartialAnalysis");
+
+ std::vector<BlockRangeDescriptor> BlockRanges;
+ {
+ uint64_t CurrentOffset = ChunkStartOffsetInBlock;
+ uint32_t ChunkBlockIndex = 0;
+ uint32_t NeedBlockChunkIndexOffset = 0;
+ BlockRangeDescriptor NextRange{.BlockIndex = BlockIndex};
+ while (NeedBlockChunkIndexOffset < BlockChunkIndexNeeded.size() && ChunkBlockIndex < BlockDescription.ChunkRawHashes.size())
+ {
+ const uint32_t ChunkCompressedLength = BlockDescription.ChunkCompressedLengths[ChunkBlockIndex];
+ if (ChunkBlockIndex < BlockChunkIndexNeeded[NeedBlockChunkIndexOffset])
+ {
+ if (NextRange.RangeLength > 0)
+ {
+ BlockRanges.push_back(NextRange);
+ NextRange = {.BlockIndex = BlockIndex};
+ }
+ ChunkBlockIndex++;
+ CurrentOffset += ChunkCompressedLength;
+ }
+ else if (ChunkBlockIndex == BlockChunkIndexNeeded[NeedBlockChunkIndexOffset])
+ {
+ if (NextRange.RangeLength == 0)
+ {
+ NextRange.RangeStart = CurrentOffset;
+ NextRange.ChunkBlockIndexStart = ChunkBlockIndex;
+ }
+ NextRange.RangeLength += ChunkCompressedLength;
+ NextRange.ChunkBlockIndexCount++;
+ ChunkBlockIndex++;
+ CurrentOffset += ChunkCompressedLength;
+ NeedBlockChunkIndexOffset++;
+ }
+ else
+ {
+ ZEN_ASSERT(false);
+ }
+ }
+ if (NextRange.RangeLength > 0)
+ {
+ BlockRanges.push_back(NextRange);
+ }
+ }
+ ZEN_ASSERT(!BlockRanges.empty());
+
+ OutTotalWantedChunksSize =
+ std::accumulate(BlockRanges.begin(), BlockRanges.end(), uint64_t(0), [](uint64_t Current, const BlockRangeDescriptor& Range) {
+ return Current + Range.RangeLength;
+ });
+
+ double RangeWantedPercent = (OutTotalWantedChunksSize * 100.0) / TotalBlockSize;
+
+ if (BlockRanges.size() == 1)
+ {
+ ZEN_CONSOLE_VERBOSE("Range request of {} ({:.2f}%) using single range from block {} ({}) as is",
+ NiceBytes(OutTotalWantedChunksSize),
+ RangeWantedPercent,
+ BlockDescription.BlockHash,
+ NiceBytes(TotalBlockSize));
+ return BlockRanges;
+ }
+
+ if (LimitToSingleRange)
+ {
+ const BlockRangeDescriptor MergedRange = MergeBlockRanges(BlockRanges);
+ const double RangeRequestedPercent = (MergedRange.RangeLength * 100.0) / TotalBlockSize;
+ const double WastedPercent = ((MergedRange.RangeLength - OutTotalWantedChunksSize) * 100.0) / MergedRange.RangeLength;
+ ZEN_CONSOLE_VERBOSE(
+ "Range request of {} ({:.2f}%) using {} ranges from block {} ({}) limited to single block range {} ({:.2f}%) wasting "
+ "{:.2f}% ({})",
+ NiceBytes(OutTotalWantedChunksSize),
+ RangeWantedPercent,
+ BlockRanges.size(),
+ BlockDescription.BlockHash,
+ NiceBytes(TotalBlockSize),
+ NiceBytes(MergedRange.RangeLength),
+ RangeRequestedPercent,
+ WastedPercent,
+ NiceBytes(MergedRange.RangeLength - OutTotalWantedChunksSize));
+ return MakeOptionalBlockRangeVector(TotalBlockSize, MergedRange);
+ }
+
+ if (RangeWantedPercent > FullBlockRangePercentLimit)
+ {
+ const BlockRangeDescriptor MergedRange = MergeBlockRanges(BlockRanges);
+ const double RangeRequestedPercent = (MergedRange.RangeLength * 100.0) / TotalBlockSize;
+ const double WastedPercent = ((MergedRange.RangeLength - OutTotalWantedChunksSize) * 100.0) / MergedRange.RangeLength;
+ ZEN_CONSOLE_VERBOSE(
+ "Range request of {} ({:.2f}%) using {} ranges from block {} ({}) exceeds {}%. Merged to single block range {} "
+ "({:.2f}%) wasting {:.2f}% ({})",
+ NiceBytes(OutTotalWantedChunksSize),
+ RangeWantedPercent,
+ BlockRanges.size(),
+ BlockDescription.BlockHash,
+ NiceBytes(TotalBlockSize),
+ FullBlockRangePercentLimit,
+ NiceBytes(MergedRange.RangeLength),
+ RangeRequestedPercent,
+ WastedPercent,
+ NiceBytes(MergedRange.RangeLength - OutTotalWantedChunksSize));
+ return MakeOptionalBlockRangeVector(TotalBlockSize, MergedRange);
+ }
+
+ std::vector<BlockRangeDescriptor> CollapsedBlockRanges = CollapseBlockRanges(16u * 1024u, BlockRanges);
+ while (GetBlockRangeLimitForRange(ForceMergeLimits, TotalBlockSize, CollapsedBlockRanges))
+ {
+ CollapsedBlockRanges = CollapseBlockRanges(CalculateNextGap(CollapsedBlockRanges), CollapsedBlockRanges);
+ }
+
+ const std::uint64_t WantedCollapsedSize =
+ std::accumulate(CollapsedBlockRanges.begin(),
+ CollapsedBlockRanges.end(),
+ uint64_t(0),
+ [](uint64_t Current, const BlockRangeDescriptor& Range) { return Current + Range.RangeLength; });
+
+ const double CollapsedRangeRequestedPercent = (WantedCollapsedSize * 100.0) / TotalBlockSize;
+
+ {
+ const double WastedPercent = ((WantedCollapsedSize - OutTotalWantedChunksSize) * 100.0) / WantedCollapsedSize;
+
+ ZEN_CONSOLE_VERBOSE(
+ "Range request of {} ({:.2f}%) using {} ranges from block {} ({}) collapsed to {} {:.2f}% using {} ranges wasting {:.2f}% "
+ "({})",
+ NiceBytes(OutTotalWantedChunksSize),
+ RangeWantedPercent,
+ BlockRanges.size(),
+ BlockDescription.BlockHash,
+ NiceBytes(TotalBlockSize),
+ NiceBytes(WantedCollapsedSize),
+ CollapsedRangeRequestedPercent,
+ CollapsedBlockRanges.size(),
+ WastedPercent,
+ NiceBytes(WantedCollapsedSize - OutTotalWantedChunksSize));
+ return CollapsedBlockRanges;
+ }
+ };
+
class BufferedOpenFile
{
public:
@@ -5758,17 +6027,42 @@ namespace {
return Result;
}
+ enum EPartialBlockRequestMode
+ {
+ Off,
+ ZenCacheOnly,
+ Mixed,
+ All,
+ Invalid
+ };
+ static EPartialBlockRequestMode PartialBlockRequestModeFromString(const std::string_view ModeString)
+ {
+ switch (HashStringAsLowerDjb2(ModeString))
+ {
+ case HashStringDjb2("false"):
+ return EPartialBlockRequestMode::Off;
+ case HashStringDjb2("zencacheonly"):
+ return EPartialBlockRequestMode::ZenCacheOnly;
+ case HashStringDjb2("mixed"):
+ return EPartialBlockRequestMode::Mixed;
+ case HashStringDjb2("true"):
+ return EPartialBlockRequestMode::All;
+ default:
+ return EPartialBlockRequestMode::Invalid;
+ }
+ }
+
struct UpdateOptions
{
- std::filesystem::path SystemRootDir;
- std::filesystem::path ZenFolderPath;
- std::uint64_t LargeAttachmentSize = DefaultPreferredMultipartChunkSize * 4u;
- std::uint64_t PreferredMultipartChunkSize = DefaultPreferredMultipartChunkSize;
- bool AllowPartialBlockRequests = true;
- bool WipeTargetFolder = false;
- bool PrimeCacheOnly = false;
- bool EnableOtherDownloadsScavenging = true;
- bool EnableTargetFolderScavenging = true;
+ std::filesystem::path SystemRootDir;
+ std::filesystem::path ZenFolderPath;
+ std::uint64_t LargeAttachmentSize = DefaultPreferredMultipartChunkSize * 4u;
+ std::uint64_t PreferredMultipartChunkSize = DefaultPreferredMultipartChunkSize;
+ EPartialBlockRequestMode PartialBlockRequestMode = EPartialBlockRequestMode::Mixed;
+ bool WipeTargetFolder = false;
+ bool PrimeCacheOnly = false;
+ bool EnableOtherDownloadsScavenging = true;
+ bool EnableTargetFolderScavenging = true;
};
void UpdateFolder(StorageInstance& Storage,
@@ -5788,7 +6082,8 @@ namespace {
{
ZEN_TRACE_CPU("UpdateFolder");
- ZEN_ASSERT((!Options.PrimeCacheOnly) || (Options.PrimeCacheOnly && (!Options.AllowPartialBlockRequests)));
+ ZEN_ASSERT((!Options.PrimeCacheOnly) ||
+ (Options.PrimeCacheOnly && (Options.PartialBlockRequestMode == EPartialBlockRequestMode::Off)));
Stopwatch IndexTimer;
@@ -6558,33 +6853,20 @@ namespace {
return NeededBlockChunkIndexes;
};
- std::vector<uint32_t> CachedChunkBlockIndexes;
-
- struct BlockRangeDescriptor
- {
- uint32_t BlockIndex = (uint32_t)-1;
- uint64_t RangeStart = 0;
- uint64_t RangeLength = 0;
- uint32_t ChunkBlockIndexStart = 0;
- uint32_t ChunkBlockIndexCount = 0;
- };
- std::vector<BlockRangeDescriptor> BlockRangeWorks;
-
- std::vector<uint32_t> FullBlockWorks;
+ std::vector<uint32_t> CachedChunkBlockIndexes;
+ std::vector<uint32_t> FetchBlockIndexes;
+ std::vector<std::vector<uint32_t>> AllBlockChunkIndexNeeded;
for (uint32_t BlockIndex = 0; BlockIndex < BlockCount; BlockIndex++)
{
const ChunkBlockDescription& BlockDescription = BlockDescriptions[BlockIndex];
- const std::vector<uint32_t> BlockChunkIndexNeeded = GetNeededChunkBlockIndexes(BlockDescription);
+ std::vector<uint32_t> BlockChunkIndexNeeded = GetNeededChunkBlockIndexes(BlockDescription);
if (!BlockChunkIndexNeeded.empty())
{
if (Options.PrimeCacheOnly)
{
- TotalRequestCount++;
- TotalPartWriteCount++;
-
- FullBlockWorks.push_back(BlockIndex);
+ FetchBlockIndexes.push_back(BlockIndex);
}
else
{
@@ -6603,188 +6885,13 @@ namespace {
UsingCachedBlock = true;
}
}
-
if (!UsingCachedBlock)
{
- bool WantsToDoPartialBlockDownload = BlockChunkIndexNeeded.size() < BlockDescription.ChunkRawHashes.size();
- bool CanDoPartialBlockDownload =
- (BlockDescription.HeaderSize > 0) &&
- (BlockDescription.ChunkCompressedLengths.size() == BlockDescription.ChunkRawHashes.size());
- if (Options.AllowPartialBlockRequests && WantsToDoPartialBlockDownload && CanDoPartialBlockDownload)
- {
- std::vector<BlockRangeDescriptor> BlockRanges;
-
- ZEN_TRACE_CPU("UpdateFolder_HandleBlocks_PartialAnalysis");
-
- uint32_t NeedBlockChunkIndexOffset = 0;
- uint32_t ChunkBlockIndex = 0;
- uint32_t CurrentOffset =
- gsl::narrow<uint32_t>(CompressedBuffer::GetHeaderSizeForNoneEncoder() + BlockDescription.HeaderSize);
-
- const uint64_t TotalBlockSize = std::accumulate(BlockDescription.ChunkCompressedLengths.begin(),
- BlockDescription.ChunkCompressedLengths.end(),
- std::uint64_t(CurrentOffset));
- BlockRangeDescriptor NextRange{.BlockIndex = BlockIndex};
- while (NeedBlockChunkIndexOffset < BlockChunkIndexNeeded.size() &&
- ChunkBlockIndex < BlockDescription.ChunkRawHashes.size())
- {
- const uint32_t ChunkCompressedLength = BlockDescription.ChunkCompressedLengths[ChunkBlockIndex];
- if (ChunkBlockIndex < BlockChunkIndexNeeded[NeedBlockChunkIndexOffset])
- {
- if (NextRange.RangeLength > 0)
- {
- BlockRanges.push_back(NextRange);
- NextRange = {.BlockIndex = BlockIndex};
- }
- ChunkBlockIndex++;
- CurrentOffset += ChunkCompressedLength;
- }
- else if (ChunkBlockIndex == BlockChunkIndexNeeded[NeedBlockChunkIndexOffset])
- {
- if (NextRange.RangeLength == 0)
- {
- NextRange.RangeStart = CurrentOffset;
- NextRange.ChunkBlockIndexStart = ChunkBlockIndex;
- }
- NextRange.RangeLength += ChunkCompressedLength;
- NextRange.ChunkBlockIndexCount++;
- ChunkBlockIndex++;
- CurrentOffset += ChunkCompressedLength;
- NeedBlockChunkIndexOffset++;
- }
- else
- {
- ZEN_ASSERT(false);
- }
- }
- if (NextRange.RangeLength > 0)
- {
- BlockRanges.push_back(NextRange);
- }
-
- ZEN_ASSERT(!BlockRanges.empty());
-
- std::vector<BlockRangeDescriptor> CollapsedBlockRanges;
- auto It = BlockRanges.begin();
- CollapsedBlockRanges.push_back(*It++);
- while (It != BlockRanges.end())
- {
- BlockRangeDescriptor& LastRange = CollapsedBlockRanges.back();
- uint64_t Slack = It->RangeStart - (LastRange.RangeStart + LastRange.RangeLength);
- uint64_t BothRangeSize = It->RangeLength + LastRange.RangeLength;
- if (Slack <= Max(BothRangeSize / 8, 64u * 1024u)) // Made up heuristic - we'll see how it pans out
- {
- LastRange.ChunkBlockIndexCount =
- (It->ChunkBlockIndexStart + It->ChunkBlockIndexCount) - LastRange.ChunkBlockIndexStart;
- LastRange.RangeLength = (It->RangeStart + It->RangeLength) - LastRange.RangeStart;
- }
- else
- {
- CollapsedBlockRanges.push_back(*It);
- }
- ++It;
- }
-
- const std::uint64_t WantedSize = std::accumulate(
- CollapsedBlockRanges.begin(),
- CollapsedBlockRanges.end(),
- uint64_t(0),
- [](uint64_t Current, const BlockRangeDescriptor& Range) { return Current + Range.RangeLength; });
- ZEN_ASSERT(WantedSize <= TotalBlockSize);
- if (WantedSize > ((TotalBlockSize * 95) / 100))
- {
- ZEN_CONSOLE_VERBOSE("Using more than 95% ({}) of block {} ({}), requesting full block",
- NiceBytes(WantedSize),
- BlockDescription.BlockHash,
- NiceBytes(TotalBlockSize));
- TotalRequestCount++;
- TotalPartWriteCount++;
-
- FullBlockWorks.push_back(BlockIndex);
- }
- else if ((WantedSize > ((TotalBlockSize * 9) / 10)) && CollapsedBlockRanges.size() > 1)
- {
- ZEN_CONSOLE_VERBOSE(
- "Using more than 90% ({}) of block {} ({}) using {} requests, requesting full block",
- NiceBytes(WantedSize),
- BlockDescription.BlockHash,
- NiceBytes(TotalBlockSize),
- CollapsedBlockRanges.size());
- TotalRequestCount++;
- TotalPartWriteCount++;
-
- FullBlockWorks.push_back(BlockIndex);
- }
- else if ((WantedSize > ((TotalBlockSize * 8) / 10)) && (CollapsedBlockRanges.size() > 16))
- {
- ZEN_CONSOLE_VERBOSE(
- "Using more than 80% ({}) of block {} ({}) using {} requests, requesting full block",
- NiceBytes(WantedSize),
- BlockDescription.BlockHash,
- NiceBytes(TotalBlockSize),
- CollapsedBlockRanges.size());
- TotalRequestCount++;
- TotalPartWriteCount++;
-
- FullBlockWorks.push_back(BlockIndex);
- }
- else if ((WantedSize > ((TotalBlockSize * 7) / 10)) && (CollapsedBlockRanges.size() > 48))
- {
- ZEN_CONSOLE_VERBOSE(
- "Using more than 70% ({}) of block {} ({}) using {} requests, requesting full block",
- NiceBytes(WantedSize),
- BlockDescription.BlockHash,
- NiceBytes(TotalBlockSize),
- CollapsedBlockRanges.size());
- TotalRequestCount++;
- TotalPartWriteCount++;
-
- FullBlockWorks.push_back(BlockIndex);
- }
- else if ((WantedSize > ((TotalBlockSize * 6) / 10)) && (CollapsedBlockRanges.size() > 64))
- {
- ZEN_CONSOLE_VERBOSE(
- "Using more than 60% ({}) of block {} ({}) using {} requests, requesting full block",
- NiceBytes(WantedSize),
- BlockDescription.BlockHash,
- NiceBytes(TotalBlockSize),
- CollapsedBlockRanges.size());
- TotalRequestCount++;
- TotalPartWriteCount++;
-
- FullBlockWorks.push_back(BlockIndex);
- }
- else
- {
- if (WantedSize > ((TotalBlockSize * 5) / 10))
- {
- ZEN_CONSOLE_VERBOSE("Using {}% ({}) of block {} ({}) using {} requests, requesting partial block",
- (WantedSize * 100) / TotalBlockSize,
- NiceBytes(WantedSize),
- BlockDescription.BlockHash,
- NiceBytes(TotalBlockSize),
- CollapsedBlockRanges.size());
- }
- TotalRequestCount += CollapsedBlockRanges.size();
- TotalPartWriteCount += CollapsedBlockRanges.size();
-
- BlockRangeWorks.insert(BlockRangeWorks.end(), CollapsedBlockRanges.begin(), CollapsedBlockRanges.end());
- }
- }
- else
- {
- TotalRequestCount++;
- TotalPartWriteCount++;
-
- FullBlockWorks.push_back(BlockIndex);
- }
+ FetchBlockIndexes.push_back(BlockIndex);
}
}
}
- else
- {
- ZEN_CONSOLE_VERBOSE("Skipping block {} due to cache reuse", BlockDescriptions[BlockIndex].BlockHash);
- }
+ AllBlockChunkIndexNeeded.emplace_back(std::move(BlockChunkIndexNeeded));
}
struct BlobsExistsResult
@@ -6802,18 +6909,12 @@ namespace {
tsl::robin_set<IoHash> BlobHashesSet;
- BlobHashesSet.reserve(LooseChunkHashWorks.size() + FullBlockWorks.size());
+ BlobHashesSet.reserve(LooseChunkHashWorks.size() + FetchBlockIndexes.size());
for (LooseChunkHashWorkData& LooseChunkHashWork : LooseChunkHashWorks)
{
BlobHashesSet.insert(RemoteContent.ChunkedContent.ChunkHashes[LooseChunkHashWork.RemoteChunkIndex]);
}
- for (const BlockRangeDescriptor& BlockRange : BlockRangeWorks)
- {
- const uint32_t BlockIndex = BlockRange.BlockIndex;
- const ChunkBlockDescription& BlockDescription = BlockDescriptions[BlockIndex];
- BlobHashesSet.insert(BlockDescription.BlockHash);
- }
- for (uint32_t BlockIndex : FullBlockWorks)
+ for (uint32_t BlockIndex : FetchBlockIndexes)
{
const ChunkBlockDescription& BlockDescription = BlockDescriptions[BlockIndex];
BlobHashesSet.insert(BlockDescription.BlockHash);
@@ -6847,6 +6948,162 @@ namespace {
}
}
+ std::vector<BlockRangeDescriptor> BlockRangeWorks;
+ std::vector<uint32_t> FullBlockWorks;
+ {
+ Stopwatch Timer;
+
+ std::vector<uint32_t> PartialBlockIndexes;
+
+ for (uint32_t BlockIndex : FetchBlockIndexes)
+ {
+ const ChunkBlockDescription& BlockDescription = BlockDescriptions[BlockIndex];
+
+ const std::vector<uint32_t> BlockChunkIndexNeeded = std::move(AllBlockChunkIndexNeeded[BlockIndex]);
+ if (!BlockChunkIndexNeeded.empty())
+ {
+ bool WantsToDoPartialBlockDownload = BlockChunkIndexNeeded.size() < BlockDescription.ChunkRawHashes.size();
+ bool CanDoPartialBlockDownload =
+ (BlockDescription.HeaderSize > 0) &&
+ (BlockDescription.ChunkCompressedLengths.size() == BlockDescription.ChunkRawHashes.size());
+
+ bool AllowedToDoPartialRequest = false;
+ bool BlockExistInCache = ExistsResult.ExistingBlobs.contains(BlockDescription.BlockHash);
+ switch (Options.PartialBlockRequestMode)
+ {
+ case EPartialBlockRequestMode::Off:
+ break;
+ case EPartialBlockRequestMode::ZenCacheOnly:
+ AllowedToDoPartialRequest = BlockExistInCache;
+ break;
+ case EPartialBlockRequestMode::Mixed:
+ case EPartialBlockRequestMode::All:
+ AllowedToDoPartialRequest = true;
+ break;
+ default:
+ ZEN_ASSERT(false);
+ break;
+ }
+
+ const uint32_t ChunkStartOffsetInBlock =
+ gsl::narrow<uint32_t>(CompressedBuffer::GetHeaderSizeForNoneEncoder() + BlockDescription.HeaderSize);
+
+ const uint64_t TotalBlockSize = std::accumulate(BlockDescription.ChunkCompressedLengths.begin(),
+ BlockDescription.ChunkCompressedLengths.end(),
+ std::uint64_t(ChunkStartOffsetInBlock));
+
+ if (AllowedToDoPartialRequest && WantsToDoPartialBlockDownload && CanDoPartialBlockDownload)
+ {
+ ZEN_TRACE_CPU("UpdateFolder_HandleBlocks_PartialAnalysis");
+
+ bool LimitToSingleRange =
+ BlockExistInCache ? false : Options.PartialBlockRequestMode == EPartialBlockRequestMode::Mixed;
+ uint64_t TotalWantedChunksSize = 0;
+ std::optional<std::vector<BlockRangeDescriptor>> MaybeBlockRanges =
+ CalculateBlockRanges(BlockIndex,
+ BlockDescription,
+ BlockChunkIndexNeeded,
+ LimitToSingleRange,
+ ChunkStartOffsetInBlock,
+ TotalBlockSize,
+ TotalWantedChunksSize);
+ ZEN_ASSERT(TotalWantedChunksSize <= TotalBlockSize);
+
+ if (MaybeBlockRanges.has_value())
+ {
+ const std::vector<BlockRangeDescriptor>& BlockRanges = MaybeBlockRanges.value();
+ ZEN_ASSERT(!BlockRanges.empty());
+ BlockRangeWorks.insert(BlockRangeWorks.end(), BlockRanges.begin(), BlockRanges.end());
+ TotalRequestCount += BlockRanges.size();
+ TotalPartWriteCount += BlockRanges.size();
+
+ uint64_t RequestedSize = std::accumulate(
+ BlockRanges.begin(),
+ BlockRanges.end(),
+ uint64_t(0),
+ [](uint64_t Current, const BlockRangeDescriptor& Range) { return Current + Range.RangeLength; });
+ PartialBlockIndexes.push_back(BlockIndex);
+
+ if (RequestedSize > TotalWantedChunksSize)
+ {
+ ZEN_CONSOLE_VERBOSE("Requesting {} chunks ({}) from block {} ({}) using {} requests (extra bytes {})",
+ BlockChunkIndexNeeded.size(),
+ NiceBytes(RequestedSize),
+ BlockDescription.BlockHash,
+ NiceBytes(TotalBlockSize),
+ BlockRanges.size(),
+ NiceBytes(RequestedSize - TotalWantedChunksSize));
+ }
+ }
+ else
+ {
+ FullBlockWorks.push_back(BlockIndex);
+ TotalRequestCount++;
+ TotalPartWriteCount++;
+ }
+ }
+ else
+ {
+ FullBlockWorks.push_back(BlockIndex);
+ TotalRequestCount++;
+ TotalPartWriteCount++;
+ }
+ }
+ }
+
+ if (!PartialBlockIndexes.empty())
+ {
+ uint64_t TotalFullBlockRequestBytes = 0;
+ for (uint32_t BlockIndex : FullBlockWorks)
+ {
+ const ChunkBlockDescription& BlockDescription = BlockDescriptions[BlockIndex];
+ uint32_t CurrentOffset =
+ gsl::narrow<uint32_t>(CompressedBuffer::GetHeaderSizeForNoneEncoder() + BlockDescription.HeaderSize);
+
+ TotalFullBlockRequestBytes += std::accumulate(BlockDescription.ChunkCompressedLengths.begin(),
+ BlockDescription.ChunkCompressedLengths.end(),
+ std::uint64_t(CurrentOffset));
+ }
+
+ uint64_t TotalPartialBlockBytes = 0;
+ for (uint32_t BlockIndex : PartialBlockIndexes)
+ {
+ const ChunkBlockDescription& BlockDescription = BlockDescriptions[BlockIndex];
+ uint32_t CurrentOffset =
+ gsl::narrow<uint32_t>(CompressedBuffer::GetHeaderSizeForNoneEncoder() + BlockDescription.HeaderSize);
+
+ TotalPartialBlockBytes += std::accumulate(BlockDescription.ChunkCompressedLengths.begin(),
+ BlockDescription.ChunkCompressedLengths.end(),
+ std::uint64_t(CurrentOffset));
+ }
+
+ uint64_t NonPartialTotalBlockBytes = TotalFullBlockRequestBytes + TotalPartialBlockBytes;
+
+ const uint64_t TotalPartialBlockRequestBytes =
+ std::accumulate(BlockRangeWorks.begin(),
+ BlockRangeWorks.end(),
+ uint64_t(0),
+ [](uint64_t Current, const BlockRangeDescriptor& Range) { return Current + Range.RangeLength; });
+ uint64_t TotalExtraPartialBlocksRequests = BlockRangeWorks.size() - PartialBlockIndexes.size();
+
+ uint64_t TotalSavedBlocksSize = TotalPartialBlockBytes - TotalPartialBlockRequestBytes;
+ double SavedSizePercent = (TotalSavedBlocksSize * 100.0) / NonPartialTotalBlockBytes;
+
+ if (!IsQuiet)
+ {
+ ZEN_CONSOLE(
+ "Analisys of partial block requests saves download of {} out of {} ({:.1f}%) using {} extra "
+ "requests. Completed in {}",
+ NiceBytes(TotalSavedBlocksSize),
+ NiceBytes(NonPartialTotalBlockBytes),
+ SavedSizePercent,
+ TotalExtraPartialBlocksRequests,
+ NiceTimeSpanMs(ExistsResult.ElapsedTimeMs));
+ }
+ // exit(0);
+ }
+ }
+
BufferedWriteFileCache WriteCache;
for (uint32_t ScavengeOpIndex = 0; ScavengeOpIndex < ScavengeCopyOperations.size(); ScavengeOpIndex++)
@@ -7204,7 +7461,7 @@ namespace {
Storage.BuildCacheStorage->PutBuildBlob(
BuildId,
ChunkHash,
- BuildBlob.GetContentType(),
+ ZenContentType::kCompressedBinary,
CompositeBuffer(SharedBuffer(BuildBlob)));
}
if (!BuildBlob)
@@ -7792,7 +8049,7 @@ namespace {
{
Storage.BuildCacheStorage->PutBuildBlob(BuildId,
BlockDescription.BlockHash,
- BlockBuffer.GetContentType(),
+ ZenContentType::kCompressedBinary,
CompositeBuffer(SharedBuffer(BlockBuffer)));
}
}
@@ -9396,17 +9653,17 @@ namespace {
struct DownloadOptions
{
- std::filesystem::path SystemRootDir;
- std::filesystem::path ZenFolderPath;
- bool AllowMultiparts = true;
- bool AllowPartialBlockRequests = true;
- bool CleanTargetFolder = false;
- bool PostDownloadVerify = false;
- bool PrimeCacheOnly = false;
- bool EnableOtherDownloadsScavenging = true;
- bool EnableTargetFolderScavenging = true;
- std::string IncludeWildcard;
- std::string ExcludeWildcard;
+ std::filesystem::path SystemRootDir;
+ std::filesystem::path ZenFolderPath;
+ bool AllowMultiparts = true;
+ EPartialBlockRequestMode PartialBlockRequestMode = EPartialBlockRequestMode::Mixed;
+ bool CleanTargetFolder = false;
+ bool PostDownloadVerify = false;
+ bool PrimeCacheOnly = false;
+ bool EnableOtherDownloadsScavenging = true;
+ bool EnableTargetFolderScavenging = true;
+ std::string IncludeWildcard;
+ std::string ExcludeWildcard;
};
void DownloadFolder(StorageInstance& Storage,
@@ -9433,7 +9690,8 @@ namespace {
auto EndProgress =
MakeGuard([&]() { ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::StepCount, TaskSteps::StepCount); });
- ZEN_ASSERT((!Options.PrimeCacheOnly) || (Options.PrimeCacheOnly && (!Options.AllowPartialBlockRequests)));
+ ZEN_ASSERT((!Options.PrimeCacheOnly) ||
+ (Options.PrimeCacheOnly && (Options.PartialBlockRequestMode == EPartialBlockRequestMode::Off)));
Stopwatch DownloadTimer;
@@ -9601,7 +9859,7 @@ namespace {
.ZenFolderPath = Options.ZenFolderPath,
.LargeAttachmentSize = LargeAttachmentSize,
.PreferredMultipartChunkSize = PreferredMultipartChunkSize,
- .AllowPartialBlockRequests = Options.AllowPartialBlockRequests,
+ .PartialBlockRequestMode = Options.PartialBlockRequestMode,
.WipeTargetFolder = Options.CleanTargetFolder,
.PrimeCacheOnly = Options.PrimeCacheOnly,
.EnableOtherDownloadsScavenging = Options.EnableOtherDownloadsScavenging,
@@ -10154,6 +10412,31 @@ BuildsCommand::BuildsCommand()
"<excludewildcard>");
};
+ auto AddMultipartOptions = [this](cxxopts::Options& Ops) {
+ Ops.add_option("",
+ "",
+ "allow-multipart",
+ "Allow large attachments to be transfered using multipart protocol. Defaults to true.",
+ cxxopts::value(m_AllowMultiparts),
+ "<allowmultipart>");
+ };
+
+ auto AddPartialBlockRequestOptions = [this](cxxopts::Options& Ops) {
+ Ops.add_option("",
+ "",
+ "allow-partial-block-requests",
+ "Allow request for partial chunk blocks.\n"
+ " false = only full block requests allowed\n"
+ " mixed = multiple partial block ranges requests per block allowed to zen cache, single partial block range "
+ "request per block to host\n"
+ " zencacheonly = multiple partial block ranges requests per block allowed to zen cache, only full block requests "
+ "allowed to host\n"
+ " true = multiple partial block ranges requests per block allowed to zen cache and host\n"
+ "Defaults to 'mixed'.",
+ cxxopts::value(m_AllowPartialBlockRequests),
+ "<allowpartialblockrequests>");
+ };
+
m_Options.add_option("",
"v",
"verb",
@@ -10256,12 +10539,9 @@ BuildsCommand::BuildsCommand()
"Percent of an existing block that must be relevant for it to be resused. Defaults to 85.",
cxxopts::value(m_BlockReuseMinPercentLimit),
"<minreuse>");
- m_UploadOptions.add_option("",
- "",
- "allow-multipart",
- "Allow large attachments to be transfered using multipart protocol. Defaults to true.",
- cxxopts::value(m_AllowMultiparts),
- "<allowmultipart>");
+
+ AddMultipartOptions(m_UploadOptions);
+
m_UploadOptions.add_option("",
"",
"manifest-path",
@@ -10326,18 +10606,9 @@ BuildsCommand::BuildsCommand()
"Force download of all content by ignoring any existing local content",
cxxopts::value(m_Force),
"<force>");
- m_DownloadOptions.add_option("",
- "",
- "allow-multipart",
- "Allow large attachments to be transfered using multipart protocol. Defaults to true.",
- cxxopts::value(m_AllowMultiparts),
- "<allowmultipart>");
- m_DownloadOptions.add_option("",
- "",
- "allow-partial-block-requests",
- "Allow request for partial chunk blocks. Defaults to true.",
- cxxopts::value(m_AllowPartialBlockRequests),
- "<allowpartialblockrequests>");
+ AddMultipartOptions(m_DownloadOptions);
+
+ AddPartialBlockRequestOptions(m_DownloadOptions);
m_DownloadOptions
.add_option("", "", "verify", "Enable post download verify of all tracked files", cxxopts::value(m_PostDownloadVerify), "<verify>");
@@ -10404,18 +10675,8 @@ BuildsCommand::BuildsCommand()
AddWorkerOptions(m_TestOptions);
m_TestOptions.add_options()("h,help", "Print help");
m_TestOptions.add_option("", "l", "local-path", "Root file system folder used as base", cxxopts::value(m_Path), "<local-path>");
- m_TestOptions.add_option("",
- "",
- "allow-multipart",
- "Allow large attachments to be transfered using multipart protocol. Defaults to true.",
- cxxopts::value(m_AllowMultiparts),
- "<allowmultipart>");
- m_TestOptions.add_option("",
- "",
- "allow-partial-block-requests",
- "Allow request for partial chunk blocks. Defaults to true.",
- cxxopts::value(m_AllowPartialBlockRequests),
- "<allowpartialblockrequests>");
+ AddMultipartOptions(m_TestOptions);
+ AddPartialBlockRequestOptions(m_TestOptions);
m_TestOptions.add_option("",
"",
"enable-scavenge",
@@ -10929,8 +11190,13 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
else
{
ZEN_CONSOLE_WARN("Unable to reach cache host {}. Reason: {}", m_ZenCacheHost, TestResult.second);
+ m_ZenCacheHost = "";
}
}
+ else if (!m_OverrideHost.empty())
+ {
+ CloudHost = m_OverrideHost;
+ }
}
else
{
@@ -10967,33 +11233,40 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
}
if (!m_ZenCacheHost.empty())
{
- Result.CacheHttp = std::make_unique<HttpClient>(m_ZenCacheHost,
- HttpClientSettings{.LogCategory = "httpcacheclient",
- .ConnectTimeout = std::chrono::milliseconds{3000},
- .Timeout = std::chrono::milliseconds{30000},
- .AssumeHttp2 = CacheAssumeHttp2,
- .AllowResume = true,
- .RetryCount = 0});
- Result.BuildCacheStorage = CreateZenBuildStorageCache(*Result.CacheHttp,
- StorageCacheStats,
- m_Namespace,
- m_Bucket,
- TempPath / "zencache",
- m_PrimeCacheOnly);
- CacheDescription = fmt::format("Zen {}{}. SessionId: '{}'",
- BuildCacheName.empty() ? "" : fmt::format("{}, ", BuildCacheName),
- m_ZenCacheHost,
- Result.CacheHttp->GetSessionId());
-
- if (!m_Namespace.empty())
- {
- CacheDescription += fmt::format(". Namespace '{}'", m_Namespace);
+ if (auto TestResult = TestCacheEndpoint(m_ZenCacheHost, false); TestResult.first)
+ {
+ Result.CacheHttp = std::make_unique<HttpClient>(m_ZenCacheHost,
+ HttpClientSettings{.LogCategory = "httpcacheclient",
+ .ConnectTimeout = std::chrono::milliseconds{3000},
+ .Timeout = std::chrono::milliseconds{30000},
+ .AssumeHttp2 = CacheAssumeHttp2,
+ .AllowResume = true,
+ .RetryCount = 0});
+ Result.BuildCacheStorage = CreateZenBuildStorageCache(*Result.CacheHttp,
+ StorageCacheStats,
+ m_Namespace,
+ m_Bucket,
+ TempPath / "zencache",
+ m_PrimeCacheOnly);
+ CacheDescription = fmt::format("Zen {}{}. SessionId: '{}'",
+ BuildCacheName.empty() ? "" : fmt::format("{}, ", BuildCacheName),
+ m_ZenCacheHost,
+ Result.CacheHttp->GetSessionId());
+
+ if (!m_Namespace.empty())
+ {
+ CacheDescription += fmt::format(". Namespace '{}'", m_Namespace);
+ }
+ if (!m_Bucket.empty())
+ {
+ CacheDescription += fmt::format(" Bucket '{}'", m_Bucket);
+ }
+ Result.CacheName = BuildCacheName.empty() ? m_ZenCacheHost : BuildCacheName;
}
- if (!m_Bucket.empty())
+ else
{
- CacheDescription += fmt::format(" Bucket '{}'", m_Bucket);
+ ZEN_CONSOLE_WARN("Unable to reach cache host {}. Reason: {}", m_ZenCacheHost, TestResult.second);
}
- Result.CacheName = BuildCacheName.empty() ? m_ZenCacheHost : BuildCacheName;
}
if (!IsQuiet)
{
@@ -11433,6 +11706,20 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
}
}
+ auto ParseAllowPartialBlockRequests = [&]() -> EPartialBlockRequestMode {
+ if (m_PrimeCacheOnly)
+ {
+ return EPartialBlockRequestMode::Off;
+ }
+ EPartialBlockRequestMode Mode = PartialBlockRequestModeFromString(m_AllowPartialBlockRequests);
+ if (Mode == EPartialBlockRequestMode::Invalid)
+ {
+ throw OptionParseException(fmt::format("'--allow-partial-block-requests' ('{}') is invalid", m_AllowPartialBlockRequests),
+ SubOption->help());
+ }
+ return Mode;
+ };
+
if (SubOption == &m_DownloadOptions)
{
if (!IsQuiet)
@@ -11477,7 +11764,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
ZEN_CONSOLE_WARN("Ignoring '--force' option when '--cache-prime-only' is enabled");
}
- if (m_AllowPartialBlockRequests && m_PrimeCacheOnly)
+ if (m_AllowPartialBlockRequests != "false" && m_PrimeCacheOnly)
{
ZEN_CONSOLE_WARN("Ignoring '--allow-partial-block-requests' option when '--cache-prime-only' is enabled");
}
@@ -11485,6 +11772,8 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
std::vector<Oid> BuildPartIds = ParseBuildPartIds();
std::vector<std::string> BuildPartNames = ParseBuildPartNames();
+ EPartialBlockRequestMode PartialBlockRequestMode = ParseAllowPartialBlockRequests();
+
DownloadFolder(Storage,
BuildId,
BuildPartIds,
@@ -11493,7 +11782,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
DownloadOptions{.SystemRootDir = m_SystemRootDir,
.ZenFolderPath = m_ZenFolderPath,
.AllowMultiparts = m_AllowMultiparts,
- .AllowPartialBlockRequests = m_AllowPartialBlockRequests && !m_PrimeCacheOnly,
+ .PartialBlockRequestMode = PartialBlockRequestMode,
.CleanTargetFolder = m_Clean,
.PostDownloadVerify = m_PostDownloadVerify,
.PrimeCacheOnly = m_PrimeCacheOnly,
@@ -11680,6 +11969,8 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
}
MakeSafeAbsolutePathÍnPlace(m_ZenFolderPath);
+ EPartialBlockRequestMode PartialBlockRequestMode = ParseAllowPartialBlockRequests();
+
BuildStorage::Statistics StorageStats;
BuildStorageCache::Statistics StorageCacheStats;
@@ -11705,7 +11996,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
DownloadOptions{.SystemRootDir = m_SystemRootDir,
.ZenFolderPath = m_ZenFolderPath,
.AllowMultiparts = m_AllowMultiparts,
- .AllowPartialBlockRequests = m_AllowPartialBlockRequests,
+ .PartialBlockRequestMode = PartialBlockRequestMode,
.CleanTargetFolder = BuildIdString == m_BuildIds.front(),
.PostDownloadVerify = true,
.PrimeCacheOnly = false,
@@ -11791,6 +12082,8 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
}
});
+ EPartialBlockRequestMode PartialBlockRequestMode = ParseAllowPartialBlockRequests();
+
BuildStorage::Statistics StorageStats;
BuildStorageCache::Statistics StorageCacheStats;
@@ -11877,7 +12170,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
DownloadOptions{.SystemRootDir = m_SystemRootDir,
.ZenFolderPath = DownloadPath / ZenFolderName,
.AllowMultiparts = m_AllowMultiparts,
- .AllowPartialBlockRequests = m_AllowPartialBlockRequests,
+ .PartialBlockRequestMode = PartialBlockRequestMode,
.CleanTargetFolder = true,
.PostDownloadVerify = true,
.PrimeCacheOnly = false,
@@ -11898,7 +12191,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
DownloadOptions{.SystemRootDir = m_SystemRootDir,
.ZenFolderPath = DownloadPath / ZenFolderName,
.AllowMultiparts = m_AllowMultiparts,
- .AllowPartialBlockRequests = m_AllowPartialBlockRequests,
+ .PartialBlockRequestMode = PartialBlockRequestMode,
.CleanTargetFolder = false,
.PostDownloadVerify = true,
.PrimeCacheOnly = false,
@@ -12011,7 +12304,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
DownloadOptions{.SystemRootDir = m_SystemRootDir,
.ZenFolderPath = DownloadPath / ZenFolderName,
.AllowMultiparts = m_AllowMultiparts,
- .AllowPartialBlockRequests = m_AllowPartialBlockRequests,
+ .PartialBlockRequestMode = PartialBlockRequestMode,
.CleanTargetFolder = false,
.PostDownloadVerify = true,
.PrimeCacheOnly = false,
@@ -12063,7 +12356,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
DownloadOptions{.SystemRootDir = m_SystemRootDir,
.ZenFolderPath = DownloadPath / ZenFolderName,
.AllowMultiparts = m_AllowMultiparts,
- .AllowPartialBlockRequests = m_AllowPartialBlockRequests,
+ .PartialBlockRequestMode = PartialBlockRequestMode,
.CleanTargetFolder = false,
.PostDownloadVerify = true,
.PrimeCacheOnly = false,
@@ -12083,7 +12376,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
DownloadOptions{.SystemRootDir = m_SystemRootDir,
.ZenFolderPath = DownloadPath / ZenFolderName,
.AllowMultiparts = m_AllowMultiparts,
- .AllowPartialBlockRequests = m_AllowPartialBlockRequests,
+ .PartialBlockRequestMode = PartialBlockRequestMode,
.CleanTargetFolder = false,
.PostDownloadVerify = true,
.PrimeCacheOnly = false,
@@ -12103,7 +12396,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
DownloadOptions{.SystemRootDir = m_SystemRootDir,
.ZenFolderPath = DownloadPath / ZenFolderName,
.AllowMultiparts = m_AllowMultiparts,
- .AllowPartialBlockRequests = m_AllowPartialBlockRequests,
+ .PartialBlockRequestMode = PartialBlockRequestMode,
.CleanTargetFolder = false,
.PostDownloadVerify = true,
.PrimeCacheOnly = false,
@@ -12123,7 +12416,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
DownloadOptions{.SystemRootDir = m_SystemRootDir,
.ZenFolderPath = DownloadPath2 / ZenFolderName,
.AllowMultiparts = m_AllowMultiparts,
- .AllowPartialBlockRequests = m_AllowPartialBlockRequests,
+ .PartialBlockRequestMode = PartialBlockRequestMode,
.CleanTargetFolder = false,
.PostDownloadVerify = true,
.PrimeCacheOnly = false,
diff --git a/src/zen/cmds/builds_cmd.h b/src/zen/cmds/builds_cmd.h
index 26f804fa5..be0422210 100644
--- a/src/zen/cmds/builds_cmd.h
+++ b/src/zen/cmds/builds_cmd.h
@@ -64,7 +64,7 @@ private:
bool m_Force = false;
uint8_t m_BlockReuseMinPercentLimit = 85;
bool m_AllowMultiparts = true;
- bool m_AllowPartialBlockRequests = true;
+ std::string m_AllowPartialBlockRequests = "mixed";
std::string m_ManifestPath; // Not a std::filesystem::path since it can be relative to m_Path
// Direct access token (may expire)
diff --git a/src/zen/zen.cpp b/src/zen/zen.cpp
index 200c8e4b4..2721433b0 100644
--- a/src/zen/zen.cpp
+++ b/src/zen/zen.cpp
@@ -72,6 +72,88 @@ ZEN_THIRD_PARTY_INCLUDES_END
namespace zen {
+#if ZEN_PLATFORM_WINDOWS
+static HANDLE
+GetConsoleHandle()
+{
+ static HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
+ return hStdOut;
+}
+#endif
+
+static bool
+CheckStdoutTty()
+{
+#if ZEN_PLATFORM_WINDOWS
+ HANDLE hStdOut = GetConsoleHandle();
+ DWORD dwMode = 0;
+ static bool IsConsole = ::GetConsoleMode(hStdOut, &dwMode);
+ return IsConsole;
+#else
+ return isatty(fileno(stdout));
+#endif
+}
+
+static bool
+IsStdoutTty()
+{
+ static bool StdoutIsTty = CheckStdoutTty();
+ return StdoutIsTty;
+}
+
+static void
+OutputToConsoleRaw(const char* String, size_t Length)
+{
+#if ZEN_PLATFORM_WINDOWS
+ HANDLE hStdOut = GetConsoleHandle();
+#endif
+
+#if ZEN_PLATFORM_WINDOWS
+ if (IsStdoutTty())
+ {
+ WriteConsoleA(hStdOut, String, (DWORD)Length, 0, 0);
+ }
+ else
+ {
+ ::WriteFile(hStdOut, (LPCVOID)String, (DWORD)Length, 0, 0);
+ }
+#else
+ fwrite(String, 1, Length, stdout);
+#endif
+}
+
+static void
+OutputToConsoleRaw(const std::string& String)
+{
+ OutputToConsoleRaw(String.c_str(), String.length());
+}
+
+static void
+OutputToConsoleRaw(const StringBuilderBase& SB)
+{
+ OutputToConsoleRaw(SB.c_str(), SB.Size());
+}
+
+static uint32_t
+GetConsoleColumns(uint32_t Default)
+{
+#if ZEN_PLATFORM_WINDOWS
+ HANDLE hStdOut = GetConsoleHandle();
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ if (GetConsoleScreenBufferInfo(hStdOut, &csbi) == TRUE)
+ {
+ return (uint32_t)(csbi.srWindow.Right - csbi.srWindow.Left + 1);
+ }
+#else
+ struct winsize w;
+ if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == 0)
+ {
+ return (uint32_t)w.ws_col;
+ }
+#endif
+ return Default;
+}
+
enum class ReturnCode : std::int8_t
{
kSuccess = 0,
@@ -122,6 +204,8 @@ ZenCmdBase::ParseOptions(int argc, char** argv)
bool
ZenCmdBase::ParseOptions(cxxopts::Options& CmdOptions, int argc, char** argv)
{
+ CmdOptions.set_width(GetConsoleColumns(80));
+
cxxopts::ParseResult Result;
try
@@ -308,88 +392,6 @@ ZenCmdBase::ResolveTargetHostSpec(const std::string& InHostSpec)
return ResolveTargetHostSpec(InHostSpec, /* out */ Dummy);
}
-#if ZEN_PLATFORM_WINDOWS
-static HANDLE
-GetConsoleHandle()
-{
- static HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
- return hStdOut;
-}
-#endif
-
-static bool
-CheckStdoutTty()
-{
-#if ZEN_PLATFORM_WINDOWS
- HANDLE hStdOut = GetConsoleHandle();
- DWORD dwMode = 0;
- static bool IsConsole = ::GetConsoleMode(hStdOut, &dwMode);
- return IsConsole;
-#else
- return isatty(fileno(stdout));
-#endif
-}
-
-static bool
-IsStdoutTty()
-{
- static bool StdoutIsTty = CheckStdoutTty();
- return StdoutIsTty;
-}
-
-static void
-OutputToConsoleRaw(const char* String, size_t Length)
-{
-#if ZEN_PLATFORM_WINDOWS
- HANDLE hStdOut = GetConsoleHandle();
-#endif
-
-#if ZEN_PLATFORM_WINDOWS
- if (IsStdoutTty())
- {
- WriteConsoleA(hStdOut, String, (DWORD)Length, 0, 0);
- }
- else
- {
- ::WriteFile(hStdOut, (LPCVOID)String, (DWORD)Length, 0, 0);
- }
-#else
- fwrite(String, 1, Length, stdout);
-#endif
-}
-
-static void
-OutputToConsoleRaw(const std::string& String)
-{
- OutputToConsoleRaw(String.c_str(), String.length());
-}
-
-static void
-OutputToConsoleRaw(const StringBuilderBase& SB)
-{
- OutputToConsoleRaw(SB.c_str(), SB.Size());
-}
-
-static uint32_t
-GetConsoleColumns()
-{
-#if ZEN_PLATFORM_WINDOWS
- HANDLE hStdOut = GetConsoleHandle();
- CONSOLE_SCREEN_BUFFER_INFO csbi;
- if (GetConsoleScreenBufferInfo(hStdOut, &csbi) == TRUE)
- {
- return (uint32_t)(csbi.srWindow.Right - csbi.srWindow.Left + 1);
- }
-#else
- struct winsize w;
- if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == 0)
- {
- return (uint32_t)w.ws_col;
- }
-#endif
- return 1024;
-}
-
void
ProgressBar::SetLogOperationName(Mode InMode, std::string_view Name)
{
@@ -504,7 +506,7 @@ ProgressBar::UpdateState(const State& NewState, bool DoLinebreak)
uint64_t ETAMS =
(NewState.Status == State::EStatus::Running) && (PercentDone > 5) ? (ETAElapsedMS * NewState.RemainingCount) / Completed : 0;
- uint32_t ConsoleColumns = GetConsoleColumns();
+ uint32_t ConsoleColumns = GetConsoleColumns(1024);
const std::string PercentString = fmt::format("{:#3}%", PercentDone);