using Beam.Abstractions; using Beam.Exceptions; using Beam.Models; using Microsoft.Extensions.Logging; namespace Beam.Downloaders { /// /// Groups multiple binary downloads into a single Fragment, applying /// failure detection and exponential-back-off retries for each link. /// public class UnitFragmentDownloaderBinary : IUnitDownloader>> { public UnitFragmentDownloaderBinary(HttpClient client, AsyncTransformer transformer, AsyncDownloadFailurePredicate?[]? failurePredicate = null, int fragmentSize = 4, ILogger? logger = null, IUnitDownloader? internalDownloader = null) { Client = client; Transformer = transformer; FailurePredicate = failurePredicate; UnitDownloader = internalDownloader ?? new UnitDownloaderBinary(Client, Transformer, FailurePredicate); LinksPerDownload = fragmentSize; Logger = logger; } public HttpClient Client { get; } public AsyncTransformer Transformer { get; } public AsyncDownloadFailurePredicate?[]? FailurePredicate { get; } public int LinksPerDownload { get; set; } public ILogger? Logger { get; set; } private readonly IUnitDownloader UnitDownloader; async Task<(bool, Fragment>?)> IUnitDownloader>>.TryDownload( IOrdered[] link, CancellationToken ct, int maximumRetryCount, IProgress? tryProgress) { var fragment = new Fragment>(link.Length); if (!Fragment>.TryAcquireUpdater(fragment, out var updater)) throw new AssertionException(Exceptions.Exceptions.fragment_locked); var isFailure = false; await Parallel.ForEachAsync(link, async (orderedLink, pct) => { pct.ThrowIfCancellationRequested(); ct.ThrowIfCancellationRequested(); var (success, downloaded) = await UnitDownloader.TryDownload([orderedLink], ct, maximumRetryCount, tryProgress); if (!success || downloaded is null) { Interlocked.Exchange(ref isFailure, true); Logger?.LogError("Failed to retrieve {Link} order={Order}", orderedLink.Data, orderedLink.Order); return; } updater(new Ordered(downloaded, orderedLink.Order)); }); if (!isFailure) Fragment>.SetComplete(fragment, true); Fragment>.TryReleaseUpdater(fragment, updater); return (!isFailure, fragment); } } }