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/delegatingresolver: avoid proxy if networktype of target address is not tcp #8215

Open
wants to merge 11 commits into
base: master
Choose a base branch
from

Conversation

eshitachandwani
Copy link
Member

@eshitachandwani eshitachandwani commented Apr 2, 2025

Fixes: #8207

RELEASE NOTES:

  • Proxy connections are no longer attempted for targets with non-TCP network types.

@eshitachandwani eshitachandwani added this to the 1.72 Release milestone Apr 2, 2025
@eshitachandwani eshitachandwani added the Area: Resolvers/Balancers Includes LB policy & NR APIs, resolver/balancer/picker wrappers, LB policy impls and utilities. label Apr 2, 2025
Copy link

codecov bot commented Apr 2, 2025

Codecov Report

Attention: Patch coverage is 74.13793% with 15 lines in your changes missing coverage. Please review.

Project coverage is 82.16%. Comparing base (5edab9e) to head (d254b04).
Report is 12 commits behind head on master.

Files with missing lines Patch % Lines
.../resolver/delegatingresolver/delegatingresolver.go 74.13% 13 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #8215      +/-   ##
==========================================
- Coverage   82.17%   82.16%   -0.02%     
==========================================
  Files         410      412       +2     
  Lines       40236    40536     +300     
==========================================
+ Hits        33065    33306     +241     
- Misses       5822     5863      +41     
- Partials     1349     1367      +18     
Files with missing lines Coverage Δ
.../resolver/delegatingresolver/delegatingresolver.go 79.11% <74.13%> (-3.12%) ⬇️

... and 45 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment on lines 208 to 212
if len(curState.Endpoints) != 0 {
networkType, ok = networktype.Get(curState.Endpoints[0].Addresses[0])
} else {
networkType, ok = networktype.Get(curState.Addresses[0])
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Here we're making an assumption that the network type of the first address of the endpoint is the same the address type of all remaining addresses. This may be true for most real-world usecases, but it isn't a requirement specified in the resolver API. It should not be too difficult to avoid proxying on a per-address basis. There are two places below where we do the following, one for resolver.State.Addresses and once for resolver.State.Endpoints:

proxyattributes.Set(proxyAddr, proxyattributes.Options{
	User:        r.proxyURL.User,
	ConnectAddr: targetAddr.Addr,
})

We can refactor this into a method that takes in the target address and proxy address and returns the combined address.

fn (r *delegatingResolver) combineAddresses(targetAddr, proxyAddr resolver.Address) resolver.Address {
    if  networktype.Get(targetAddr) != "tcp" {
        return targetAddr
    }
    return proxyattributes.Set(proxyAddr, proxyattributes.Options{
	User:        r.proxyURL.User,
	ConnectAddr: targetAddr.Addr,
    })
}

Copy link
Contributor

Choose a reason for hiding this comment

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

The above comment end up adding duplicate addresses in the loop on targetResolverState.Endpoints. We will probably need to invert the loop on r.proxyAddrs and endpt.Addresses and break early:

for _, endpt := range (*r.targetResolverState).Endpoints {
	var addrs []resolver.Address
	for _, targetAddr := range endpt.Addresses {
                 if  networktype.Get(targetAddr) != "tcp" {
                          addrs = append(addrs, targetAddr)
                          continue
                  }
                  for _, proxyAddr := range r.proxyAddrs { 
			addrs = append(addrs, proxyattributes.Set(proxyAddr, proxyattributes.Options{
				User:        r.proxyURL.User,
				ConnectAddr: targetAddr.Addr,
			}))
		}
	}
	endpoints = append(endpoints, resolver.Endpoint{Addresses: addrs})
}

Copy link
Member Author

Choose a reason for hiding this comment

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

Right, done ! Also added a check in beginning , if there is not address with network type TCP , we do not wait for proxy update and just update the cc state.

@arjan-bal
Copy link
Contributor

Please also fix the format of the release notes.

Comment on lines 208 to 211
if networkType, ok := networktype.Get(addr); !ok || networkType == "tcp" {
isTCP = true
break
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Out here we're assuming !ok to be the same of tcp. However, the old code in the dialer used to parse the address string when !ok to determine the network type, we should probably maintain the same behaviour.

if !ok {
networkType, address = parseDialTarget(address)
}
if networkType == "tcp" && useProxy {
return proxyDial(ctx, address, grpcUA)
}

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 216 to 219
if networkType, ok := networktype.Get(addr); !ok || networkType == "tcp" || isTCP {
isTCP = true
break
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the code will be simpler if we refactor finding a TCP address in a helper function. The helper function can return early instead of keeping track of a isTCP boolean and breaking out of nested loops.

Copy link
Member Author

Choose a reason for hiding this comment

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

Right! Done!

Comment on lines 252 to 257
} else {
addresses = append(addresses, proxyattributes.Set(proxyAddr, proxyattributes.Options{
User: r.proxyURL.User,
ConnectAddr: targetAddr.Addr,
}))
}
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: You can use continue in the if block to avoid indenting the else block.

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 274 to 281
} else {
for _, proxyAddr := range r.proxyAddrs {
addrs = append(addrs, proxyattributes.Set(proxyAddr, proxyattributes.Options{
User: r.proxyURL.User,
ConnectAddr: targetAddr.Addr,
}))
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: You can use continue in the if block to avoid indenting the else block.

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.

// updateClientConnStateLocked creates a list of combined addresses by
// pairing each proxy address with every target address. For each pair, it
// generates a new [resolver.Address] using the proxy address, and adding the
// target address as the attribute along with user info. It returns nil if
// either resolver has not sent update even once and returns the error from
// ClientConn update once both resolvers have sent update atleast once.
func (r *delegatingResolver) updateClientConnStateLocked() error {
if r.targetResolverState == nil || r.proxyAddrs == nil {
if r.targetResolverState == nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

If r.proxyAddrs == nil is no longer in this conditional, does it mean that we can have a case where the proxy is configured, but we don't end up waiting for the resolved proxy addresses here?

return nil
}

curState := *r.targetResolverState

// If no addresses returned by resolver have network type as tcp , do not wait for proxy update.
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Please wrap comment lines at 80-cols. See: go/go-style/decisions#comment-line-length

// updateClientConnStateLocked creates a list of combined addresses by
// pairing each proxy address with every target address. For each pair, it
// generates a new [resolver.Address] using the proxy address, and adding the
// target address as the attribute along with user info. It returns nil if
// either resolver has not sent update even once and returns the error from
// ClientConn update once both resolvers have sent update atleast once.
func (r *delegatingResolver) updateClientConnStateLocked() error {
if r.targetResolverState == nil || r.proxyAddrs == nil {
if r.targetResolverState == nil {
return nil
}

curState := *r.targetResolverState
Copy link
Contributor

Choose a reason for hiding this comment

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

Even though we have this local variable curState, I see that the code (both existing and newly added) also uses r.targetResolverState in multiple places. Can we make it consistent and use only one of them.

return r.cc.UpdateState(curState)
}

if r.proxyAddrs == nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, I see that the previous check is moved here.

}

// Verify that the state clientconn receives is same as updated by target resolver,
// since we want to avoid proxy for any network type aprt from tcp.
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: typo "aprt"

Comment on lines +813 to +815
case <-time.After(defaultTestShortTimeout):
t.Fatalf("Delegating resolver did not call update state")
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this be racy, especially on some slow GitHub action machines? We generally use defaultTestShortTimeout for things that we expect to not happen. We do expect a state update here. So, I would rather use defaultTestTimeout here and validate the state received on stateCh.

}
}

// Tests the scenario where a proxy is configured, and the resolver returns addresses with varied
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Same here. Please wrap comments at 80-cols. Please figure out a way to configure your editor to do this for you.

const (
targetTestAddr = "test.target"
resolvedTargetTestAddr1 = "1.1.1.1:8080"
resolvedTargetTestAddr2 = "2.2.2.2:8080"
Copy link
Contributor

Choose a reason for hiding this comment

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

This is not used. Please delete.

Comment on lines +851 to +852
resolvedProxyTestAddr1 = "11.11.11.11:7687"
resolvedProxyTestAddr2 = "22.22.22.22:7687"
Copy link
Contributor

Choose a reason for hiding this comment

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

These two are not used. Please delete.

@easwars easwars assigned eshitachandwani and unassigned easwars and arjan-bal Apr 11, 2025
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.

grpc.Dial fails to connect to unix domain socket when https_proxy is set
3 participants