mirror of
https://github.com/WowUp/WowUp.git
synced 2026-04-21 06:17:46 -04:00
Add circuit breakers.
Drop most cached times to 5 min. Better scan error handling. Better featured addons error handling.
This commit is contained in:
@@ -5,6 +5,6 @@ namespace WowUp.Common.Services.Contracts
|
||||
{
|
||||
public interface ICacheService
|
||||
{
|
||||
Task<T> GetCache<T>(string cacheKey, Func<Task<T>> fallbackAction, int ttlMinutes = 60);
|
||||
Task<T> GetCache<T>(string cacheKey, Func<Task<T>> fallbackAction, int ttlMinutes = 10);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Flurl;
|
||||
using Flurl.Http;
|
||||
using Polly;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
@@ -32,6 +33,14 @@ namespace WowUp.WPF.AddonProviders
|
||||
private readonly ICacheService _cacheService;
|
||||
private readonly IAnalyticsService _analyticsService;
|
||||
|
||||
private readonly AsyncPolicy CircuitBreaker = Policy
|
||||
.Handle<FlurlHttpException>()
|
||||
.CircuitBreakerAsync(
|
||||
2,
|
||||
TimeSpan.FromMinutes(1),
|
||||
(ex, ts) => { Log.Error(ex, "Curse CircuitBreaker broken"); },
|
||||
() => { Log.Information("Curse CircuitBreaker reset"); });
|
||||
|
||||
public string Name => "Curse";
|
||||
|
||||
public CurseAddonProvider(
|
||||
@@ -119,11 +128,11 @@ namespace WowUp.WPF.AddonProviders
|
||||
|
||||
return await _cacheService.GetCache(url, async () =>
|
||||
{
|
||||
return await url
|
||||
return await CircuitBreaker.ExecuteAsync(async () => await url
|
||||
.WithHeaders(HttpUtilities.DefaultHeaders)
|
||||
.WithTimeout(HttpTimeoutSeconds)
|
||||
.GetJsonAsync<CurseSearchResult>();
|
||||
});
|
||||
.GetJsonAsync<CurseSearchResult>());
|
||||
}, 5);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<PotentialAddon>> Search(string query, WowClientType clientType)
|
||||
@@ -492,11 +501,11 @@ namespace WowUp.WPF.AddonProviders
|
||||
|
||||
try
|
||||
{
|
||||
return await url
|
||||
return await CircuitBreaker.ExecuteAsync(async () => await url
|
||||
.WithHeaders(HttpUtilities.DefaultHeaders)
|
||||
.WithTimeout(HttpTimeoutSeconds)
|
||||
.PostJsonAsync(addonIds.Select(id => Convert.ToInt32(id)).ToArray())
|
||||
.ReceiveJson<List<CurseSearchResult>>();
|
||||
.ReceiveJson<List<CurseSearchResult>>());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -512,11 +521,11 @@ namespace WowUp.WPF.AddonProviders
|
||||
|
||||
try
|
||||
{
|
||||
return await url
|
||||
return await CircuitBreaker.ExecuteAsync(async () => await url
|
||||
.SetQueryParams(new { gameId = 1, searchFilter = query })
|
||||
.WithTimeout(HttpTimeoutSeconds)
|
||||
.WithHeaders(HttpUtilities.DefaultHeaders)
|
||||
.GetJsonAsync<IList<CurseSearchResult>>();
|
||||
.GetJsonAsync<IList<CurseSearchResult>>());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -542,12 +551,12 @@ namespace WowUp.WPF.AddonProviders
|
||||
|
||||
var response = await _cacheService.GetCache(url, async () =>
|
||||
{
|
||||
return await url
|
||||
return await CircuitBreaker.ExecuteAsync(async () => await url
|
||||
.WithHeaders(HttpUtilities.DefaultHeaders)
|
||||
.WithTimeout(HttpTimeoutSeconds)
|
||||
.PostJsonAsync(body)
|
||||
.ReceiveJson<CurseGetFeaturedResponse>();
|
||||
});
|
||||
.ReceiveJson<CurseGetFeaturedResponse>());
|
||||
}, 5);
|
||||
|
||||
return response.Popular.ToList();
|
||||
}
|
||||
@@ -562,11 +571,11 @@ namespace WowUp.WPF.AddonProviders
|
||||
{
|
||||
var url = $"{ApiUrl}/fingerprint";
|
||||
|
||||
return await url
|
||||
return await CircuitBreaker.ExecuteAsync(async () => await url
|
||||
.WithHeaders(HttpUtilities.DefaultHeaders)
|
||||
.WithTimeout(HttpTimeoutSeconds)
|
||||
.PostJsonAsync(fingerprints)
|
||||
.ReceiveJson<CurseFingerprintsResponse>();
|
||||
.ReceiveJson<CurseFingerprintsResponse>());
|
||||
}
|
||||
|
||||
private PotentialAddon GetPotentialAddon(CurseSearchResult searchResult)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using Flurl;
|
||||
using Flurl.Http;
|
||||
using Polly;
|
||||
using Polly.CircuitBreaker;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -28,6 +30,14 @@ namespace WowUp.WPF.AddonProviders
|
||||
private readonly ICacheService _cacheService;
|
||||
private readonly IAnalyticsService _analyticsService;
|
||||
|
||||
private readonly AsyncPolicy CircuitBreaker = Policy
|
||||
.Handle<FlurlHttpException>()
|
||||
.CircuitBreakerAsync(
|
||||
2,
|
||||
TimeSpan.FromMinutes(1),
|
||||
(ex, ts) => { Log.Error(ex, "TukUI CircuitBreaker broken"); },
|
||||
() => { Log.Information("TukUI CircuitBreaker reset"); });
|
||||
|
||||
public string Name => "TukUI";
|
||||
|
||||
public TukUiAddonProvider(
|
||||
@@ -229,11 +239,12 @@ namespace WowUp.WPF.AddonProviders
|
||||
{
|
||||
var query = GetAddonsSuffix(clientType);
|
||||
|
||||
var result = await ApiUrl
|
||||
.SetQueryParam(query, "all")
|
||||
.WithTimeout(HttpTimeoutSeconds)
|
||||
.WithHeaders(HttpUtilities.DefaultHeaders)
|
||||
.GetJsonAsync<List<TukUiAddon>>();
|
||||
var result = await CircuitBreaker.ExecuteAsync(async () =>
|
||||
await ApiUrl
|
||||
.SetQueryParam(query, "all")
|
||||
.WithTimeout(HttpTimeoutSeconds)
|
||||
.WithHeaders(HttpUtilities.DefaultHeaders)
|
||||
.GetJsonAsync<List<TukUiAddon>>());
|
||||
|
||||
if (clientType.IsRetail())
|
||||
{
|
||||
@@ -246,20 +257,20 @@ namespace WowUp.WPF.AddonProviders
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Failed to get all addons");
|
||||
return null;
|
||||
throw;
|
||||
}
|
||||
});
|
||||
}, 5);
|
||||
|
||||
return results ?? new List<TukUiAddon>();
|
||||
}
|
||||
|
||||
private async Task<TukUiAddon> GetClientApiAddon(string addonName)
|
||||
{
|
||||
return await ClientApiUrl
|
||||
return await CircuitBreaker.ExecuteAsync(async () => await ClientApiUrl
|
||||
.SetQueryParam("ui", addonName)
|
||||
.WithTimeout(HttpTimeoutSeconds)
|
||||
.WithHeaders(HttpUtilities.DefaultHeaders)
|
||||
.GetJsonAsync<TukUiAddon>();
|
||||
.GetJsonAsync<TukUiAddon>());
|
||||
}
|
||||
|
||||
private async Task<TukUiAddon> GetElvUiRetailAddon()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Flurl.Http;
|
||||
using Polly;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -27,6 +28,14 @@ namespace WowUp.WPF.AddonProviders
|
||||
private readonly IAnalyticsService _analyticsService;
|
||||
private readonly ICacheService _cacheService;
|
||||
|
||||
private readonly AsyncPolicy CircuitBreaker = Policy
|
||||
.Handle<FlurlHttpException>()
|
||||
.CircuitBreakerAsync(
|
||||
2,
|
||||
TimeSpan.FromMinutes(1),
|
||||
(ex, ts) => { Log.Error(ex, "WowInterface CircuitBreaker broken"); },
|
||||
() => { Log.Information("WowInterface CircuitBreaker reset"); });
|
||||
|
||||
public string Name => "WowInterface";
|
||||
|
||||
public WowInterfaceAddonProvider(
|
||||
@@ -153,13 +162,13 @@ namespace WowUp.WPF.AddonProviders
|
||||
|
||||
return await _cacheService.GetCache(url, async () =>
|
||||
{
|
||||
var results = await url
|
||||
var results = await CircuitBreaker.ExecuteAsync(async () => await url
|
||||
.WithHeaders(HttpUtilities.DefaultHeaders)
|
||||
.WithTimeout(HttpTimeoutSeconds)
|
||||
.GetJsonAsync<List<AddonDetailsResponse>>();
|
||||
.GetJsonAsync<List<AddonDetailsResponse>>());
|
||||
|
||||
return results.FirstOrDefault();
|
||||
});
|
||||
}, 5);
|
||||
}
|
||||
|
||||
private string GetAddonId(Uri addonUri)
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
{
|
||||
"ChangeLogs": [
|
||||
{
|
||||
"Version": "1.18.3",
|
||||
"Description": "Add cuircuit breakers to reduce chaining failing calls onto apis."
|
||||
},
|
||||
{
|
||||
"Version": "1.18.2",
|
||||
"Description": "Add some shorter HTTP timeout times (4 seconds).\nAdd some feedback when scanning from a provider fails.\nFix a bug with CF sending empty current files."
|
||||
|
||||
@@ -159,11 +159,23 @@ namespace WowUp.WPF.Services
|
||||
|
||||
public async Task<List<PotentialAddon>> GetFeaturedAddons(WowClientType clientType)
|
||||
{
|
||||
var addonTasks = _providers.Select(p => p.GetFeaturedAddons(clientType));
|
||||
var addonResults = await Task.WhenAll(addonTasks);
|
||||
var addonResultsConcat = addonResults.SelectMany(res => res);
|
||||
List<PotentialAddon> addonResults = new List<PotentialAddon>();
|
||||
foreach (var provider in _providers)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await provider.GetFeaturedAddons(clientType);
|
||||
addonResults.AddRange(result);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Failed to get feature addons from {provider.Name}");
|
||||
}
|
||||
}
|
||||
|
||||
return addonResultsConcat.OrderByDescending(result => result.DownloadCount).ToList();
|
||||
return addonResults
|
||||
.OrderByDescending(result => result.DownloadCount)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public async Task<List<Addon>> GetAddons(WowClientType clientType, bool rescan = false)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
@@ -213,7 +214,7 @@ namespace WowUp.WPF.ViewModels
|
||||
|
||||
try
|
||||
{
|
||||
if(SelectedClientType == WowClientType.None)
|
||||
if (SelectedClientType == WowClientType.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -240,6 +241,10 @@ namespace WowUp.WPF.ViewModels
|
||||
|
||||
SetResultCountContextText(DisplayAddons.Count);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "failed to get popular addons");
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<PackageId>WowUp</PackageId>
|
||||
<Authors>Jliddev</Authors>
|
||||
<Product>WowUp</Product>
|
||||
<Version>1.18.2</Version>
|
||||
<Version>1.18.3</Version>
|
||||
<ApplicationIcon>wowup_logo_512np_RRT_icon.ico</ApplicationIcon>
|
||||
<Copyright>jliddev</Copyright>
|
||||
<PackageProjectUrl>https://wowup.io</PackageProjectUrl>
|
||||
@@ -62,6 +62,7 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.7" />
|
||||
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.19" />
|
||||
<PackageReference Include="Polly" Version="7.2.1" />
|
||||
<PackageReference Include="protobuf-net" Version="3.0.29" />
|
||||
<PackageReference Include="Serilog" Version="2.9.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
|
||||
|
||||
Reference in New Issue
Block a user