From 5d452824cdb8785434bfa7f23a6f70a17dffc911 Mon Sep 17 00:00:00 2001 From: sam Date: Tue, 11 Mar 2025 16:10:55 +0100 Subject: [PATCH] refactor(backend): use single shared HTTP client with backoff --- .../Extensions/WebApplicationExtensions.cs | 36 +++ Foxnouns.Backend/Foxnouns.Backend.csproj | 13 +- Foxnouns.Backend/Jobs/CreateDataExportJob.cs | 4 +- .../Services/Auth/FediverseAuthService.cs | 8 +- .../Auth/RemoteAuthService.Discord.cs | 4 +- .../Services/Auth/RemoteAuthService.Google.cs | 2 +- .../Services/Auth/RemoteAuthService.Tumblr.cs | 4 +- .../Services/Auth/RemoteAuthService.cs | 2 +- Foxnouns.Backend/packages.lock.json | 234 ++++++++++++++---- 9 files changed, 232 insertions(+), 75 deletions(-) diff --git a/Foxnouns.Backend/Extensions/WebApplicationExtensions.cs b/Foxnouns.Backend/Extensions/WebApplicationExtensions.cs index 8db7a1b..6a010fc 100644 --- a/Foxnouns.Backend/Extensions/WebApplicationExtensions.cs +++ b/Foxnouns.Backend/Extensions/WebApplicationExtensions.cs @@ -21,8 +21,10 @@ using Foxnouns.Backend.Services; using Foxnouns.Backend.Services.Auth; using Foxnouns.Backend.Services.V1; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Http.Resilience; using Minio; using NodaTime; +using Polly; using Prometheus; using Serilog; using Serilog.Events; @@ -100,6 +102,40 @@ public static class WebApplicationExtensions builder.Host.ConfigureServices( (ctx, services) => { + // create a single HTTP client for all requests. + // it's also configured with a retry mechanism, so that requests aren't immediately lost to the void if they fail + services.AddSingleton(_ => + { + // ReSharper disable once SuggestVarOrType_Elsewhere + var retryPipeline = new ResiliencePipelineBuilder() + .AddRetry( + new HttpRetryStrategyOptions + { + BackoffType = DelayBackoffType.Linear, + MaxRetryAttempts = 3, + } + ) + .Build(); + + var resilienceHandler = new ResilienceHandler(retryPipeline) + { + InnerHandler = new SocketsHttpHandler + { + PooledConnectionLifetime = TimeSpan.FromMinutes(15), + }, + }; + + var client = new HttpClient(resilienceHandler); + client.DefaultRequestHeaders.Remove("User-Agent"); + client.DefaultRequestHeaders.Remove("Accept"); + client.DefaultRequestHeaders.Add( + "User-Agent", + $"pronouns.cc/{BuildInfo.Version}" + ); + client.DefaultRequestHeaders.Add("Accept", "application/json"); + return client; + }); + services .AddQueue() .AddSmtpMailer(ctx.Configuration) diff --git a/Foxnouns.Backend/Foxnouns.Backend.csproj b/Foxnouns.Backend/Foxnouns.Backend.csproj index a9972ab..42a3beb 100644 --- a/Foxnouns.Backend/Foxnouns.Backend.csproj +++ b/Foxnouns.Backend/Foxnouns.Backend.csproj @@ -25,12 +25,13 @@ all + - - + + @@ -38,14 +39,14 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + - - + + diff --git a/Foxnouns.Backend/Jobs/CreateDataExportJob.cs b/Foxnouns.Backend/Jobs/CreateDataExportJob.cs index 3662e33..1c392f7 100644 --- a/Foxnouns.Backend/Jobs/CreateDataExportJob.cs +++ b/Foxnouns.Backend/Jobs/CreateDataExportJob.cs @@ -27,6 +27,7 @@ using NodaTime.Text; namespace Foxnouns.Backend.Jobs; public class CreateDataExportJob( + HttpClient client, DatabaseContext db, IClock clock, UserRendererService userRenderer, @@ -36,7 +37,6 @@ public class CreateDataExportJob( ILogger logger ) { - private static readonly HttpClient Client = new(); private readonly ILogger _logger = logger.ForContext(); public static void Enqueue(Snowflake userId) @@ -201,7 +201,7 @@ public class CreateDataExportJob( if (s3Path == null) return; - HttpResponseMessage resp = await Client.GetAsync(s3Path); + HttpResponseMessage resp = await client.GetAsync(s3Path); if (resp.StatusCode != HttpStatusCode.OK) { _logger.Warning("S3 path {S3Path} returned a non-200 status, not saving file", s3Path); diff --git a/Foxnouns.Backend/Services/Auth/FediverseAuthService.cs b/Foxnouns.Backend/Services/Auth/FediverseAuthService.cs index 49afe1d..daa15ab 100644 --- a/Foxnouns.Backend/Services/Auth/FediverseAuthService.cs +++ b/Foxnouns.Backend/Services/Auth/FediverseAuthService.cs @@ -34,6 +34,7 @@ public partial class FediverseAuthService ILogger logger, Config config, DatabaseContext db, + HttpClient client, KeyCacheService keyCacheService, ISnowflakeGenerator snowflakeGenerator ) @@ -43,12 +44,7 @@ public partial class FediverseAuthService _db = db; _keyCacheService = keyCacheService; _snowflakeGenerator = snowflakeGenerator; - - _client = new HttpClient(); - _client.DefaultRequestHeaders.Remove("User-Agent"); - _client.DefaultRequestHeaders.Remove("Accept"); - _client.DefaultRequestHeaders.Add("User-Agent", $"pronouns.cc/{BuildInfo.Version}"); - _client.DefaultRequestHeaders.Add("Accept", "application/json"); + _client = client; } public async Task GenerateAuthUrlAsync( diff --git a/Foxnouns.Backend/Services/Auth/RemoteAuthService.Discord.cs b/Foxnouns.Backend/Services/Auth/RemoteAuthService.Discord.cs index de16d2f..d884fda 100644 --- a/Foxnouns.Backend/Services/Auth/RemoteAuthService.Discord.cs +++ b/Foxnouns.Backend/Services/Auth/RemoteAuthService.Discord.cs @@ -27,7 +27,7 @@ public partial class RemoteAuthService ) { var redirectUri = $"{config.BaseUrl}/auth/callback/discord"; - HttpResponseMessage resp = await _httpClient.PostAsync( + HttpResponseMessage resp = await client.PostAsync( _discordTokenUri, new FormUrlEncodedContent( new Dictionary @@ -59,7 +59,7 @@ public partial class RemoteAuthService var req = new HttpRequestMessage(HttpMethod.Get, _discordUserUri); req.Headers.Add("Authorization", $"{token.TokenType} {token.AccessToken}"); - HttpResponseMessage resp2 = await _httpClient.SendAsync(req, ct); + HttpResponseMessage resp2 = await client.SendAsync(req, ct); resp2.EnsureSuccessStatusCode(); DiscordUserResponse? user = await resp2.Content.ReadFromJsonAsync(ct); if (user == null) diff --git a/Foxnouns.Backend/Services/Auth/RemoteAuthService.Google.cs b/Foxnouns.Backend/Services/Auth/RemoteAuthService.Google.cs index 3245858..938ba32 100644 --- a/Foxnouns.Backend/Services/Auth/RemoteAuthService.Google.cs +++ b/Foxnouns.Backend/Services/Auth/RemoteAuthService.Google.cs @@ -28,7 +28,7 @@ public partial class RemoteAuthService ) { var redirectUri = $"{config.BaseUrl}/auth/callback/google"; - HttpResponseMessage resp = await _httpClient.PostAsync( + HttpResponseMessage resp = await client.PostAsync( _googleTokenUri, new FormUrlEncodedContent( new Dictionary diff --git a/Foxnouns.Backend/Services/Auth/RemoteAuthService.Tumblr.cs b/Foxnouns.Backend/Services/Auth/RemoteAuthService.Tumblr.cs index d63ee1a..45b9161 100644 --- a/Foxnouns.Backend/Services/Auth/RemoteAuthService.Tumblr.cs +++ b/Foxnouns.Backend/Services/Auth/RemoteAuthService.Tumblr.cs @@ -29,7 +29,7 @@ public partial class RemoteAuthService ) { var redirectUri = $"{config.BaseUrl}/auth/callback/tumblr"; - HttpResponseMessage resp = await _httpClient.PostAsync( + HttpResponseMessage resp = await client.PostAsync( _tumblrTokenUri, new FormUrlEncodedContent( new Dictionary @@ -62,7 +62,7 @@ public partial class RemoteAuthService var req = new HttpRequestMessage(HttpMethod.Get, _tumblrUserUri); req.Headers.Add("Authorization", $"Bearer {token.AccessToken}"); - HttpResponseMessage resp2 = await _httpClient.SendAsync(req, ct); + HttpResponseMessage resp2 = await client.SendAsync(req, ct); if (!resp2.IsSuccessStatusCode) { string respBody = await resp2.Content.ReadAsStringAsync(ct); diff --git a/Foxnouns.Backend/Services/Auth/RemoteAuthService.cs b/Foxnouns.Backend/Services/Auth/RemoteAuthService.cs index 6e0ba76..93b006b 100644 --- a/Foxnouns.Backend/Services/Auth/RemoteAuthService.cs +++ b/Foxnouns.Backend/Services/Auth/RemoteAuthService.cs @@ -25,6 +25,7 @@ using Microsoft.EntityFrameworkCore; namespace Foxnouns.Backend.Services.Auth; public partial class RemoteAuthService( + HttpClient client, Config config, ILogger logger, DatabaseContext db, @@ -32,7 +33,6 @@ public partial class RemoteAuthService( ) { private readonly ILogger _logger = logger.ForContext(); - private readonly HttpClient _httpClient = new(); public record RemoteUser(string Id, string Username); diff --git a/Foxnouns.Backend/packages.lock.json b/Foxnouns.Backend/packages.lock.json index 96daa5d..365ee8c 100644 --- a/Foxnouns.Backend/packages.lock.json +++ b/Foxnouns.Backend/packages.lock.json @@ -155,6 +155,18 @@ "Microsoft.Extensions.Primitives": "9.0.2" } }, + "Microsoft.Extensions.Http.Resilience": { + "type": "Direct", + "requested": "[9.2.0, )", + "resolved": "9.2.0", + "contentHash": "Km+YyCuk1IaeOsAzPDygtgsUOh3Fi89hpA18si0tFJmpSBf9aKzP9ffV5j7YOoVDvRWirpumXAPQzk1inBsvKw==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "9.0.2", + "Microsoft.Extensions.Http.Diagnostics": "9.2.0", + "Microsoft.Extensions.ObjectPool": "9.0.2", + "Microsoft.Extensions.Resilience": "9.2.0" + } + }, "MimeKit": { "type": "Direct", "requested": "[4.10.0, )", @@ -537,6 +549,16 @@ "Microsoft.Extensions.Logging": "9.0.2" } }, + "Microsoft.Extensions.AmbientMetadata.Application": { + "type": "Transitive", + "resolved": "9.2.0", + "contentHash": "GMCX3zybUB22aAADjYPXrWhhd1HNMkcY5EcFAJnXy/4k5pPpJ6TS4VRl37xfrtosNyzbpO2SI7pd2Q5PvggSdg==", + "dependencies": { + "Microsoft.Extensions.Configuration": "9.0.2", + "Microsoft.Extensions.Hosting.Abstractions": "9.0.2", + "Microsoft.Extensions.Options.ConfigurationExtensions": "9.0.2" + } + }, "Microsoft.Extensions.Caching.Abstractions": { "type": "Transitive", "resolved": "9.0.2", @@ -545,13 +567,22 @@ "Microsoft.Extensions.Primitives": "9.0.2" } }, + "Microsoft.Extensions.Compliance.Abstractions": { + "type": "Transitive", + "resolved": "9.2.0", + "contentHash": "Te+N4xphDlGIS90lKJMZyezFiMWKLAtYV2/M8gGJG4thH6xyC7LWhMzgz2+tWMehxwZlBUq2D9DvVpjKBZFTPQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.2", + "Microsoft.Extensions.ObjectPool": "9.0.2" + } + }, "Microsoft.Extensions.Configuration": { "type": "Transitive", - "resolved": "9.0.0", - "contentHash": "YIMO9T3JL8MeEXgVozKt2v79hquo/EFtnY0vgxmLnUvk1Rei/halI7kOWZL2RBeV9FMGzgM9LZA8CVaNwFMaNA==", + "resolved": "9.0.2", + "contentHash": "EBZW+u96tApIvNtjymXEIS44tH0I/jNwABHo4c33AchWOiDWCq2rL3klpnIo+xGrxoVGJzPDISV6hZ+a9C9SzQ==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.0", - "Microsoft.Extensions.Primitives": "9.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "9.0.2", + "Microsoft.Extensions.Primitives": "9.0.2" } }, "Microsoft.Extensions.Configuration.Abstractions": { @@ -564,10 +595,10 @@ }, "Microsoft.Extensions.Configuration.Binder": { "type": "Transitive", - "resolved": "9.0.0", - "contentHash": "RiScL99DcyngY9zJA2ROrri7Br8tn5N4hP4YNvGdTN/bvg1A3dwvDOxHnNZ3Im7x2SJ5i4LkX1uPiR/MfSFBLQ==", + "resolved": "9.0.2", + "contentHash": "krJ04xR0aPXrOf5dkNASg6aJjsdzexvsMRL6UNOUjiTzqBvRr95sJ1owoKEm89bSONQCfZNhHrAFV9ahDqIPIw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "9.0.2" } }, "Microsoft.Extensions.DependencyInjection": { @@ -583,6 +614,14 @@ "resolved": "9.0.2", "contentHash": "MNe7GSTBf3jQx5vYrXF0NZvn6l7hUKF6J54ENfAgCO8y6xjN1XUmKKWG464LP2ye6QqDiA1dkaWEZBYnhoZzjg==" }, + "Microsoft.Extensions.DependencyInjection.AutoActivation": { + "type": "Transitive", + "resolved": "9.2.0", + "contentHash": "WcwfTpl3IcPcaahTVEaJwMUg1eWog1SkIA6jQZZFqMXiMX9/tVkhNB6yzUQmBdGWdlWDDRKpOmK7T7x1Uu05pQ==", + "dependencies": { + "Microsoft.Extensions.Hosting.Abstractions": "9.0.2" + } + }, "Microsoft.Extensions.DependencyModel": { "type": "Transitive", "resolved": "9.0.2", @@ -590,54 +629,74 @@ }, "Microsoft.Extensions.Diagnostics": { "type": "Transitive", - "resolved": "9.0.0", - "contentHash": "0CF9ZrNw5RAlRfbZuVIvzzhP8QeWqHiUmMBU/2H7Nmit8/vwP3/SbHeEctth7D4Gz2fBnEbokPc1NU8/j/1ZLw==", + "resolved": "9.0.2", + "contentHash": "kwFWk6DPaj1Roc0CExRv+TTwjsiERZA730jQIPlwCcS5tMaCAQtaGfwAK0z8CMFpVTiT+MgKXpd/P50qVCuIgg==", "dependencies": { - "Microsoft.Extensions.Configuration": "9.0.0", - "Microsoft.Extensions.Diagnostics.Abstractions": "9.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "9.0.0" + "Microsoft.Extensions.Configuration": "9.0.2", + "Microsoft.Extensions.Diagnostics.Abstractions": "9.0.2", + "Microsoft.Extensions.Options.ConfigurationExtensions": "9.0.2" } }, "Microsoft.Extensions.Diagnostics.Abstractions": { "type": "Transitive", - "resolved": "9.0.0", - "contentHash": "1K8P7XzuzX8W8pmXcZjcrqS6x5eSSdvhQohmcpgiQNY/HlDAlnrhR9dvlURfFz428A+RTCJpUyB+aKTA6AgVcQ==", + "resolved": "9.0.2", + "contentHash": "kFwIZEC/37cwKuEm/nXvjF7A/Myz9O7c7P9Csgz6AOiiDE62zdOG5Bu7VkROu1oMYaX0wgijPJ5LqVt6+JKjVg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.0", - "Microsoft.Extensions.Options": "9.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.2", + "Microsoft.Extensions.Options": "9.0.2" + } + }, + "Microsoft.Extensions.Diagnostics.ExceptionSummarization": { + "type": "Transitive", + "resolved": "9.2.0", + "contentHash": "et5JevHsLv1w1O1Zhb6LiUfai/nmDRzIHnbrZJdzLsIbbMCKTZpeHuANYIppAD//n12KvgOne05j4cu0GhG9gw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.2" } }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", - "resolved": "9.0.0", - "contentHash": "uK439QzYR0q2emLVtYzwyK3x+T5bTY4yWsd/k/ZUS9LR6Sflp8MIdhGXW8kQCd86dQD4tLqvcbLkku8qHY263Q==", + "resolved": "9.0.2", + "contentHash": "IcOBmTlr2jySswU+3x8c3ql87FRwTVPQgVKaV5AXzPT5u0VItfNU8SMbESpdSp5STwxT/1R99WYszgHWsVkzhg==", "dependencies": { - "Microsoft.Extensions.Primitives": "9.0.0" + "Microsoft.Extensions.Primitives": "9.0.2" } }, "Microsoft.Extensions.Hosting.Abstractions": { "type": "Transitive", - "resolved": "9.0.0", - "contentHash": "yUKJgu81ExjvqbNWqZKshBbLntZMbMVz/P7Way2SBx7bMqA08Mfdc9O7hWDKAiSp+zPUGT6LKcSCQIPeDK+CCw==", + "resolved": "9.0.2", + "contentHash": "PvjZW6CMdZbPbOwKsQXYN5VPtIWZQqdTRuBPZiW3skhU3hymB17XSlLVC4uaBbDZU+/3eHG3p80y+MzZxZqR7Q==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.0", - "Microsoft.Extensions.Diagnostics.Abstractions": "9.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "9.0.0", - "Microsoft.Extensions.Logging.Abstractions": "9.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "9.0.2", + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.2", + "Microsoft.Extensions.Diagnostics.Abstractions": "9.0.2", + "Microsoft.Extensions.FileProviders.Abstractions": "9.0.2", + "Microsoft.Extensions.Logging.Abstractions": "9.0.2" } }, "Microsoft.Extensions.Http": { "type": "Transitive", - "resolved": "9.0.0", - "contentHash": "DqI4q54U4hH7bIAq9M5a/hl5Odr/KBAoaZ0dcT4OgutD8dook34CbkvAfAIzkMVjYXiL+E5ul9etwwqiX4PHGw==", + "resolved": "9.0.2", + "contentHash": "34+kcwxPZr3Owk9eZx268+gqGNB8G/8Y96gZHomxam0IOH08FhPBjPrLWDtKdVn4+sVUUJnJMpECSTJi4XXCcg==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.0", - "Microsoft.Extensions.Diagnostics": "9.0.0", - "Microsoft.Extensions.Logging": "9.0.0", - "Microsoft.Extensions.Logging.Abstractions": "9.0.0", - "Microsoft.Extensions.Options": "9.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "9.0.2", + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.2", + "Microsoft.Extensions.Diagnostics": "9.0.2", + "Microsoft.Extensions.Logging": "9.0.2", + "Microsoft.Extensions.Logging.Abstractions": "9.0.2", + "Microsoft.Extensions.Options": "9.0.2" + } + }, + "Microsoft.Extensions.Http.Diagnostics": { + "type": "Transitive", + "resolved": "9.2.0", + "contentHash": "Eeup1LuD5hVk5SsKAuX1D7I9sF380MjrNG10IaaauRLOmrRg8rq2TA8PYTXVBXf3MLkZ6m2xpBqRbZdxf8ygkg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.AutoActivation": "9.2.0", + "Microsoft.Extensions.Http": "9.0.2", + "Microsoft.Extensions.Options.ConfigurationExtensions": "9.0.2", + "Microsoft.Extensions.Telemetry": "9.2.0", + "System.IO.Pipelines": "9.0.2" } }, "Microsoft.Extensions.Logging": { @@ -660,23 +719,23 @@ }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", - "resolved": "9.0.0", - "contentHash": "H05HiqaNmg6GjH34ocYE9Wm1twm3Oz2aXZko8GTwGBzM7op2brpAA8pJ5yyD1OpS1mXUtModBYOlcZ/wXeWsSg==", + "resolved": "9.0.2", + "contentHash": "pnwYZE7U6d3Y6iMVqADOAUUMMBGYAQPsT3fMwVr/V1Wdpe5DuVGFcViZavUthSJ5724NmelIl1cYy+kRfKfRPQ==", "dependencies": { - "Microsoft.Extensions.Configuration": "9.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "9.0.0", - "Microsoft.Extensions.Configuration.Binder": "9.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.0", - "Microsoft.Extensions.Logging": "9.0.0", - "Microsoft.Extensions.Logging.Abstractions": "9.0.0", - "Microsoft.Extensions.Options": "9.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "9.0.0" + "Microsoft.Extensions.Configuration": "9.0.2", + "Microsoft.Extensions.Configuration.Abstractions": "9.0.2", + "Microsoft.Extensions.Configuration.Binder": "9.0.2", + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.2", + "Microsoft.Extensions.Logging": "9.0.2", + "Microsoft.Extensions.Logging.Abstractions": "9.0.2", + "Microsoft.Extensions.Options": "9.0.2", + "Microsoft.Extensions.Options.ConfigurationExtensions": "9.0.2" } }, "Microsoft.Extensions.ObjectPool": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "udvKco0sAVgYGTBnHUb0tY9JQzJ/nPDiv/8PIyz69wl1AibeCDZOLVVI+6156dPfHmJH7ws5oUJRiW4ZmAvuuA==" + "resolved": "9.0.2", + "contentHash": "nWx7uY6lfkmtpyC2dGc0IxtrZZs/LnLCQHw3YYQucbqWj8a27U/dZ+eh72O3ZiolqLzzLkVzoC+w/M8dZwxRTw==" }, "Microsoft.Extensions.Options": { "type": "Transitive", @@ -689,14 +748,14 @@ }, "Microsoft.Extensions.Options.ConfigurationExtensions": { "type": "Transitive", - "resolved": "9.0.0", - "contentHash": "Ob3FXsXkcSMQmGZi7qP07EQ39kZpSBlTcAZLbJLdI4FIf0Jug8biv2HTavWmnTirchctPlq9bl/26CXtQRguzA==", + "resolved": "9.0.2", + "contentHash": "OPm1NXdMg4Kb4Kz+YHdbBQfekh7MqQZ7liZ5dYUd+IbJakinv9Fl7Ck6Strbgs0a6E76UGbP/jHR532K/7/feQ==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "9.0.0", - "Microsoft.Extensions.Configuration.Binder": "9.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.0", - "Microsoft.Extensions.Options": "9.0.0", - "Microsoft.Extensions.Primitives": "9.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "9.0.2", + "Microsoft.Extensions.Configuration.Binder": "9.0.2", + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.2", + "Microsoft.Extensions.Options": "9.0.2", + "Microsoft.Extensions.Primitives": "9.0.2" } }, "Microsoft.Extensions.Primitives": { @@ -704,6 +763,42 @@ "resolved": "9.0.2", "contentHash": "puBMtKe/wLuYa7H6docBkLlfec+h8L35DXqsDKKJgW0WY5oCwJ3cBJKcDaZchv6knAyqOMfsl6VUbaR++E5LXA==" }, + "Microsoft.Extensions.Resilience": { + "type": "Transitive", + "resolved": "9.2.0", + "contentHash": "dyaM+Jeznh/i21bOrrRs3xceFfn0571EOjOq95dRXmL1rHDLC4ExhACJ2xipRBP6g1AgRNqmryi+hMrVWWgmlg==", + "dependencies": { + "Microsoft.Extensions.Diagnostics": "9.0.2", + "Microsoft.Extensions.Diagnostics.ExceptionSummarization": "9.2.0", + "Microsoft.Extensions.Options.ConfigurationExtensions": "9.0.2", + "Microsoft.Extensions.Telemetry.Abstractions": "9.2.0", + "Polly.Extensions": "8.4.2", + "Polly.RateLimiting": "8.4.2" + } + }, + "Microsoft.Extensions.Telemetry": { + "type": "Transitive", + "resolved": "9.2.0", + "contentHash": "4+bw7W4RrAMrND9TxonnSmzJOdXiPxljoda8OPJiReIN607mKCc0t0Mf28sHNsTujO1XQw28wsI0poxeeQxohw==", + "dependencies": { + "Microsoft.Extensions.AmbientMetadata.Application": "9.2.0", + "Microsoft.Extensions.DependencyInjection.AutoActivation": "9.2.0", + "Microsoft.Extensions.Logging.Configuration": "9.0.2", + "Microsoft.Extensions.ObjectPool": "9.0.2", + "Microsoft.Extensions.Telemetry.Abstractions": "9.2.0" + } + }, + "Microsoft.Extensions.Telemetry.Abstractions": { + "type": "Transitive", + "resolved": "9.2.0", + "contentHash": "kEl+5G3RqS20XaEhHh/nOugcjKEK+rgVtMJra1iuwNzdzQXElelf3vu8TugcT7rIZ/T4T76EKW1OX/fmlxz4hw==", + "dependencies": { + "Microsoft.Extensions.Compliance.Abstractions": "9.2.0", + "Microsoft.Extensions.Logging.Abstractions": "9.0.2", + "Microsoft.Extensions.ObjectPool": "9.0.2", + "Microsoft.Extensions.Options": "9.0.2" + } + }, "Microsoft.NETCore.Platforms": { "type": "Transitive", "resolved": "1.1.1", @@ -760,6 +855,30 @@ "System.IO.Pipelines": "5.0.1" } }, + "Polly.Core": { + "type": "Transitive", + "resolved": "8.4.2", + "contentHash": "BpE2I6HBYYA5tF0Vn4eoQOGYTYIK1BlF5EXVgkWGn3mqUUjbXAr13J6fZVbp7Q3epRR8yshacBMlsHMhpOiV3g==" + }, + "Polly.Extensions": { + "type": "Transitive", + "resolved": "8.4.2", + "contentHash": "GZ9vRVmR0jV2JtZavt+pGUsQ1O1cuRKG7R7VOZI6ZDy9y6RNPvRvXK1tuS4ffUrv8L0FTea59oEuQzgS0R7zSA==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "8.0.0", + "Microsoft.Extensions.Options": "8.0.0", + "Polly.Core": "8.4.2" + } + }, + "Polly.RateLimiting": { + "type": "Transitive", + "resolved": "8.4.2", + "contentHash": "ehTImQ/eUyO07VYW2WvwSmU9rRH200SKJ/3jku9rOkyWE0A2JxNFmAVms8dSn49QLSjmjFRRSgfNyOgr/2PSmA==", + "dependencies": { + "Polly.Core": "8.4.2", + "System.Threading.RateLimiting": "8.0.0" + } + }, "Sentry": { "type": "Transitive", "resolved": "5.3.0", @@ -901,8 +1020,8 @@ }, "System.IO.Pipelines": { "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "jRn6JYnNPW6xgQazROBLSfpdoczRw694vO5kKvMcNnpXuolEixUyw6IBuBs2Y2mlSX/LdLvyyWmfXhaI3ND1Yg==" + "resolved": "9.0.2", + "contentHash": "UIBaK7c/A3FyQxmX/747xw4rCUkm1BhNiVU617U5jweNJssNjLJkPUGhBsrlDG0BpKWCYKsncD+Kqpy4KmvZZQ==" }, "System.Reactive": { "type": "Transitive", @@ -940,6 +1059,11 @@ "type": "Transitive", "resolved": "7.0.0", "contentHash": "qmeeYNROMsONF6ndEZcIQ+VxR4Q/TX/7uIVLJqtwIWL7dDWeh0l1UIqgo4wYyjG//5lUNhwkLDSFl+pAWO6oiA==" + }, + "System.Threading.RateLimiting": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "7mu9v0QDv66ar3DpGSZHg9NuNcxDaaAcnMULuZlaTpP9+hwXhrxNGsF5GmLkSHxFdb5bBc1TzeujsRgTrPWi+Q==" } } }