devhl
devhl
CC#
Created by devhl on 3/9/2024 in #help
Slow HttpClient responses
I have a program which polls an API. It usually works great, but lately, I've been seeing many HttpClient requests take up to 30 seconds despite my timeouts. Here is how I configured the client. And here is the library where these methods to configure Polly reside https://github.com/devhl-labs/CocApi/blob/47f7e1ec097fdfd7f50e60436d7a0fe96d87f721/src/CocApi.Test/Program.cs#L62
options.AddCocApiHttpClients(
builder: builder => builder
.AddRetryPolicy(context.Configuration.GetValue<int>("CocApi:Rest:HttpClient:Retries")) // 1
.AddTimeoutPolicy(TimeSpan.FromMilliseconds(context.Configuration.GetValue<long>("CocApi:Rest:HttpClient:Timeout"))) // 1.5 seconds
.AddCircuitBreakerPolicy(
context.Configuration.GetValue<int>("CocApi:Rest:HttpClient:HandledEventsAllowedBeforeBreaking"), // 20
TimeSpan.FromSeconds(context.Configuration.GetValue<int>("CocApi:Rest:HttpClient:DurationOfBreak"))) // 30 seconds
.ConfigurePrimaryHttpMessageHandler(sp => new HttpClientHandler
{
// this property is important if you query the api very fast
MaxConnectionsPerServer = context.Configuration.GetValue<int>("CocApi:Rest:HttpClient:MaxConnectionsPerServer"), // 100
CookieContainer = sp.GetRequiredService<CookieContainer>().Value
})
);
options.AddCocApiHttpClients(
builder: builder => builder
.AddRetryPolicy(context.Configuration.GetValue<int>("CocApi:Rest:HttpClient:Retries")) // 1
.AddTimeoutPolicy(TimeSpan.FromMilliseconds(context.Configuration.GetValue<long>("CocApi:Rest:HttpClient:Timeout"))) // 1.5 seconds
.AddCircuitBreakerPolicy(
context.Configuration.GetValue<int>("CocApi:Rest:HttpClient:HandledEventsAllowedBeforeBreaking"), // 20
TimeSpan.FromSeconds(context.Configuration.GetValue<int>("CocApi:Rest:HttpClient:DurationOfBreak"))) // 30 seconds
.ConfigurePrimaryHttpMessageHandler(sp => new HttpClientHandler
{
// this property is important if you query the api very fast
MaxConnectionsPerServer = context.Configuration.GetValue<int>("CocApi:Rest:HttpClient:MaxConnectionsPerServer"), // 100
CookieContainer = sp.GetRequiredService<CookieContainer>().Value
})
);
And here is the request itself. After the request, I log the difference between the current DateTime and the requestedAtLocalVar, which is showing me up to 30 seconds. It normally shows less than a second.
DateTime requestedAtLocalVar = DateTime.UtcNow;

using (HttpResponseMessage httpResponseMessageLocalVar = await HttpClient.SendAsync(httpRequestMessageLocalVar, cancellationToken).ConfigureAwait(false))
{
string responseContentLocalVar = await httpResponseMessageLocalVar.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);

....
DateTime requestedAtLocalVar = DateTime.UtcNow;

using (HttpResponseMessage httpResponseMessageLocalVar = await HttpClient.SendAsync(httpRequestMessageLocalVar, cancellationToken).ConfigureAwait(false))
{
string responseContentLocalVar = await httpResponseMessageLocalVar.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);

....
31 replies
CC#
Created by devhl on 8/19/2023 in #help
❔ Can't share JsonSerializerOptions with several SerializerContext
From this SO I see that you can't share options with multiple contexts. This is what I am observing. To work around this limitation, when I construct my context with a copy of the options. This seems like the wrong thing to do, so I am looking for input from the community. The reason I am using multiple contexts is when I tried with only one context, there was a warning about a name conflict. Since what I am talking about here is generated C#, it seemed wise to avoid the warning and just use multiple contexts. Here is the test showing the de/serialization works as I have it. https://github.com/devhl-labs/openapi-generator/blob/f237e51991d5c1c7ef5500bba8ff51ea319f0353/samples/client/petstore/csharp/OpenAPIClient-generichost-manual-tests/OpenAPIClient-generichost-manual-tests/UnitTest1.cs#L49 Here is the context I am using in the above test. https://github.com/devhl-labs/openapi-generator/blob/f237e51991d5c1c7ef5500bba8ff51ea319f0353/samples/client/petstore/csharp/OpenAPIClient-generichost-net6.0-nrt/src/Org.OpenAPITools/Model/Category.cs#L184C18-L184C18 Here is how the host is getting the options and the context. https://github.com/devhl-labs/openapi-generator/blob/f237e51991d5c1c7ef5500bba8ff51ea319f0353/samples/client/petstore/csharp/OpenAPIClient-generichost-net6.0-nrt/src/Org.OpenAPITools/Client/HostConfiguration.cs#L137 Is my approach wrong?
2 replies
CC#
Created by devhl on 6/3/2023 in #help
❔ Get required value from IConfiguration
5 replies
CC#
Created by devhl on 5/27/2023 in #help
✅ NotNullWhen not working as expected
This must just be a Roslyn bug...right?
using System.Diagnostics.CodeAnalysis;

namespace ConsoleApp1
{
internal class Program
{
static void Main(string[] args)
{
string userInput = "foo";

bool isFormatted = TryFormat(userInput, out string? formattedUserInput);

if (isFormatted)
Console.WriteLine(formattedUserInput.ToString()); // 'formattedUserInput' may be null here
}

public static bool TryFormat(string userInput, [NotNullWhen(true)] out string? output)
{
output = userInput;

return true;
}
}
}
using System.Diagnostics.CodeAnalysis;

namespace ConsoleApp1
{
internal class Program
{
static void Main(string[] args)
{
string userInput = "foo";

bool isFormatted = TryFormat(userInput, out string? formattedUserInput);

if (isFormatted)
Console.WriteLine(formattedUserInput.ToString()); // 'formattedUserInput' may be null here
}

public static bool TryFormat(string userInput, [NotNullWhen(true)] out string? output)
{
output = userInput;

return true;
}
}
}
5 replies
CC#
Created by devhl on 3/13/2023 in #help
❔ Utf8JsonReader.GetString exceptions
This document seems to contradict itself. The returns section says if the token is null then you get null, but the exceptions say null will cause an exception. Am I understanding this right and it is wrong documentation? https://learn.microsoft.com/en-us/dotnet/api/system.text.json.utf8jsonreader.getstring?view=net-7.0#returns
9 replies
CC#
Created by devhl on 2/12/2023 in #help
❔ HttpResponse CacheHeaders
21 replies
CC#
Created by devhl on 10/28/2022 in #help
Middleware to modify url
I'm trying to modify my HttpClient so it modifies a url before sending. I think I see how to do it but I just want to check as I've never done this. Also, what is a primary message handler? How is primary different than any other message handler?
services.AddHttpClient("cocApi", config =>
{
config.BaseAddress = new Uri(hostBuilder.Configuration["Settings:BaseAddress"]);
})
.ConfigurePrimaryHttpMessageHandler(sp => new SocketsHttpHandler()
{
MaxConnectionsPerServer = 10
})
.AddHttpMessageHandler((services) => new Foo());
});
}

public class Foo : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
Console.WriteLine("hi"); // this fires so i guess i got it right
return base.SendAsync(request, cancellationToken);
}
}
services.AddHttpClient("cocApi", config =>
{
config.BaseAddress = new Uri(hostBuilder.Configuration["Settings:BaseAddress"]);
})
.ConfigurePrimaryHttpMessageHandler(sp => new SocketsHttpHandler()
{
MaxConnectionsPerServer = 10
})
.AddHttpMessageHandler((services) => new Foo());
});
}

public class Foo : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
Console.WriteLine("hi"); // this fires so i guess i got it right
return base.SendAsync(request, cancellationToken);
}
}
10 replies
CC#
Created by devhl on 9/21/2022 in #help
File names for generic class
I usually name my generic classes like Foo`1.cs. I believe Microsoft used to do that too, but looks like they've moved away from that. Is there a reason not to? Or a preferred alternative?
21 replies