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

#356 #695 #1924 Custom HttpMessageInvoker pooling #1824

Merged
merged 35 commits into from
Jan 18, 2024

Conversation

ggnaegi
Copy link
Member

@ggnaegi ggnaegi commented Nov 30, 2023

Closes #356 #695 #1924

After some checks, it seems impossible to use HttpClientFactory in the solution.
There are several reasons for this:

  • First, the HttpMessageHandler can potentially be unique for each route. In the case of a "Named" HttpClient, it's possible to use ConfigurePrimaryHttpMessageHandler, but this method will only be invoked the first time HttpClientFactory.CreateClient("Named") is called. In our case, this would mean that we are configuring as many "Named" clients as there are routes. This is not quite what we are looking for.
  • Second point, the use of resources. It seems that HttpMessageInvoker is potentially less resource-intensive than HttpClient. Of course, HttpMessageInvoker is not returned by HttpClientFactory and worse, if we force the cast, then we could face major performance problems.
    See: HttpClient vs HttpMessageInvoker buffering issue microsoft/reverse-proxy#458

I conclude that we must implement our own pool mechanism...
Thus, I propose a first draft of a solution using solutions borrowed from StackExchange and Microsoft. It will obviously have to be tested in detail, but it serves as a basis for discussion.

Proposed Changes

  • Using HttpMessageInvoker instead of HttpClient
  • Using SocketsHttpHandler, introducing a new parameter PooledConnectionLifetime in HttpHandlerOptions
  • Proposing an implementation of IMessageInvokerPool, the pool of message invokers, inspired by StackExchange implementation
  • Removing redundant classes used in Ocelot (HttpClientBuilder, HttpClientWrapper and others)

I will provide Acceptance and Unit tests later. You should @raman-m, @RaynaldM have a look at this draft. Thanks

payloadName Mean Max Op/s Allocated
1KBPayload.json 591.3 us 648.3 us 1,691.1792 39.78 KB
16KBPayload.json 642.4 us 664.7 us 1,556.7119 82.36 KB
32KBPayload.json 650.1 us 675.2 us 1,538.2294 192.68 KB
64KBPayload.json 827.9 us 841.7 us 1,207.8715 350.25 KB
128KBPayload.json 997.5 us 1,018.1 us 1,002.5522 668.68 KB
256KBPayload.json 1,424.4 us 1,461.5 us 702.0456 1303.03 KB
512KBPayload.json 2,323.8 us 2,466.2 us 430.3273 2568.97 KB
2048KBPayload.json 8,299.8 us 8,570.5 us 120.4855 10171.14 KB
8192KBPayload.json 24,469.6 us 25,469.2 us 40.867 40619.62 KB
10MBPayload.dat 29,709.8 us 32,654.6 us 33.6589 42708.54 KB
15360KBPayload.json 54,608.4 us 70,385.4 us 18.3122 47989.27 KB
30720KBPayload.json 101,482.4 us 114,551.1 us 9.8539 95953.63 KB
100MBPayload.dat 381,869.0 us 419,428.2 us 2.6187 362479 KB
1024MBPayload.dat 3,300,482.0 us 3,444,481.6 us 0.303 5193381.84 KB

Table 1: Performance with current http client

payloadName Mean Max Op/s Allocated
1KBPayload.json 579.6 us 588.2 us 1,725.2972 39.26 KB
16KBPayload.json 619.0 us 666.1 us 1,615.4381 66.49 KB
32KBPayload.json 661.8 us 670.4 us 1,511.0611 160.79 KB
64KBPayload.json 856.2 us 888.3 us 1,167.9673 286.51 KB
128KBPayload.json 886.4 us 901.6 us 1,128.1035 540.97 KB
256KBPayload.json 1,478.0 us 1,503.4 us 676.5674 1047.99 KB
512KBPayload.json 2,539.9 us 2,570.8 us 393.713 2061.79 KB
2048KBPayload.json 8,232.4 us 9,159.4 us 121.4709 8128.46 KB
8192KBPayload.json 25,601.3 us 26,334.2 us 39.0605 32306.69 KB
10MBPayload.dat 26,567.3 us 27,575.2 us 37.6403 32300.54 KB
15360KBPayload.json 29,534.4 us 31,930.2 us 33.8588 32361.59 KB
30720KBPayload.json 100,626.1 us 103,409.9 us 9.9378 64746.37 KB
100MBPayload.dat 330,750.7 us 351,597.2 us 3.0234 258272.69 KB
1024MBPayload.dat 3,441,790.4 us 3,559,791.2 us 0.2905 4124999.17 KB

Table 2: Performance with the custom message invoker pool

@ggnaegi ggnaegi changed the title Custom HttpMessageInvoker pooling #356 Custom HttpMessageInvoker pooling Nov 30, 2023
@raman-m raman-m linked an issue Nov 30, 2023 that may be closed by this pull request
@raman-m raman-m added feature A new feature needs feedback Issue is waiting on feedback before acceptance 2023 Annual 2023 release labels Nov 30, 2023
@raman-m
Copy link
Member

raman-m commented Nov 30, 2023

@ggnaegi Wow! So great thing... 😍 You are real coding locomotive, Gui! 😉

FYI... Delivery after #1724 , so these both PRs cannot be in the same release because of Prod testing.

@raman-m
Copy link
Member

raman-m commented Dec 4, 2023

Guys, Let's return to this awesome PR after Nov'23 release.. 🆗 ❔

@raman-m raman-m added this to the December'23 milestone Dec 4, 2023
@ggnaegi
Copy link
Member Author

ggnaegi commented Dec 11, 2023

@raman-m @RaynaldM I'm writing the test cases at the minute. No worries guys! I realized we can still simplify the logic, since the Downstreamroute is an immutable, no need to use the timeout as key parameter, the object reference is good enough. We might have problems in the future if allowing dynamic configuration though (1: empty the pool, then 2: load the new configuration).

@ggnaegi
Copy link
Member Author

ggnaegi commented Dec 11, 2023

@raman-m @RaynaldM I'm writing the test cases at the minute. No worries guys! I realized we can still simplify the logic, since the Downstreamroute is an immutable, no need to use the timeout as key parameter, the object reference is good enough. We might have problems in the future if allowing dynamic configuration though (1: empty the pool, then 2: load the new configuration).

By the way, why not use the Record type instead?

@RaynaldM
Copy link
Collaborator

@raman-m @RaynaldM I'm writing the test cases at the minute. No worries guys! I realized we can still simplify the logic, since the Downstreamroute is an immutable, no need to use the timeout as key parameter, the object reference is good enough. We might have problems in the future if allowing dynamic configuration though (1: empty the pool, then 2: load the new configuration).

By the way, why not use the Record type instead?

It is a good idea

@ggnaegi ggnaegi force-pushed the features/http_client_pool branch from 4d56fa2 to 8a62fba Compare December 15, 2023 11:09
@raman-m raman-m added the high High priority label Dec 16, 2023
@raman-m
Copy link
Member

raman-m commented Dec 16, 2023

👍

@ggnaegi
Copy link
Member Author

ggnaegi commented Dec 17, 2023

Hello @raman-m and @RaynaldM I'm providing some benchmarks, it seems the memory leak has been addressed

@ggnaegi
Copy link
Member Author

ggnaegi commented Dec 18, 2023

@ggnaegi
Copy link
Member Author

ggnaegi commented Jan 17, 2024

@raman-m so, I have added a memory usage acceptance test in ContentTests. I have tried it with the current develop, and we can see a memory increase of about the payload size. In this PR, it is kept within a range of +/- 10 Mb

@raman-m but we should be careful and foresee some field tests when merged to develop, since the refactoring is quite aggressive....

@raman-m raman-m changed the title #356 #695 #1924 Custom HttpMessageInvoker pooling #356 #695 #1924 Custom HttpMessageInvoker pooling Jan 17, 2024
@raman-m
Copy link
Member

raman-m commented Jan 17, 2024

@raman-m but we should be careful and foresee some field tests when merged to develop, since the refactoring is quite aggressive....

@ggnaegi Yes, it is.
Sorry, I don't understand... What are "some field tests"?

Merging was done by you a couple of hours ago. I'll just press the button, there will be no merge conflicts with develop branch too.
So, what's the rest of work?

@ggnaegi
Copy link
Member Author

ggnaegi commented Jan 17, 2024

@raman-m but we should be careful and foresee some field tests when merged to develop, since the refactoring is quite aggressive....

@ggnaegi Yes, it is. Sorry, I don't understand... What are "some field tests"?

Merging from develop was done by you a couple of hours ago. I'll just press the button, there will be no merge conflicts.

@raman-m I mean, wait on feedbacks from @RaynaldM and others before moving to release and create the packages ;-)

@raman-m
Copy link
Member

raman-m commented Jan 17, 2024

Ha, making packages... No! I see you are so hot to release 🤣
There is the rest of 2 other features in the milestone.
Ok, make sense to wait for Ray's feedback...

@raman-m
Copy link
Member

raman-m commented Jan 18, 2024

Ready to merge! ✔️

@ggnaegi @RaynaldM
Let me know if you agree with merging.

Copy link
Member

@raman-m raman-m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent job, Gui! 🤩

@raman-m raman-m merged commit 5e7e76b into ThreeMammals:develop Jan 18, 2024
2 checks passed
@IamMartinPeek
Copy link

Hey guys, just asking. When will this be released ? Would need that for my project 👍

@ggnaegi
Copy link
Member Author

ggnaegi commented Feb 2, 2024

Hey guys, just asking. When will this be released ? Would need that for my project 👍

Hello, we have validated the behavior on production environments. It's looking good. we will finalize the release process in the next few days.

@raman-m
Copy link
Member

raman-m commented Feb 11, 2024

@IamMartinPeek Soon... It will be released, I hope, on Monday, Feb 12...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature A new feature high High priority Nov'23 November 2023 release
Projects
None yet
4 participants