You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I have spent the last couple of weeks working resilience strategy. For some background, I am developing a syncing capability with my mobile app and openlibrary. I have 46 book series with about 370 books. I have developed methods that will first update my books with the openlibrary id and then sync my book series with my personal list on openlibrary.org. That's a whole lot of rest calls to openlibrary.org and I was exceeding their rate limiter and getting errors back. That's where all this came into play.
There was quite a bit of work to wrap all of my calls through the app in a polly resilience pipeline. After some research and discover it appears there is a much simpler way to do this. You can basically attach your resilience strategy to an httpclient.
The bulk of the changes are in the constructor of OpenLibraryClient. I have added two optional parameters that allow you to customize the resilience strategy. If the parameters are not provided then the default resilience strategy is used. You could also opt to only create the resilience strategy if the parameter is provided. The 2nd parameter is providing a logBuilder for debugging etc..
I am not finished with research in this area I did however want to share this with you so you could think about this approach.
I have been trying to figure out how to add some information to the debug statements that provides the url called on the polly debug and information debug output lines. That has been a bit elusive. Also making sure cancelation tokens are used correctly is on my list.
Another approach that might make the library simpilier is to allow the user to pass in the httpclient. Then all of the setup could be done in the calling method outside of openlibrary.net
Below the code here some brief documentation on how to create an openlibraryclient passing in optional strategies.
Brady
`
public OpenLibraryClient(Action? configureOptions = null,Action? logBuilder = null)
{
ServiceCollection? services = new ServiceCollection();
// Define the custom HttpClientHandler
//_cookieContainer = new CookieContainer();
_httpHandler = new HttpClientHandler
{
AllowAutoRedirect = true,
UseCookies = true,
CookieContainer = _cookieContainer// Ensure a new CookieContainer is used
};
services.AddHttpClient(httpClientName)
.ConfigurePrimaryHttpMessageHandler(() => _httpHandler)
//.AddHttpMessageHandler<OperationKeyHandler>()
.AddStandardResilienceHandler(configureOptions);
if (logBuilder != null)
services.AddLogging(logBuilder);
var serviceProvider = services.BuildServiceProvider();
_httpClientFactory = serviceProvider.GetService<IHttpClientFactory>();
_httpClient = _httpClientFactory.CreateClient(httpClientName+"-standard");
`
/// To reuse the backing HttpClient, reuse the OpenLibrary instance.
/// //Examples of configureOptions :
/// // refer to the following link for more information on how to configure the retry strategy options.
/// //https://learn.microsoft.com/en-us/dotnet/core/resilience/http-resilience?tabs=dotnet-cli
/// var retryStrategy = new HttpRetryStrategyOptions
/// {
/// // Customize and configure the retry logic.
/// BackoffType = DelayBackoffType.Exponential,
/// MaxRetryAttempts = 5,
/// UseJitter = true
/// };
///
/// // two different rate limiters are defined here for an example , you only use one with the library.
/// var rateLimiter = new FixedWindowRateLimiter(
/// new FixedWindowRateLimiterOptions
/// {
/// PermitLimit = 2,
/// Window = TimeSpan.FromSeconds(1),
/// QueueLimit = int.MaxValue,
/// });
///
///
/// // 200 request per minute leaky bucket strategy
///
/// var tokenLimiter = new TokenBucketRateLimiter(
/// new TokenBucketRateLimiterOptions
/// {
/// TokensPerPeriod = 200 * 60 / 5, // Calculate tokens per second (200 req/min / 5 seconds per token bucket)
/// TokenLimit = 400, // Allow 400 tokens in the bucket (2 minutes worth)
/// ReplenishmentPeriod = TimeSpan.FromSeconds(5), // Replenish every 5 seconds
/// QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
/// QueueLimit = int.MaxValue
/// });
///
///
/// string name = "OpenLibraryDefault";
/// var configureOptions = new Action(options =>
/// {
/// options.Retry = retryStrategy;
/// options.RateLimiter = new HttpRateLimiterStrategyOptions
/// {
/// Name = $"{name}-{nameof(HttpStandardResilienceOptions.RateLimiter)}",
/// RateLimiter = args => rateLimiter.AcquireAsync(cancellationToken: args.Context.CancellationToken)
/// };
/// });
///
/// OLClient = new OpenLibraryClient(configureOptions);
The text was updated successfully, but these errors were encountered:
I have spent the last couple of weeks working resilience strategy. For some background, I am developing a syncing capability with my mobile app and openlibrary. I have 46 book series with about 370 books. I have developed methods that will first update my books with the openlibrary id and then sync my book series with my personal list on openlibrary.org. That's a whole lot of rest calls to openlibrary.org and I was exceeding their rate limiter and getting errors back. That's where all this came into play.
There was quite a bit of work to wrap all of my calls through the app in a polly resilience pipeline. After some research and discover it appears there is a much simpler way to do this. You can basically attach your resilience strategy to an httpclient.
The bulk of the changes are in the constructor of OpenLibraryClient. I have added two optional parameters that allow you to customize the resilience strategy. If the parameters are not provided then the default resilience strategy is used. You could also opt to only create the resilience strategy if the parameter is provided. The 2nd parameter is providing a logBuilder for debugging etc..
I got most of this from the following article.
https://learn.microsoft.com/en-us/dotnet/core/resilience/http-resilience?tabs=dotnet-cli
I am not finished with research in this area I did however want to share this with you so you could think about this approach.
I have been trying to figure out how to add some information to the debug statements that provides the url called on the polly debug and information debug output lines. That has been a bit elusive. Also making sure cancelation tokens are used correctly is on my list.
Another approach that might make the library simpilier is to allow the user to pass in the httpclient. Then all of the setup could be done in the calling method outside of openlibrary.net
Below the code here some brief documentation on how to create an openlibraryclient passing in optional strategies.
Brady
`
public OpenLibraryClient(Action? configureOptions = null,Action? logBuilder = null)
{
`
/// To reuse the backing HttpClient, reuse the OpenLibrary instance.
/// //Examples of configureOptions :
/// // refer to the following link for more information on how to configure the retry strategy options.
/// //https://learn.microsoft.com/en-us/dotnet/core/resilience/http-resilience?tabs=dotnet-cli
/// var retryStrategy = new HttpRetryStrategyOptions
/// {
/// // Customize and configure the retry logic.
/// BackoffType = DelayBackoffType.Exponential,
/// MaxRetryAttempts = 5,
/// UseJitter = true
/// };
///
/// // two different rate limiters are defined here for an example , you only use one with the library.
/// var rateLimiter = new FixedWindowRateLimiter(
/// new FixedWindowRateLimiterOptions
/// {
/// PermitLimit = 2,
/// Window = TimeSpan.FromSeconds(1),
/// QueueLimit = int.MaxValue,
/// });
///
///
/// // 200 request per minute leaky bucket strategy
///
/// var tokenLimiter = new TokenBucketRateLimiter(
/// new TokenBucketRateLimiterOptions
/// {
/// TokensPerPeriod = 200 * 60 / 5, // Calculate tokens per second (200 req/min / 5 seconds per token bucket)
/// TokenLimit = 400, // Allow 400 tokens in the bucket (2 minutes worth)
/// ReplenishmentPeriod = TimeSpan.FromSeconds(5), // Replenish every 5 seconds
/// QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
/// QueueLimit = int.MaxValue
/// });
///
///
/// string name = "OpenLibraryDefault";
/// var configureOptions = new Action(options =>
/// {
/// options.Retry = retryStrategy;
/// options.RateLimiter = new HttpRateLimiterStrategyOptions
/// {
/// Name = $"{name}-{nameof(HttpStandardResilienceOptions.RateLimiter)}",
/// RateLimiter = args => rateLimiter.AcquireAsync(cancellationToken: args.Context.CancellationToken)
/// };
/// });
///
/// OLClient = new OpenLibraryClient(configureOptions);
The text was updated successfully, but these errors were encountered: