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

endpointsharding, lazy: Remove intermediary gracefulswitch balancers #8052

Merged
merged 4 commits into from
Feb 3, 2025

Conversation

arjan-bal
Copy link
Contributor

@arjan-bal arjan-bal commented Jan 30, 2025

Presently both lazy and endpointsharding create gracefulswtich balancers to manage their children. The gracefulswitch balancer is unnecessary as the type of the child balancer in not expected to change in existing use cases. Having the gracefulswitch child requires the child balancer's builder to be registered in the global LB registry. The lazy LB is not a complete LB policy and should not be registered globally.

This PR makes the constructors of endpointpointsharding and lazy accept the child balancer's builder to avoid using the global registry. This PR also removes the ParseConfig functions/methods in lazy and endpointsharding as they will simply forward the LoadBalancingConfigs to their children. The children are responsible to parse the LoadBalancingConfig JSON. Since pickfirst can use a nil LoadBalancingConfig, the existing users of endpointsharding don't need to provide a LoadBalancingConfig.

RELEASE NOTES:

  • balancer/endpointsharding: The constructor accepts the child balancer's builder and a struct with optional configuration.
  • balancer/endpointsharding: Balancers created with the new DisableAutoReconnect option will not attempt to call ExitIdle automatically on their children when the children report idle.

@arjan-bal arjan-bal added the Type: Internal Cleanup Refactors, etc label Jan 30, 2025
@arjan-bal arjan-bal added this to the 1.71 Release milestone Jan 30, 2025
@arjan-bal arjan-bal requested a review from dfawley January 30, 2025 10:50
Copy link

codecov bot commented Jan 30, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 82.31%. Comparing base (b0e2ae9) to head (dc2ffa5).
Report is 12 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #8052      +/-   ##
==========================================
+ Coverage   82.29%   82.31%   +0.01%     
==========================================
  Files         384      388       +4     
  Lines       38926    39028     +102     
==========================================
+ Hits        32034    32124      +90     
- Misses       5565     5577      +12     
  Partials     1327     1327              
Files with missing lines Coverage Δ
balancer/endpointsharding/endpointsharding.go 77.40% <100.00%> (+0.27%) ⬆️
balancer/lazy/lazy.go 81.53% <100.00%> (+7.11%) ⬆️
balancer/leastrequest/leastrequest.go 88.69% <100.00%> (ø)
balancer/weightedroundrobin/balancer.go 83.38% <100.00%> (-0.10%) ⬇️

... and 47 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.

Mostly looks fine, but the minor API thing should be decided.

@@ -100,7 +99,7 @@ func (crr *customRoundRobin) UpdateClientConnState(state balancer.ClientConnStat
// is guaranteed to happen since the aggregator will always call
// UpdateChildState in its UpdateClientConnState.
return crr.Balancer.UpdateClientConnState(balancer.ClientConnState{
BalancerConfig: gracefulSwitchPickFirst,
BalancerConfig: nil, // pickfirst can handle nil configs.
Copy link
Member

Choose a reason for hiding this comment

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

Delete the field entirely to remove complications from example?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed throughout.

@@ -236,7 +233,7 @@ func (b *wrrBalancer) UpdateClientConnState(ccs balancer.ClientConnState) error
// This causes child to update picker inline and will thus cause inline
// picker update.
return b.child.UpdateClientConnState(balancer.ClientConnState{
BalancerConfig: endpointShardingLBConfig,
BalancerConfig: nil, // pickfirst can handle nil configs.
Copy link
Member

Choose a reason for hiding this comment

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

And then I'd delete it from all our code to avoid clutter. Most LB policies should handle not having configs, except some special xds things.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed.

// NewBalancer returns a load balancing policy that manages homogeneous child
// policies each owning a single endpoint. The endpointsharding balancer
// forwards the LoadBalancingConfig in ClientConn state updates to its children.
func NewBalancer(cc balancer.ClientConn, opts balancer.BuildOptions, childBuilder balancer.Builder, esOpts Options) balancer.Balancer {
Copy link
Member

Choose a reason for hiding this comment

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

I'm wondering about the signature here.

There's a lot of options:

  1. Keep this & make anyone wanting to use es+lazy wrap lazy in a Builder implementation.
  2. Keep this & implement a Builder in lazy, still.
  3. Create type ChildBuilder interface { Build(..) .. } without Name() and make lazy implement that.
  4. Accept a function matching the signature of Build here.

None of these are great. I think I might like (4) the best for usability? But I dislike it for understandability. I suppose that could be helped with:

type ChildBuilder = func(...) ... // matching Build

?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed to use a type alias. Didn't export the ChildBuilder type since it's only an alias.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated to use a type declaration instead of an alias.


func (builder) Build(cc balancer.ClientConn, bOpts balancer.BuildOptions) balancer.Balancer {
// NewBalancer is the constructor for the lazy balancer.
func NewBalancer(cc balancer.ClientConn, bOpts balancer.BuildOptions, childBuilder balancer.Builder) balancer.Balancer {
Copy link
Member

Choose a reason for hiding this comment

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

The same concerns as above apply here, too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed, using an alias to the type of the balancer.Build method now.

@dfawley dfawley assigned arjan-bal and unassigned dfawley Jan 30, 2025
@arjan-bal arjan-bal assigned dfawley and unassigned arjan-bal Jan 31, 2025
@@ -45,8 +45,12 @@ const (
logPrefix = "[lazy-lb %p] "
)

// childBuilderFunc creates a new balancer with the ClientConn. It has the same
// type as the balancer.Builder.Build method.
type childBuilderFunc = func(cc balancer.ClientConn, opts balancer.BuildOptions) balancer.Balancer
Copy link
Member

Choose a reason for hiding this comment

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

This really should be exported, otherwise you expose unexported symbols. The user would have to look at the source code to learn how to use it.

Also I'm completely unclear what the difference is in making this a type alias vs. just a type definition (w/ vs. w/o the =).

Copy link
Contributor Author

@arjan-bal arjan-bal Feb 3, 2025

Choose a reason for hiding this comment

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

A type alias provides a new name for an existing type without introducing a new type that has a different identity. So the unexported childBuilderFunc isn't its own type and doesn't need to be exposed to package users. The language server expands the childBuilderFunc type to func(cc balancer.ClientConn, opts balancer.BuildOptions) balancer.Balancer.

image

Using a type declaration introduces a new type. For a type declaration, it makes sense to export the type because external users need to create variables of the type to call this function. The language server also mentions the type now.
image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changing to a type declaration and exporting the type. The benefit is the shorter function signature and the godoc of the type being visible.

@arjan-bal arjan-bal force-pushed the avoid-registering-lazy branch from 5104d5c to dc2ffa5 Compare February 3, 2025 05:58
@arjan-bal arjan-bal merged commit 990f5e0 into grpc:master Feb 3, 2025
14 of 15 checks passed
@arjan-bal arjan-bal deleted the avoid-registering-lazy branch February 3, 2025 06:51
janardhanvissa pushed a commit to janardhanvissa/grpc-go that referenced this pull request Feb 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants