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

#977 Enable validation of DI scopes in Kube and PollKube discovery providers #2180

Merged

Conversation

kick2nick
Copy link
Contributor

@kick2nick kick2nick commented Oct 21, 2024

Fixes #977

Proposed Changes

  • Added unit tests for verifying that Kube & PollKube providers can be resolved from DI with enabled scope validation.
  • Fixed Kubernetes acceptance tests. Used registered by Ocelot extension KubeApiClient instead of manually created one. Enabled scope validation for ServiceProvider
  • Made IKubeApiClient singleton lifetime explicit.

Successor

Docs

@kick2nick
Copy link
Contributor Author

@raman-m,
please see #977 (comment)

@raman-m raman-m added Service Discovery Ocelot feature: Service Discovery hotfix Gitflow: Hotfix issue, PR related to hotfix branch Kubernetes Service discovery by Kubernetes Oct'24 October 2024 release labels Oct 22, 2024
@raman-m raman-m added this to the October'24 milestone Oct 22, 2024
@raman-m raman-m changed the title #977 Fixed Kube/PollKube service provider scope validation #977 Enable validation of DI scopes in Kube and PollKube service providers Oct 22, 2024
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.

  1. I'm not as confident as you; the proposed changes seem to be breaking ones.
  2. There's no acceptance test for the original user scenario in bug Cannot resolve scoped service KubeClient.IKubeApiClient from root provider #977.
    In BDD, it's quite clear that we should test it first before any actual refactoring.
  3. Why were so many changes proposed for a minor scope validation change? I don't understand.

I'm convinced that scope validation can be implemented with less code.

@@ -9,10 +11,23 @@ public static class OcelotBuilderExtensions
public static IOcelotBuilder AddKubernetes(this IOcelotBuilder builder, bool usePodServiceAccount = true)
{
builder.Services
.AddKubeClient(usePodServiceAccount)
.AddSingleton<IKubeApiClient, KubeApiClient>(KubeApiClientFactory)
Copy link
Member

Choose a reason for hiding this comment

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

It appears to be a breaking change 🤯 Lovely singletons once more❗
The main purpose of the Ocelot.Provider.Kubernetes NuGet package is to facilitate the integration of the KubeClient package with Ocelot. The proposed changes undermine this objective by removing AddKubeClient. This design direction is flawed because our solutions should be developed based on the official KubeClient package's functionality, not by altering the library's behavior to suit our needs!
You have to understand that the issue is not AddKubeClient extension, but singleton factory aka KubernetesProviderFactory.Get with bad scope management!

Copy link
Contributor Author

@kick2nick kick2nick Oct 22, 2024

Choose a reason for hiding this comment

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

It appears to be a breaking change 🤯

Why is it a breaking change?
✔️ Public API hasn't changed
✔️ IKubeApiClient has the same singleton lifetime as it had before this fix(implicitly promoted)
Proposed change - make IKubeApiClient singleton lifetime explicit by registering it as singleton.

our solutions should be developed based on the official KubeClient package's functionality, not by altering the library's behavior to suit our needs!

KubeClient package's functionality - talking to kube api as httpclient wrapper. I don't understand why it's lifetime can't be managed by Ocelot.Provider.Kubernetes. (like it was done for consul).

@raman-m raman-m requested review from RaynaldM and ggnaegi October 22, 2024 09:58
@raman-m
Copy link
Member

raman-m commented Oct 22, 2024

@kick2nick commented on Oct 21

Initially, I was uncertain if the provider.CreateScope() approach was ideal for our needs until I saw the HttpContext.RequestServices in PR #2179. To be honest, I'm not concerned with Sergio's solutions as they addressed issues in an older version of Ocelot, which may not be applicable to the current versions. We should consider your suggestions only if there is a consensus within the team.

I have some doubts about this approach:

  • HttpClient MessageHandlers won't be reused - KubeClient creates new one for each KubeApiClient instance, that can cause socket exhaustion issues on heavy loads.

Excellent! How did you validate this hypothesis? Theoretical assumptions differ from empirical testing. Additionally, as previously mentioned in this message, we will pursue alternative solutions, setting aside my provider.CreateScope() design recommendation, as it appears there are superior design options based on HttpContext.

  • This scope is separate from request HttpContext scope and no services would be potentially reused.

Using HttpContext.RequestServices seems to be the appropriate method, though the consequences for the PollKube provider are still unclear to me.

  • There is no option to use long polling with this approach, HttpClient will be disposed with scope
    So my opinion is better leave KubeApiClient lifetime as is (it's now promoted to singleton), but make it explicit by registering it as singleton.

If it's not implemented, why are we discussing it? You're correct to assume that we might encounter issues with disposed objects. Couldn't this be managed by HttpContext? I'm quite confident that using HttpContext.RequestServices would prevent any disposed objects, eliminating the need to manage scope lifetime.

But if you insist on Sergio's approach than no problem, I'll implement it :)

Please disregard the previous approach for Sergio; we will devise an alternative solution. I find these debates to be unproductive.

@kick2nick
Copy link
Contributor Author

kick2nick commented Oct 22, 2024

until I saw the HttpContext.RequestServices in PR #2179.

There you are resolving IConsulServiceBuilder once from the scope of the first http request to downstream service and then cache it along with loadBalancer. IConsulServiceBuilder is not IDisposable and it works (but you don't have truly scoped IConsulServiceBuilder ).
The MS doc states →

The framework creates a scope per request, and RequestServices exposes the scoped service provider. All scoped services are valid for as long as the request is active.


Using HttpContext.RequestServices seems to be the appropriate method, though the consequences for the PollKube provider are still unclear to me.

Usual Kube won't work if IKubeApiClientFactory is resolved from HttpContext.RequestServices inside KubernetesProviderFactory.Get because HttpClient will be disposed(like in example). We have another option - inject IHttpContextAccessor into Kube and on each request resolve IKubeApiClient, but for PollKube it won't work (it calls Kube on timer ticks without HttpContext).

Excellent! How did you validate this hypothesis? Theoretical assumptions differ from empirical testing.

There is whole concept - HttpClientFactory that adresses this issues. Please read first 2 paragraphs.

@raman-m raman-m changed the title #977 Enable validation of DI scopes in Kube and PollKube service providers #977 Enable validation of DI scopes in Kube and PollKube discovery providers Oct 23, 2024
@raman-m
Copy link
Member

raman-m commented Oct 23, 2024

What's up, man? Are you online?

@kick2nick
Copy link
Contributor Author

What's up, man? Are you online?

Yes, I'm here. Fixed review comments, pointed why HttpContext.RequestServices can't be used. Anything else?

@kick2nick
Copy link
Contributor Author

Couldn't this be managed by HttpContext? I'm quite confident that using HttpContext.RequestServices would prevent any disposed objects, eliminating the need to manage scope lifetime.

No! Because IKubeApiClient will be resolved once, stored inside Kube singleton, and then disposed after HttpContext gone. Provided test as proof.
kick2nick@bc2f7e6
results:
image

@raman-m
Copy link
Member

raman-m commented Oct 23, 2024

Yes, I'm here. Fixed review comments, pointed why HttpContext.RequestServices can't be used.

Well done, my astute developer! I concur, HttpContext.RequestServices is not suitable for Kube, and your evidence supports this. I'm weary of these debates as well. It seems the upcoming debate session will be unproductive.

Why suggest refactoring everything for a minor issue that throws an InvalidOperationException with the message Cannot resolve scoped service 'KubeClient.IKubeApiClient' from root provider?

I've asked you:

I'm convinced that scope validation can be implemented with less code.

You didn't respond but instead advocated for your ideal solution, which I believe is commendable and, in my view, should have been implemented 3-5 years ago, utilizing a design based on singleton factories. Writing less code is possible by creating a fake scope and using it during the operation of reading the list of services, as suggested here, but sure you didn't like it. This approach is quite straightforward and minimalistic. However, sometimes proposing ideal solutions can be a waste of time. As I have previously mentioned, both Consul and Kube classes will be transitioned to DI converting them to IServiceDiscoveryProvider objects, and the ServiceDiscoveryFinderDelegate will be eliminated forever.

Anything else?

Code review has done.
Please halt any further development at this point; I will proceed with the final review of the PR...

@raman-m
Copy link
Member

raman-m commented Oct 24, 2024

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.

Ready for delivery ✅

@raman-m raman-m merged commit 8b633af into ThreeMammals:develop Oct 24, 2024
1 check passed
@raman-m
Copy link
Member

raman-m commented Oct 24, 2024

@kick2nick Nikolay,
Thank you for your contribution! 💪

raman-m added a commit that referenced this pull request Oct 25, 2024
* OcelotBuilderExtensionsTests for Eureka SD

* BuildServiceProvider(true)
#2179 (comment)

* Add TestHostBuilder with enabled scopes validation by default across all testing projects
#2179 (comment)

* Update Sample projects with force scopes validation.
Add Ocelot.Samples.Web project.
Add OcelotHostBuilder and DownstreamHostBuilder helpers.
Add Ocelot.Samples.Web project reference to all samples projects.
TargetFramework is `net8.0` only.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
hotfix Gitflow: Hotfix issue, PR related to hotfix branch Kubernetes Service discovery by Kubernetes Oct'24 October 2024 release Service Discovery Ocelot feature: Service Discovery
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Cannot resolve scoped service KubeClient.IKubeApiClient from root provider
2 participants