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

chore: expose NewClient method to end users #7010

Merged
merged 17 commits into from Mar 7, 2024
Merged

Conversation

bruuuuuuuce
Copy link
Contributor

@bruuuuuuuce bruuuuuuuce commented Feb 28, 2024

This will allow users to create a connection, leaving it in idle state, rather than having it automatically try and connect in the background, which is the current behavior of Dial. Leaving the connection in idle state is more consistent with how other grpc implementations deal with connections

Looks like this change was already planned, but just has not been commited yet

grpc-go/clientconn.go

Lines 222 to 225 in 99ded5c

// Taking this approach of kicking it out of idle at the end of this method
// allows us to share the code between channel creation and exiting idle
// mode. This will also make it easy for us to switch to starting the
// channel off in idle, i.e. by making newClient exported.

RELEASE NOTES:

  • grpc: introduce grpc.NewClient to allow users to create new clients with connections remaining in idle state

This will allow users to create a connection, leaving it in idle state, rather than having it automatically try and connect in the background, which is the current behavior of `Dial`. Leaving the connection in idle state is more consistent with how other grpc implementations deal with connections
Copy link

linux-foundation-easycla bot commented Feb 28, 2024

CLA Signed

The committers listed above are authorized under a signed CLA.

Copy link

codecov bot commented Feb 28, 2024

Codecov Report

Merging #7010 (5938a72) into master (55341d7) will increase coverage by 0.00%.
The diff coverage is 81.25%.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #7010   +/-   ##
=======================================
  Coverage   82.43%   82.43%           
=======================================
  Files         296      296           
  Lines       31463    31471    +8     
=======================================
+ Hits        25937    25944    +7     
- Misses       4468     4470    +2     
+ Partials     1058     1057    -1     
Files Coverage Δ
credentials/google/xds.go 64.91% <100.00%> (ø)
dialoptions.go 88.88% <100.00%> (+0.04%) ⬆️
internal/xds/xds.go 100.00% <ø> (ø)
resolver/resolver.go 100.00% <100.00%> (+5.55%) ⬆️
xds/internal/balancer/clusterimpl/clusterimpl.go 86.73% <100.00%> (ø)
clientconn.go 90.90% <70.00%> (-1.75%) ⬇️

... and 15 files with indirect coverage changes

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.

One more thing needs to be figured out before we can do this: we want the default name resolver used when calling NewClient directly by users to be dns instead of the awkward and non-standard passthru used by Dial.

However, the default is set globally, and can be overridden, so we need to come up with a way to have a different default for the two different paths, override them both when the user calls resolver.SetDefaultScheme, and have the proper code path use the proper one. (This is mentioned in #1786.)

Let me know if you're interested in taking that work on as well.

@bruuuuuuuce
Copy link
Contributor Author

Let me know if you're interested in taking that work on as well.

Sure I can take a stab at this

balancer/rls/config.go Outdated Show resolved Hide resolved
dialoptions.go Outdated Show resolved Hide resolved
resolver/resolver.go Outdated Show resolved Hide resolved
@bruuuuuuuce
Copy link
Contributor Author

@dfawley lmk if this was what you were thinking, also any naming suggestions would be appreciated 😅

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.

Thanks for continuing with this! I think it's probably pretty close, though I would like to massage it a bit to avoid making any user visible changes besides the NewClient function being added.

balancer/rls/config.go Outdated Show resolved Hide resolved
clientconn.go Outdated Show resolved Hide resolved
clientconn.go Outdated Show resolved Hide resolved
@bruuuuuuuce bruuuuuuuce requested a review from dfawley March 1, 2024 23:59
@dfawley dfawley self-assigned this Mar 4, 2024
clientconn.go Outdated Show resolved Hide resolved
clientconn_parsed_target_test.go Outdated Show resolved Hide resolved
resolver/resolver.go Outdated Show resolved Hide resolved
resolver/resolver.go Outdated Show resolved Hide resolved
resolver/resolver.go Show resolved Hide resolved
clientconn.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.

Thank you for the contribution! I have a few minor things, but I think we can also ask for a 2nd pass review now (2 approvals are required by policy).

@@ -191,6 +190,11 @@ func newClient(target string, opts ...DialOption) (conn *ClientConn, err error)
return cc, nil
}

// NewClient returns a new client in idle mode.
Copy link
Member

Choose a reason for hiding this comment

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

Just FYI / for the next reviewer's reference:

After this PR, I plan to rewrite things a little bit. I'd like this to become the primary API for users, and to call Dial and DialContext both "deprecated" in preference of this. As such, this will contain a bit more documentation, and the others will state they call it and then initiate and wait for a connection.

Choose a reason for hiding this comment

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

@dfawley ok now with the newest release both are deprecated. But how do I add a timeout?

When I try the follwing:

	conn, err := grpc.NewClient(addr,
		grpc.WithTransportCredentials(creds),
		grpc.WithBlock(),
		grpc.WithChainUnaryInterceptor(interceptors...),
		grpc.WithTimeout(dialTimeout),
	)

the linter tells me that grpc.WithTimeout is deprecated I should use grpc.DialContext (which is deprecated too). So how do I use grpc.NewClient with grpc.WithBlock that doesn't block forever?

Copy link
Member

Choose a reason for hiding this comment

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

Setting a timeout when creating a client is an anti-pattern, and is not possible in any other gRPC implementations.

See https://github.com/grpc/grpc-go/blob/master/Documentation/anti-patterns.md#dialing-in-grpc for more info.

Choose a reason for hiding this comment

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

I agree that connection failures should always be checked, not only on start-up.

But if you're working with setups such as Kubernetes, you don't want your new pod to pass liveness/readiness checks in case it has an invalid connection string. It's way safer to have this "ping" check to confirm there are no misconfigurations in your new deployment, before you terminate your previous pods.

Copy link
Member

Choose a reason for hiding this comment

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

There are other, better, more robust ways of doing that. For example, health checking: https://github.com/grpc/proposal/blob/master/A17-client-side-health-checking.md

You can also use the connectivity state API if you just want to see if the client was able to connect: https://pkg.go.dev/google.golang.org/grpc#ClientConn.GetState and https://pkg.go.dev/google.golang.org/grpc#ClientConn.WaitForStateChange

clientconn.go Outdated Show resolved Hide resolved
func (s) TestParsedTarget_Success_WithoutCustomDialer(t *testing.T) {
defScheme := resolver.GetDefaultScheme()
dialScheme := resolver.GetDefaultScheme()
Copy link
Member

Choose a reason for hiding this comment

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

I believe this should probably try to re-create the initial state in case another test calls SetDefaultScheme. I.e. do resolver.SetDefaultScheme("passthrough") and internal.UserSetDefaultScheme = false?

Comment on lines 66 to 67
// SetDefaultScheme sets the default scheme that will be used. The default
// default scheme is "passthrough".
// scheme is "passthrough".
Copy link
Member

Choose a reason for hiding this comment

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

"default default" was not actually a typo. But maybe would be clearer if it said "the default scheme is initially set to ..." or something like that.

Copy link
Member

@arvindbr8 arvindbr8 left a comment

Choose a reason for hiding this comment

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

Thanks for the PR. Looks good to me 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants