-
Notifications
You must be signed in to change notification settings - Fork 4.4k
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
Conversation
Codecov ReportAttention: Patch coverage is
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
|
There was a problem hiding this 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.
} | ||
|
||
if r.targetResolver == nil { | ||
r.targetResolver = nopResolver{} |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Noted.
There was a problem hiding this 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Space missing after //
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
r.targetResolverState.Addresses = []resolver.Address{{Addr: target.Endpoint()}} | ||
r.targetResolverState.Endpoints = []resolver.Endpoint{{Addresses: []resolver.Address{{Addr: target.Endpoint()}}}} | ||
r.targetResolverReady = true |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
internal/resolver/delegatingresolver/delegatingresolver_ext_test.go
Outdated
Show resolved
Hide resolved
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this 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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this comment.
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 { | |||
|
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
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:
Coping the context verbatim form the issue:
Background :
Before
grpc.NewClient
was introduced,grpc.Dial
was the primary way to establish gRPC connections. Withgrpc.Dial
, the default "passthrough" resolver simply passed the target address unchanged to the transport layer. Whengrpc.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 :
RELEASE NOTES: N/A