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

internal/resolver: introduce a new delegating resolver to handle both target URI and proxy address resolution #7857

Merged
merged 75 commits into from
Dec 23, 2024

Conversation

eshitachandwani
Copy link
Member

@eshitachandwani eshitachandwani commented Nov 20, 2024

As part of fixing #7556 , we create a new delegating resolver that that handles proxy configuration and delegates to child resolvers for target and proxy address resolution and does the following:

  • Use the "dns" resolver for proxy address resolution if environment variables are set.
  • Use the target's "path" portion directly if the scheme is "dns" and a proxy is configured and target resolution on client is not enabled, mimicking the old "passthrough" behavior.
  • Combine the resolved target and proxy addresses, setting the proxy CONNECT string attribute.

Coping the context verbatim form the issue:
Background :
Before grpc.NewClient was introduced, grpc.Dial was the primary way to establish gRPC connections. With grpc.Dial, the default "passthrough" resolver simply passed the target address unchanged to the transport layer. When grpc.NewClient was introduced, it switched to the "dns" resolver by default, which resolves the target address to IP addresses before passing them to the transport. This change inadvertently altered how proxies are handled when configured via environment variables. This behavior is inconsistent with what we expect i.e resolution to happen on proxy.

The complete proposal :

  • Remove environment variable detection from the transport layer. Instead, introduce a per-address attribute to specify the proxy CONNECT string. This change aligns with the xDS design and provides more control over proxy behavior.
  • Create a new resolver that handles proxy configuration and delegates to child resolvers for target and proxy address resolution. This resolver does operations stated above
  • Introduce a new DialOption to preserve the current behavior of grpc.Dial, where the resolved target address is used in the proxy CONNECT request. This option ensures backward compatibility for users relying on the existing behavior.

RELEASE NOTES: N/A

@eshitachandwani eshitachandwani added the Area: Resolvers/Balancers Includes LB policy & NR APIs, resolver/balancer/picker wrappers, LB policy impls and utilities. label Nov 20, 2024
@eshitachandwani eshitachandwani added this to the 1.69 Release milestone Nov 20, 2024
Copy link

codecov bot commented Nov 20, 2024

Codecov Report

Attention: Patch coverage is 70.28571% with 52 lines in your changes missing coverage. Please review.

Project coverage is 82.03%. Comparing base (e5a4eb0) to head (fc2c41f).
Report is 5 commits behind head on master.

Files with missing lines Patch % Lines
.../resolver/delegatingresolver/delegatingresolver.go 68.86% 40 Missing and 12 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #7857      +/-   ##
==========================================
- Coverage   82.21%   82.03%   -0.19%     
==========================================
  Files         379      381       +2     
  Lines       38261    38535     +274     
==========================================
+ Hits        31458    31612     +154     
- Misses       5514     5603      +89     
- Partials     1289     1320      +31     
Files with missing lines Coverage Δ
internal/proxyattributes/proxyattributes.go 100.00% <100.00%> (ø)
.../resolver/delegatingresolver/delegatingresolver.go 68.86% <68.86%> (ø)

... and 27 files with indirect coverage changes

@purnesh42H purnesh42H changed the title resolver/delegatingresolver: default resolver for proxy support internal/revolver: add a new delegating resolver as default resolver for proxy support Nov 21, 2024
@purnesh42H purnesh42H changed the title internal/revolver: add a new delegating resolver as default resolver for proxy support internal/resolver: add a new delegating resolver as default resolver for proxy support Nov 21, 2024
Copy link
Contributor

@purnesh42H purnesh42H left a comment

Choose a reason for hiding this comment

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

Some style and documentation comments on non test code in the first pass. Will review the test code for same next.

internal/envconfig/envconfig.go Outdated Show resolved Hide resolved
internal/envconfig/envconfig.go Outdated Show resolved Hide resolved
internal/resolver/delegatingresolver/delegatingresolver.go Outdated Show resolved Hide resolved
internal/resolver/delegatingresolver/delegatingresolver.go Outdated Show resolved Hide resolved
internal/resolver/delegatingresolver/delegatingresolver.go Outdated Show resolved Hide resolved
internal/resolver/delegatingresolver/delegatingresolver.go Outdated Show resolved Hide resolved
internal/resolver/delegatingresolver/delegatingresolver.go Outdated Show resolved Hide resolved
internal/resolver/delegatingresolver/delegatingresolver.go Outdated Show resolved Hide resolved
internal/resolver/delegatingresolver/delegatingresolver.go Outdated Show resolved Hide resolved
@arjan-bal arjan-bal removed their assignment Dec 18, 2024
}

if r.targetResolver == nil {
r.targetResolver = nopResolver{}
Copy link
Contributor

Choose a reason for hiding this comment

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

Just FYI, don't change this: An optimization for using such stateless no-op implementations is to have a single object of the struct that everyone uses. This saves allocating memory for multiple objects. Here it wouldn't help much.

Copy link
Member

Choose a reason for hiding this comment

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

FYI, empty structs are allocated specially, so if it's an empty struct it will not save memory to have a global instance of it. See this example:

https://go.dev/play/p/LmiMfy_XNP4

Running it (for me, right now) prints 0x574380 0x574380 -- that it matches shows there aren't any wasted allocations.

Not for the struct itself, at least. But if the struct is stored in an interface field, like it is here, then that interface has to be allocated to hold the struct's type + pointer. But this level of optimization also isn't important for us. :)

Copy link
Member Author

Choose a reason for hiding this comment

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

Noted.

internal/resolver/delegatingresolver/delegatingresolver.go Outdated Show resolved Hide resolved
internal/resolver/delegatingresolver/delegatingresolver.go Outdated Show resolved Hide resolved
internal/resolver/delegatingresolver/delegatingresolver.go Outdated Show resolved Hide resolved
internal/resolver/delegatingresolver/delegatingresolver.go Outdated Show resolved Hide resolved
internal/resolver/delegatingresolver/delegatingresolver.go Outdated Show resolved Hide resolved
Copy link
Contributor

@arjan-bal arjan-bal left a comment

Choose a reason for hiding this comment

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

Some minor style comment, otherwise LGTM!


var (
logger = grpclog.Component("delegating-resolver")
//HTTPSProxyFromEnvironment will be overwritten in the tests
Copy link
Contributor

@arjan-bal arjan-bal Dec 19, 2024

Choose a reason for hiding this comment

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

nit: Space missing after //.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

Comment on lines 121 to 123
r.targetResolverState.Addresses = []resolver.Address{{Addr: target.Endpoint()}}
r.targetResolverState.Endpoints = []resolver.Endpoint{{Addresses: []resolver.Address{{Addr: target.Endpoint()}}}}
r.targetResolverReady = true
Copy link
Contributor

@arjan-bal arjan-bal Dec 19, 2024

Choose a reason for hiding this comment

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

Not actionable: This is essentially the same as using passthrough as the target resolver. If we need to avoid sending the dial option to the delegating resolver, the channel could send passthrough as the target resolver to get the same effect.

// the delegating resolver creates only a target resolver and that the
// addresses returned by the delegating resolver exactly match those returned
// by the target resolver.
func (s) TestDelegatingResolverNoProxyEnvVarsSet(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

The package name is delegatingresolver_test, we don't need to add DelegatingResolver to all the test functions.

https://google.github.io/styleguide/go/decisions#package-vs-exported-symbol-name

Copy link
Contributor

Choose a reason for hiding this comment

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

Ignore my previous comment. The test name can be used to filter tests in subdirectories, so having the TestDelegatingResolver prefix is helpful.

Comment on lines 57 to 58
targetResolverReady bool // indicates if an update from the target resolver has been received
proxyResolverReady bool // indicates if an update from the proxy resolver has been received
Copy link
Member

Choose a reason for hiding this comment

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

Consider removing these and using nil for targetResolverState and nil for proxyAddrs to indicate that the state/addresses are "valid".

Having two different pieces of state that represent parts of the same thing should be avoided. I.e. what does it mean if "!targetResolverReady && len(targetResolverState.Endpoints) > 0"? That's illegal. So you can couple the two things into one field and avoid such illegal state combinations.

Copy link
Member Author

Choose a reason for hiding this comment

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

Changed the condition as well as change the field targetResolverState to a pointer *resolver.State to facilitate differentiation in a no update and an empty update from the target resolver in the condition. Because as per the flow, we want load balancer to handle an empty update.

internal/resolver/delegatingresolver/delegatingresolver.go Outdated Show resolved Hide resolved
Copy link
Member

@dfawley dfawley left a comment

Choose a reason for hiding this comment

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

LGTM pending the two small changes requested.

// curState contains all necessary information when passed to UpdateState.
// The state update is only sent after both the target and proxy resolvers
// have sent their updates, and curState has been updated with the combined
// addresses.
curState := *r.targetResolverState
Copy link
Member

Choose a reason for hiding this comment

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

Please move this down to where you commented about it.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

@@ -259,12 +254,17 @@ func (r *delegatingResolver) updateProxyResolverState(state resolver.State) erro
logger.Infof("Addresses received from proxy resolver: %s", state.Addresses)
}
if len(state.Endpoints) > 0 {

Copy link
Member

Choose a reason for hiding this comment

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

Please remove this blank line

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

@dfawley dfawley removed their assignment Dec 20, 2024
@arjan-bal arjan-bal merged commit 063d352 into grpc:master Dec 23, 2024
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: Resolvers/Balancers Includes LB policy & NR APIs, resolver/balancer/picker wrappers, LB policy impls and utilities. Type: Bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants