Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rethinking use of polly.... #22

Open
bradyguyc opened this issue Oct 3, 2024 · 0 comments
Open

Rethinking use of polly.... #22

bradyguyc opened this issue Oct 3, 2024 · 0 comments

Comments

@bradyguyc
Copy link

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)
{

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);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant