-
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
xdsclient: introduce pool to manage multiple xDS clients with same bootstrap content #7898
base: master
Are you sure you want to change the base?
xdsclient: introduce pool to manage multiple xDS clients with same bootstrap content #7898
Conversation
202847e
to
b561f1c
Compare
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #7898 +/- ##
==========================================
+ Coverage 82.05% 82.14% +0.09%
==========================================
Files 381 382 +1
Lines 38539 38558 +19
==========================================
+ Hits 31622 31674 +52
+ Misses 5602 5574 -28
+ Partials 1315 1310 -5
|
23023e9
to
12cb8a7
Compare
12cb8a7
to
8787929
Compare
8787929
to
aa7e498
Compare
var ( | ||
// DefaultPool is the default pool for xds clients. It is created at the | ||
// init time. | ||
DefaultPool *Pool |
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.
What would be the reason to export this DefaultPool
variable? Was it to make testing easier? I see most reference of this variable is within the xdsclient
package, with only one reference in xds/server_options.go
.
Would it be possible to test via Public APIs instead?
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.
yeah server options have to set and get config for testing. I think we can provide the set and get method for default pool but then its equivalent of exporting a variable.
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.
I agree with @danielzhaotongliu. We don't have to make this public. We should change the dial option and the server option to create a pool with the bootstrap config that was passed to it, and use that pool to create the xDS client. This is specified in the proposal in #7592 (comment)
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 coming back to this comment. I think we would have to export the default pool from the xdsclient
package or use the internal
package trick, because we want the xDS resolver and the xDS server to be able to use the default pool when they want to create xDS clients in non-test environments.
The internal
trick won't work once we move the xDS client out into a separate repo (as part of the generic xDS client work). So, we are only left with the option of exporting this.
Do you both have other ideas?
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.
Yes, I have kept the DefaultPool
exported for now. As you called out in other comment, we set the bootstrap config in DefaultPool
if env var is set with correct bootstrap config or contents. The resolver and server will have DefaultPool
set by default pool but we have options to override for testing.
@@ -46,7 +46,8 @@ const Scheme = "xds" | |||
|
|||
// newBuilderWithConfigForTesting creates a new xds resolver builder using a | |||
// specific xds bootstrap config, so tests can use multiple xds clients in | |||
// different ClientConns at the same time. | |||
// different ClientConns at the same time. It creates the new xds client int |
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.
typo/nit: "It creates a new xds client in the default pool with the provided config."
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
xds/internal/xdsclient/pool.go
Outdated
// expected to invoke once they are done using the client. It is safe for the | ||
// caller to invoke this close function multiple times. | ||
func (p *Pool) NewClient(name string) (XDSClient, func(), error) { | ||
config, err := bootstrap.GetConfiguration() |
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.
What would be the reason to not use the Pool.config
field xDS bootstrap configuration as that was the config passed in/parsed/created during the NewPool
function above?
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.
NewClient is for the prod path which actually reads the bootstrap config from env vars and will only deal with default pool.
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.
What about the use case where users ONLY set bootstrap config from the SetFallbackBootstrapConfig
API and do not set env var? For this case, when creating the xDS client, would it be created via this method? Would they not require p.config
instead since that is the field set from the implementation below (line 133-143)?
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.
This method must use the config
field inside the Pool
. There is no point in reading the bootstrap configuration from the env vars or the fallback bootstrap configuration. In fact, once we do that, we don't have a reason to have Pool.NewClientForTesting
. Tests could as well use pool.NewClient
.
What do you think?
NewClient is for the prod path which actually reads the bootstrap config from env vars and will only deal with default pool
The bootstrap env vars should be read when the default pool is created at init time. And if bootstrap env vars are not set, the default pool should have a nil
config and if NewClient
is called without calling SetFallbackBootstrapConfig
, i.e. config
is nil
when NewClient
is called, then NewClient
should fail with a useful error message.
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 bootstrap env vars should be read when the default pool is created at init time. And if bootstrap env vars are not set, the default pool should have a nil config and if NewClient is called without calling SetFallbackBootstrapConfig, i.e. config is nil when NewClient is called, then NewClient should fail with a useful error message.
Done
2ab7ba2
to
6e822a3
Compare
4e62279
to
43b394d
Compare
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.
I might not be familiar enough with the Github testing process for gRPC Go, have we tested this end-to-end, or would the current tests suffice? I recall there are (PSM) interop tests, are those run post PR submission or manually triggered?
Are there any performance implications of this change?
xds/internal/xdsclient/pool.go
Outdated
// expected to invoke once they are done using the client. It is safe for the | ||
// caller to invoke this close function multiple times. | ||
func (p *Pool) NewClient(name string) (XDSClient, func(), error) { | ||
config, err := bootstrap.GetConfiguration() |
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.
What about the use case where users ONLY set bootstrap config from the SetFallbackBootstrapConfig
API and do not set env var? For this case, when creating the xDS client, would it be created via this method? Would they not require p.config
instead since that is the field set from the implementation below (line 133-143)?
xds/internal/xdsclient/pool.go
Outdated
) | ||
|
||
var ( | ||
// DefaultPool is the default pool for xds clients. It is created at the |
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: Here and everywhere in this PR, please replace occurrences of xds client(s)
with xDS client(s)
. We've been trying to be consistent with xDS client(s)
recently. So, let's stick to that. Thanks.
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
xds/internal/xdsclient/pool.go
Outdated
DefaultPool *Pool | ||
) | ||
|
||
// Pool represents pool of xds clients. |
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.
This docstring should mention that the pool of clients share the same bootstrap configuration.
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
mu sync.Mutex | ||
clients map[string]*clientRefCounted | ||
config *bootstrap.Config |
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: mu
should ideally only have to guard clients
. But here, we need it to guard config
as well since SetFallbackBootstrapConfig
writes to config
. Maybe a comment here to mention that?
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.
Added in the docstring
var ( | ||
// DefaultPool is the default pool for xds clients. It is created at the | ||
// init time. | ||
DefaultPool *Pool |
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.
I agree with @danielzhaotongliu. We don't have to make this public. We should change the dial option and the server option to create a pool with the bootstrap config that was passed to it, and use that pool to create the xDS client. This is specified in the proposal in #7592 (comment)
xds/internal/xdsclient/pool.go
Outdated
} | ||
|
||
// NewPool creates a new xds client pool with the given bootstrap contents. | ||
func NewPool(bootstrapContents []byte) (*Pool, error) { |
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.
I think it would be nicer to have this accept a *bootstrap.Config
(which could optionally be nil
) rather than having to parse the configuration here. With that change, this API will return a single value, *Pool
, instead of two.
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.
When we change this to accept a bootstrap.Config
, we should also export bootstrap.newConfigFromContents
. This would allow non-test usages for NewPool
where the caller would use the exported bootstrap.newConfigFromContents
to parse the JSON config and pass it to this function.
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.
I think it would be nicer to have this accept a *bootstrap.Config (which could optionally be nil) rather than having to parse the configuration here. With that change, this API will return a single value, *Pool, instead of two.
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.
we should also export bootstrap.newConfigFromContents. This would allow non-test usages for NewPool
We already have bootstrap.NewConfigForTesting
which internally call bootstrap.newConfigFromContents
@@ -360,7 +360,7 @@ func (s) TestHandleListenerResponseFromManagementServer(t *testing.T) { | |||
// involving receipt of an RDS response from the management server. The test | |||
// verifies that the internal state of the xDS client (parsed resource and | |||
// metadata) matches expectations. | |||
func (s) TestHandleRouteConfigResponseFromManagementServer(t *testing.T) { | |||
func TestHandleRouteConfigResponseFromManagementServer(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.
Why is the s
receiver being removed from this test?
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.
Reverted. Was just testing something and forgot to put it back.
WatchExpiryTimeout: defaultTestWatchExpiryTimeout, | ||
Contents: bc, |
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.
These tests should be using a pool to create an xDS client in. In fact, once we delete these NewXxx
functions which don't operate on a pool, the tests will be left with no other choice.
xds/server.go
Outdated
@@ -93,12 +93,11 @@ func NewGRPCServer(opts ...grpc.ServerOption) (*GRPCServer, error) { | |||
// simplifies the code by eliminating the need for a mutex to protect the | |||
// xdsC and xdsClientClose fields. | |||
newXDSClient := newXDSClient |
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.
Instead of having this newXDSClient
var which can be overridden by tests to customize xDS client creation, the xDS server (and possibly the xDS resolver) should contain an xdsClientPool
as a global var which would be set to the default pool at init
time and can be customized by tests.
xds/server_test.go
Outdated
@@ -121,14 +121,18 @@ func (s) TestNewServer_Success(t *testing.T) { | |||
desc: "without_xds_creds", | |||
serverOpts: []grpc.ServerOption{ | |||
grpc.Creds(insecure.NewCredentials()), | |||
BootstrapContentsForTesting(generateBootstrapContents(t, uuid.NewString(), nonExistentManagementServer)), | |||
func() grpc.ServerOption { |
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.
Why do we need this func
here?
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.
Removed
// matched route configuration. After it transitions back into not ready | ||
// (through an explicit LDS Resource Not Found), previously running RPC's | ||
// should be gracefully closed and still work, and new RPC's should fail. | ||
func (s) TestServingModeChanges_MultipleServers(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.
I don't think we need to test serving mode changes here. All we need to do here is to ensure that we can create two servers with two different bootstrap configurations. This would also mean that the two servers will request two different LDS resources from the management server, because the LDS resource name depends on the port on which the server is listening.
Then we could have two gRPC clients that talk to each of these servers and we ensure that RPCs work. We need to ensure that a gRPC client is actually talking to the server that it intends to talk to. This can be done with the Peer
call option.
9588254
to
325def2
Compare
This change is only going to change the usages of unit tests and hermatic e2e tests (within grpc-go repo). PSM interop tests, prod and other integration tests should not observe any behavior change. The reason for this change was to bring back old behavior to be able to create xds clients with different bootstrap config even if they point to same server. |
c9274f0
to
af40ea3
Compare
af40ea3
to
d43ade8
Compare
Fixes: #7592
RELEASE NOTES: