-
Notifications
You must be signed in to change notification settings - Fork 4.6k
xds/resolver: Optimize Interceptor Chain Construction #8641
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
base: master
Are you sure you want to change the base?
xds/resolver: Optimize Interceptor Chain Construction #8641
Conversation
…tor, not for every RPC
|
@eshitachandwani : FYI this might conflict with your resolver changes for A74. And since you have been looking at the resolver code for sometime now, it would be a good PR to review. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #8641 +/- ##
==========================================
+ Coverage 79.45% 83.09% +3.64%
==========================================
Files 415 415
Lines 41339 32144 -9195
==========================================
- Hits 32844 26710 -6134
+ Misses 6621 4021 -2600
+ Partials 1874 1413 -461
🚀 New features to boost your workflow:
|
|
@arjan-bal could you review this change, please? |
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.
Leaving my comments on the non-test code, still reviewing the tests.
| // The filter config override maps contain overrides from the route, cluster, | ||
| // and virtual host respectively. The cluster override has the highest priority, | ||
| // followed by the route override, and finally the virtual host override. | ||
| func newInterceptor(filters []xdsresource.HTTPFilter, cluster, route, virtualHost map[string]httpfilter.FilterConfig) (iresolver.ClientInterceptor, 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.
nit: What do you think about adding an override suffix to cluster, route, and virtualHost? I find it can help with readability, but no worries if you prefer the current naming.
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.
| cs := r.newConfigSelector() | ||
| cs, err := r.newConfigSelector() | ||
| if err != nil { | ||
| r.onResourceError(fmt.Errorf("xds: failed to create config selector: %v", err)) |
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.
Should the function return early after calling onResourceError? I'm not sure what happens when a nil config selector is passed to sendNewServiceConfig.
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 we should return early since onResourceError will call sendNewServiceConfig with an erroring config selector, so we should not call sendNewServiceConfig again with a nil config selector (which will set a default config selector in client conn)
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.
Thanks for spotting this. Fixed it.
Looks like we don't have any tests for this case, but this is also a very very unlikey case. There are two cases when building a config selector can fail:
- A filter is part of the configuration, but is not supported on the client side. This case would not pass xDS client validation. (I had a panic for this case initially, but changed it to return an error as part of this reivew).
- The call to build the interceptor for the filter fails. This is also very unlikey because if the filter successfully parsed the configuration (during resource parsing in the xDS client), there is very little reason for it to fail when trying to build the interceptor.
I'll anyway file an issue to add a test for this edge case.
| // Should not happen if it passed xdsClient validation. | ||
| panic(fmt.Sprintf("filter %q does not support use in client", filter.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.
What do you think about continuing to return an error here? The method signature already allows for it, so callers presumably have error handling in place. Panicking to crash the process feels like a worse alternative, especially if this is a recoverable 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.
At some point in the recent past, I got comfortable using panics for cases where we definitely don't expect something to happen, without some really bad programming error. This I believe is one such. But since an error is already being handled by the caller of this function, I'm ok to return an error here instead of panicking. Thanks.
| fb.logger.Logf("BuildClientInterceptor called with config: %+v, override: %+v", config, override) | ||
|
|
||
| if config == nil { | ||
| panic("unexpected missing 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.
In my opinion printing an error to fail the test may be preferred to panicking from a spawned goroutine. The situation seems similar to the style guide recommendation about only calling t.Fatalf from the main test 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.
Done.
| // by the backend, allowing tests to assert that the correct filter | ||
| // configuration was applied for each RPC. | ||
| type testHTTPFilterWithRPCMetadata struct { | ||
| logger logger |
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: Is there a benefit of using a logger interface here? If not, I think it's simpler to directory store a testing.T 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.
It's probably an overkill, but I feel that it's better to do this instead of accepting a testing.T, since we can very easily start calling other methods on it like Error or Fatal if we have access to it.
|
|
||
| func (*testHTTPFilterWithRPCMetadata) IsTerminal() bool { return false } | ||
|
|
||
| var _ httpfilter.ClientInterceptorBuilder = &testHTTPFilterWithRPCMetadata{} |
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: I think we should add a comment here stating that ClientInterceptorBuilder is an optional interface for Filters to implement so this compile time check ensures the test filter implements it. In my opinion this would help readers.
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.
| } | ||
| var errStr string | ||
| if newStreamErr != nil { | ||
| errStr = newStreamErr.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 see that we're converting a string to an error, assigning it to testFilterCfg.newStreamErr and converting it back to a string here. Can the error string be stored in testFilterCfg.newStreamErr to avoid these conversions?
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.
| } | ||
|
|
||
| func (cs *clientStream) Context() context.Context { | ||
| return cs.ctx |
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.
Do we need to store a context here? Can we return cs.ClientStream.Context() 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.
Looks like I don't need this type at all. I was probably doing something else at some point in time that required this type. But with the current state of the test and the current implementation of testFilterInterceptor, we don't need this type at all. Thanks.
| if internal.NewXDSResolverWithConfigForTesting == nil { | ||
| t.Fatalf("internal.NewXDSResolverWithConfigForTesting is nil") | ||
| } |
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: We can avoid this check and let the code panic if we don't expect to fail in most cases.
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.
There is a bunch of commented code here that probably needs to be removed. Can you please take a look?
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. Thanks.
Existing behavior:
New behavior:
Other changes:
RELEASE NOTES: NONE