Merge pull request #4261 from Spacetech/music_brainz_provider_thread_safe

Make MusicBrainzAlbumProvider thread safe and fix retry logic
pull/4346/head
Joshua M. Boniface 4 years ago committed by GitHub
commit 19c2abb50f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -46,6 +46,7 @@ namespace MediaBrowser.Providers.Music
private readonly string _musicBrainzBaseUrl; private readonly string _musicBrainzBaseUrl;
private SemaphoreSlim _apiRequestLock = new SemaphoreSlim(1, 1);
private Stopwatch _stopWatchMusicBrainz = new Stopwatch(); private Stopwatch _stopWatchMusicBrainz = new Stopwatch();
public MusicBrainzAlbumProvider( public MusicBrainzAlbumProvider(
@ -742,48 +743,58 @@ namespace MediaBrowser.Providers.Music
/// </summary> /// </summary>
internal async Task<HttpResponseMessage> GetMusicBrainzResponse(string url, CancellationToken cancellationToken) internal async Task<HttpResponseMessage> GetMusicBrainzResponse(string url, CancellationToken cancellationToken)
{ {
using var options = new HttpRequestMessage(HttpMethod.Get, _musicBrainzBaseUrl.TrimEnd('/') + url); await _apiRequestLock.WaitAsync(cancellationToken).ConfigureAwait(false);
// MusicBrainz request a contact email address is supplied, as comment, in user agent field: try
// https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting#User-Agent
options.Headers.UserAgent.ParseAdd(string.Format(
CultureInfo.InvariantCulture,
"{0} ( {1} )",
_appHost.ApplicationUserAgent,
_appHost.ApplicationUserAgentAddress));
HttpResponseMessage response;
var attempts = 0u;
do
{ {
attempts++; HttpResponseMessage response;
var attempts = 0u;
var requestUrl = _musicBrainzBaseUrl.TrimEnd('/') + url;
if (_stopWatchMusicBrainz.ElapsedMilliseconds < _musicBrainzQueryIntervalMs) do
{ {
// MusicBrainz is extremely adamant about limiting to one request per second attempts++;
var delayMs = _musicBrainzQueryIntervalMs - _stopWatchMusicBrainz.ElapsedMilliseconds;
await Task.Delay((int)delayMs, cancellationToken).ConfigureAwait(false);
}
// Write time since last request to debug log as evidence we're meeting rate limit if (_stopWatchMusicBrainz.ElapsedMilliseconds < _musicBrainzQueryIntervalMs)
// requirement, before resetting stopwatch back to zero. {
_logger.LogDebug("GetMusicBrainzResponse: Time since previous request: {0} ms", _stopWatchMusicBrainz.ElapsedMilliseconds); // MusicBrainz is extremely adamant about limiting to one request per second.
_stopWatchMusicBrainz.Restart(); var delayMs = _musicBrainzQueryIntervalMs - _stopWatchMusicBrainz.ElapsedMilliseconds;
await Task.Delay((int)delayMs, cancellationToken).ConfigureAwait(false);
}
response = await _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(options).ConfigureAwait(false); // Write time since last request to debug log as evidence we're meeting rate limit
// requirement, before resetting stopwatch back to zero.
_logger.LogDebug("GetMusicBrainzResponse: Time since previous request: {0} ms", _stopWatchMusicBrainz.ElapsedMilliseconds);
_stopWatchMusicBrainz.Restart();
// We retry a finite number of times, and only whilst MB is indicating 503 (throttling) using var request = new HttpRequestMessage(HttpMethod.Get, requestUrl);
}
while (attempts < MusicBrainzQueryAttempts && response.StatusCode == HttpStatusCode.ServiceUnavailable); // MusicBrainz request a contact email address is supplied, as comment, in user agent field:
// https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting#User-Agent .
request.Headers.UserAgent.ParseAdd(string.Format(
CultureInfo.InvariantCulture,
"{0} ( {1} )",
_appHost.ApplicationUserAgent,
_appHost.ApplicationUserAgentAddress));
// Log error if unable to query MB database due to throttling response = await _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(request).ConfigureAwait(false);
if (attempts == MusicBrainzQueryAttempts && response.StatusCode == HttpStatusCode.ServiceUnavailable)
// We retry a finite number of times, and only whilst MB is indicating 503 (throttling).
}
while (attempts < MusicBrainzQueryAttempts && response.StatusCode == HttpStatusCode.ServiceUnavailable);
// Log error if unable to query MB database due to throttling.
if (attempts == MusicBrainzQueryAttempts && response.StatusCode == HttpStatusCode.ServiceUnavailable)
{
_logger.LogError("GetMusicBrainzResponse: 503 Service Unavailable (throttled) response received {0} times whilst requesting {1}", attempts, requestUrl);
}
return response;
}
finally
{ {
_logger.LogError("GetMusicBrainzResponse: 503 Service Unavailable (throttled) response received {0} times whilst requesting {1}", attempts, options.RequestUri); _apiRequestLock.Release();
} }
return response;
} }
/// <inheritdoc /> /// <inheritdoc />

Loading…
Cancel
Save