Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ExponentialRetry to FindDotNetCliPackage task #7882

Merged
merged 1 commit into from
Sep 10, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 63 additions & 5 deletions src/Microsoft.DotNet.Helix/Sdk/FindDotNetCliPackage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Arcade.Common;
using Microsoft.Build.Framework;
using NuGet.Versioning;

namespace Microsoft.DotNet.Helix.Sdk
{
public class FindDotNetCliPackage : BaseTask
{
// Use lots of retries since an Http Client failure here means failure to send to Helix
private ExponentialRetry _retry = new ExponentialRetry()
{
MaxAttempts = 10,
DelayBase = 3.0
};
private static readonly HttpClient _client = new HttpClient(new HttpClientHandler { CheckCertificateRevocationList = true });
private const string DotNetCliAzureFeed = "https://dotnetcli.blob.core.windows.net/dotnet";

Expand Down Expand Up @@ -56,8 +63,7 @@ private async Task ExecuteAsync()

try
{
using var req = new HttpRequestMessage(HttpMethod.Head, downloadUrl);
using HttpResponseMessage res = await _client.SendAsync(req);
using HttpResponseMessage res = await HeadRequestWithRetry(downloadUrl);

if (res.StatusCode == HttpStatusCode.NotFound)
{
Expand Down Expand Up @@ -128,10 +134,12 @@ private async Task<string> GetEffectiveVersion()
}
private async Task<string> GetMatchingProductVersionTxtContents(string baseUri, string customVersionTextFileName)
{
using HttpResponseMessage specificResponse = await _client.GetAsync($"{baseUri}/{customVersionTextFileName}");
Log.LogMessage(MessageImportance.Low, $"Checking for productVersion.txt files under {baseUri}");

using HttpResponseMessage specificResponse = await GetAsyncWithRetry($"{baseUri}/{customVersionTextFileName}");
if (specificResponse.StatusCode == HttpStatusCode.NotFound)
{
using HttpResponseMessage genericResponse = await _client.GetAsync($"{baseUri}/productVersion.txt");
using HttpResponseMessage genericResponse = await GetAsyncWithRetry($"{baseUri}/productVersion.txt");
if (genericResponse.StatusCode != HttpStatusCode.NotFound)
{
genericResponse.EnsureSuccessStatusCode();
Expand All @@ -150,6 +158,53 @@ private async Task<string> GetMatchingProductVersionTxtContents(string baseUri,
return Version;
}

private async Task<HttpResponseMessage> GetAsyncWithRetry(string uri)
{
HttpResponseMessage response = null;
await _retry.RunAsync(async attempt =>
{
try
{
response = await _client.GetAsync(uri);
return true;
}
catch (Exception toLog)
{
Log.LogMessage(MessageImportance.Low, $"Hit exception in GetAsync(); will retry up to 10 times ({toLog.Message})");
return false;
}
});
if (response == null) // All retries failed
{
throw new Exception($"Failed to GET from {uri}, even after retrying");
}
return response;
}

private async Task<HttpResponseMessage> HeadRequestWithRetry(string uri)
{
HttpResponseMessage response = null;
await _retry.RunAsync(async attempt =>
{
try
{
using var req = new HttpRequestMessage(HttpMethod.Head, uri);
response = await _client.SendAsync(req);
return true;
}
catch (Exception toLog)
{
Log.LogMessage(MessageImportance.Low, $"Hit exception in SendAsync(); will retry up to 10 times ({toLog.Message})");
return false;
}
});
if (response == null) // All retries failed
{
throw new Exception($"Failed to make HEAD request to {uri}, even after retrying");
}
return response;
}

private void NormalizeParameters()
{
if (string.Equals(Channel, "lts", StringComparison.OrdinalIgnoreCase))
Expand Down Expand Up @@ -201,7 +256,10 @@ private async Task ResolveVersionAsync()
};

Log.LogMessage(MessageImportance.Low, $"Resolving latest version from url {latestVersionUrl}");
string latestVersionContent = await _client.GetStringAsync(latestVersionUrl);

using HttpResponseMessage versionResponse = await GetAsyncWithRetry(latestVersionUrl);
versionResponse.EnsureSuccessStatusCode();
string latestVersionContent = await versionResponse.Content.ReadAsStringAsync();
string[] versionData = latestVersionContent.Split(Array.Empty<char>(), StringSplitOptions.RemoveEmptyEntries);
Version = versionData[1];
Log.LogMessage(MessageImportance.Low, $"Got latest dotnet cli version {Version}");
Expand Down