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);
}
}
}