Skip to content

Commit

Permalink
throttle httpclient requests
Browse files Browse the repository at this point in the history
  • Loading branch information
Omotola committed Oct 10, 2023
1 parent ae50a41 commit b168a9e
Show file tree
Hide file tree
Showing 2 changed files with 14 additions and 0 deletions.
7 changes: 7 additions & 0 deletions src/Microsoft.ComponentDetection.Detectors/pip/IPyPiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ public sealed class PyPiClient : IPyPiClient, IDisposable
private readonly IEnvironmentVariableService environmentVariableService;
private readonly ILogger<PyPiClient> logger;

// Semaphore to limit the number of concurrent calls to pypi.org
private readonly SemaphoreSlim semaphore;

private bool checkedMaxEntriesVariable;

// retries used so far for calls to pypi.org
Expand All @@ -80,6 +83,7 @@ public PyPiClient(IEnvironmentVariableService environmentVariableService, ILogge
FinalCacheSize = 0,
};
this.logger = logger;
this.semaphore = new SemaphoreSlim(10, 10);
}

public static HttpClient HttpClient { get; internal set; } = new HttpClient(HttpClientHandler);
Expand Down Expand Up @@ -246,11 +250,13 @@ private async Task<HttpResponseMessage> GetAndCachePyPiResponseAsync(Uri uri)
return result;
}

await this.semaphore.WaitAsync();
this.logger.LogInformation("Getting Python data from {Uri}", uri);
using var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.UserAgent.Add(ProductValue);
request.Headers.UserAgent.Add(CommentValue);
var response = await HttpClient.SendAsync(request);
this.semaphore.Release();

// The `first - wins` response accepted into the cache. This might be different from the input if another caller wins the race.
return await this.cachedResponses.GetOrCreateAsync(uri, cacheEntry =>
Expand Down Expand Up @@ -282,5 +288,6 @@ public void Dispose()
this.cacheTelemetry.FinalCacheSize = this.cachedResponses.Count;
this.cacheTelemetry.Dispose();
this.cachedResponses.Dispose();
this.semaphore.Dispose();

Check warning on line 291 in src/Microsoft.ComponentDetection.Detectors/pip/IPyPiClient.cs

View check run for this annotation

Codecov / codecov/patch

src/Microsoft.ComponentDetection.Detectors/pip/IPyPiClient.cs#L291

Added line #L291 was not covered by tests
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ public sealed class SimplePyPiClient : ISimplePyPiClient, IDisposable
// Keep telemetry on how the cache is being used for future refinements
private readonly SimplePypiCacheTelemetryRecord cacheTelemetry = new SimplePypiCacheTelemetryRecord();

// Semaphore to limit the number of concurrent calls to pypi.org
private readonly SemaphoreSlim semaphore;

/// <summary>
/// A thread safe cache implementation which contains a mapping of URI -> SimpleProject for simplepypi api projects
/// and has a limited number of entries which will expire after the cache fills or a specified interval.
Expand All @@ -62,6 +65,7 @@ public SimplePyPiClient(IEnvironmentVariableService environmentVariableService,
{
this.environmentVariableService = environmentVariableService;
this.logger = logger;
this.semaphore = new SemaphoreSlim(10, 10);
}

public static HttpClient HttpClient { get; internal set; } = new HttpClient(HttpClientHandler);
Expand Down Expand Up @@ -265,12 +269,14 @@ private async Task<HttpResponseMessage> RetryPypiRequestAsync(Uri uri, PipDepend
/// <returns> Returns the httpresponsemessage. </returns>
private async Task<HttpResponseMessage> GetPypiResponseAsync(Uri uri)
{
await this.semaphore.WaitAsync();
this.logger.LogInformation("Getting Python data from {Uri}", uri);
using var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.UserAgent.Add(ProductValue);
request.Headers.UserAgent.Add(CommentValue);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.pypi.simple.v1+json"));
var response = await HttpClient.SendAsync(request);
this.semaphore.Release();
return response;
}

Expand All @@ -282,5 +288,6 @@ public void Dispose()
this.cachedProjectWheelFiles.Dispose();
this.cachedSimplePyPiProjects.Dispose();
HttpClient.Dispose();
this.semaphore.Dispose();

Check warning on line 291 in src/Microsoft.ComponentDetection.Detectors/pip/SimplePypiClient.cs

View check run for this annotation

Codecov / codecov/patch

src/Microsoft.ComponentDetection.Detectors/pip/SimplePypiClient.cs#L291

Added line #L291 was not covered by tests
}
}

0 comments on commit b168a9e

Please sign in to comment.