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

priority: the implementation #4070

Merged
merged 13 commits into from Feb 10, 2021
Merged

Conversation

menghanl
Copy link
Contributor

@menghanl menghanl commented Dec 1, 2020

Functionality is similar to the priority inside EDS.

@menghanl menghanl requested a review from easwars December 1, 2020 21:58
@menghanl menghanl added the Type: Feature New features or improvements in behavior label Dec 1, 2020
@easwars easwars added this to the 1.35 Release milestone Dec 2, 2020
Copy link
Contributor

@easwars easwars left a comment

Choose a reason for hiding this comment

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

Is it possible to split this into smaller pieces. I understand it might be difficult, but this PR is super huge.

xds/internal/balancer/priority/balancer.go Outdated Show resolved Hide resolved
Copy link
Contributor Author

@menghanl menghanl left a comment

Choose a reason for hiding this comment

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

I didn't find an easy way to split this... I can add the priority handling methods first, but that will be a PR with no running code, or meaningful tests. And that PR would probably be harder to understand.

xds/internal/balancer/priority/balancer.go Outdated Show resolved Hide resolved
Copy link
Contributor

@easwars easwars left a comment

Choose a reason for hiding this comment

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

I have looked at very little so far, but I thought it might be better to send my comments as and when I have them instead of accumulating them since it might take a while before I get done with the whole thing.

And apologize for the delay so far.

logger *grpclog.PrefixLogger
cc balancer.ClientConn
bg *balancergroup.BalancerGroup
ctx context.Context
Copy link
Contributor

Choose a reason for hiding this comment

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

How about a grpcsync.Event instead of a context?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


priority, ok := pb.childToPriority[childName]
if !ok {
pb.logger.Infof("priority: received picker update with unknown child")
Copy link
Contributor

Choose a reason for hiding this comment

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

Include childName in the log message.

Also, this log message should be error/warning.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

}

if pb.childInUse == "" {
pb.logger.Infof("priority: no childInUse when picker update is received", pb.childInUse)
Copy link
Contributor

Choose a reason for hiding this comment

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

Prefer avoiding the use of variable names in log messages.

Also childInUse is known to be empty here. So, why print it? And there is no format string for it anyways. Should vet be catching these?

Again, this should be error/warning.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

}

// priorityInUse is higher than this priority.
if pb.priorityInUse < priority {
Copy link
Contributor

Choose a reason for hiding this comment

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

Didn't we have a type for priority which made comparisons more readable? Should we be using that?

Same here for this log message and every other log message:

  • if it is an error condition, please use error/warning
  • capture as much of the state in the message. (the received priority is not printed here)
  • do not use variable names in the log message.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I found using that type doesn't gain us much, but is very cumbersome.

Log messages updated.

case connectivity.Connecting:
pb.handlePriorityWithNewStateConnecting(child, priority, oldState)
default:
// New state is Idle, should never happen. Don't forward.
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe an error/warning log here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There should be connectivity state change in child balancers. I think we can safely ignore this here.

xds/internal/balancer/priority/balancer.go Show resolved Hide resolved
xds/internal/balancer/priority/balancer.go Show resolved Hide resolved
cancel context.CancelFunc
childStateUpdate *buffer.Unbounded

config *lbConfig
Copy link
Contributor

Choose a reason for hiding this comment

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

The only use of this field seems to be at the end of UpdateClientConnState where we write to it. Do we need this? Is there a plan to use it in the future? Or am I missing something?

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.

pb.priorities = make([]string, 0, len(newConfig.Priorities))
pb.childToPriority = make(map[string]int)
for pi, pName := range newConfig.Priorities {
pb.priorities = append(pb.priorities, pName)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can't we directly assign pb.priorities = newConfig.Priorities? If not, can we say pb.priorities[pi] = pName since you have already made the slice as big as required.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm, yeah, directly assigning works! Done

func newPriorityBalancer(cc balancer.ClientConn) *priorityBalancer {
b := &priorityBalancer{
cc: cc,
childToPriority: make(map[string]int),
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm guessing we don't need to make the childToPriority map here since it is always being made again in UpdateClientConnState.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But if the read in handleChildStateUpdate() happens before the write (in an error case), it may panic due to nil.


type childBalancer struct {
name string
parent *priorityBalancer
Copy link
Contributor

Choose a reason for hiding this comment

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

The only reason parent seems to be used here is to access the balancerGroup. Could we instead have the operations on the balancerGroup handled directly by the priorityBalancer. If that would make things complex, at least we can directly accept the balancerGroup here instead of the parent. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought the children also need the logger from parent. But it doesn't seem to call it.
I'm keeping it in case we need info logs later

for name, newSubConfig := range newConfig.Children {
bb := balancer.Get(newSubConfig.Config.Name)
if bb == nil {
pb.logger.Warningf("balancer name %v from config is not registered", newSubConfig.Config.Name)
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this only a warning and not an error? Would it be better to check this in ParseConfig and if we find an unregistered child policy, should we fail there?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Service config parsing should already checked this, and would not accept unregistered balancers. So this should never happen.

Copy link
Member

Choose a reason for hiding this comment

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

In that case, I'm in favor of Error. This will make tests fail if it happens, and since it "can't happen", it doesn't matter either way for production.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Contributor

Choose a reason for hiding this comment

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

Service config parsing should already checked this, and would not accept unregistered balancers. So this should never happen.

Sorry for coming back to this after a long break.
Where in service config parsing do we check if the balancer name is registered? I don't see such a check in the parseConfig() method defined in config.go.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This type BalancerConfig is a specially defined type

type child struct {
Config *internalserviceconfig.BalancerConfig
}

Its UnmarshalJSON is where the magic happens:

// UnmarshalJSON implements the json.Unmarshaler interface.
//
// ServiceConfig contains a list of loadBalancingConfigs, each with a name and
// config. This method iterates through that list in order, and stops at the
// first policy that is supported.
// - If the config for the first supported policy is invalid, the whole service
// config is invalid.
// - If the list doesn't contain any supported policy, the whole service config
// is invalid.

// balancer state (started or not) is in sync with the priorities (even in
// tricky cases where a child is moved from a priority to another).
//
// It's guaranteed that after this function returns:
Copy link
Contributor

Choose a reason for hiding this comment

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

Very detailed function level comments become hard to keep in sync with the code as the code evolves. Do you think it makes more sense to move them closer to the actual code?

Here and in some of the other functions.

Copy link
Member

Choose a reason for hiding this comment

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

Listing the pre/post-conditions is always fine, and a good idea.

"Steps", if they are essentially documenting "how does this function work", should be in-line or at the top of the function (inside the function decl; though this doesn't matter much for unexported functions).

for p, name := range pb.priorities {
child, ok := pb.children[name]
if !ok {
pb.logger.Warningf("child with name %q is not found in children", name)
Copy link
Contributor

Choose a reason for hiding this comment

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

This should ideally not be possible. We should check it in ParseConfig and UpdateClientConnState.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is check in config parsing. This log is just in case.

Copy link
Member

Choose a reason for hiding this comment

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

If we think it should be impossible, this should be Errorf; if it happens in tests, it will fail.

}
})
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Nix newline.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Deleted

// sendUpdate sends the addresses and config to the child balancer.
func (cb *childBalancer) sendUpdate() {
// TODO: handle aggregate returned error.
cb.parent.bg.UpdateClientConnState(cb.name, balancer.ClientConnState{
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we ignore this error?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added logging. We don't have a good way to aggregate the error now.


// Update state in child. The updated picker will be sent to parent later if
// necessary.
child, ok := pb.children[childName]
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this condition possible at all?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not in a normal case I think.

@easwars easwars assigned menghanl and unassigned easwars Dec 14, 2020

func subConnFromPicker(p balancer.Picker) func() balancer.SubConn {
return func() balancer.SubConn {
scst, _ := p.Pick(balancer.PickInfo{})
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is the error from Pick ignored? If required, testutils.IsRoundRobin should be fixed to accept a function f func() (balancer.SubConn, error).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The error should always be nil here. Otherwise the test will fail because the returned subconns don't match.

I changed this function to take a testing.T, and fatal if err is not nil.

testRRBalancerName = "another-round-robin"
)

type anotherRR struct {
Copy link
Contributor

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 balancer? Why can't we use pick-first or passthrough when testing changing of child policies?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Using RR is simpler to test the child policy's behavior. It makes one SubConn per address, but passthrough makes one SubConn for all the addresses.

defer pb.Close()

// Two children, with priorities [0, 1], each with one backend.
pb.UpdateClientConnState(balancer.ClientConnState{
Copy link
Contributor

Choose a reason for hiding this comment

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

Please do not ignore errors. We should catch them and call Fatal.
This comment applies to all calls to UpdateClientConnState

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

}

// Remove p2, no updates.
pb.UpdateClientConnState(balancer.ClientConnState{
Copy link
Contributor

Choose a reason for hiding this comment

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

If some of these client conn updates are the same across tests, maybe they can be declared as vars, and shared between tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The values are the same, but they are not "consts" (unlike the test addresses or test proto messages). I think keeping these in the tests makes the test steps clear

xds/internal/balancer/priority/balancer.go Show resolved Hide resolved
xds/internal/balancer/priority/balancer.go Show resolved Hide resolved
for name, newSubConfig := range newConfig.Children {
bb := balancer.Get(newSubConfig.Config.Name)
if bb == nil {
pb.logger.Warningf("balancer name %v from config is not registered", newSubConfig.Config.Name)
Copy link
Member

Choose a reason for hiding this comment

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

In that case, I'm in favor of Error. This will make tests fail if it happens, and since it "can't happen", it doesn't matter either way for production.

priorityInitTimer *time.Timer
}

func (pb *priorityBalancer) UpdateClientConnState(s balancer.ClientConnState) error {
Copy link
Member

Choose a reason for hiding this comment

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

Nit: p or b? pb makes me think of the common protobuf abbreviation every time I see it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

b it is.


type priorityBB struct{}

func (pbb *priorityBB) Build(cc balancer.ClientConn, _ 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.

FWIW since there are no fields in the struct, I believe removing all the pointers around priorityBB is more efficient and more "Go-standard".

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 * and the receiver name.

Comment on lines 192 to 193
pb.mu.Lock()
defer pb.mu.Unlock()
Copy link
Member

Choose a reason for hiding this comment

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

Might it be important to acquire the lock before checking done?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right. Fixed.


// priorityInUse is higher than this priority.
if pb.priorityInUse < priority {
// Lower priorities should all be closed, this is an unexpected update.
Copy link
Member

Choose a reason for hiding this comment

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

Unexpected how? If we think it's impossible, Error log instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This can happen if the child policy somehow sends an update after we tell it to stop. Not necessary an error though.

// priorityInUse is higher than this priority.
if pb.priorityInUse < priority {
// Lower priorities should all be closed, this is an unexpected update.
pb.logger.Warningf("priority: received picker update from priority %v, lower then priority in use %v", priority, pb.priorityInUse)
Copy link
Member

Choose a reason for hiding this comment

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

"lower thAn"

Copy link
Contributor 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 25 to 29
for i, v := range a {
if v != b[i] {
return false
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Remove v and save some small amount of copying?

for i := range a {
  if a[i] != b[i] {
    return false
  }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := equalStringSlice(tt.a, tt.b); got != tt.want {
t.Errorf("equalStringSlice() = %v, want %v", got, tt.want)
Copy link
Member

Choose a reason for hiding this comment

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

Print the inputs for easier debugging:

t.Errorf("equalStringSlice(%v, %v) = %v, want %v", tt.a, tt.b, got, tt.want)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@dfawley dfawley assigned menghanl and unassigned dfawley Jan 15, 2021
Copy link
Contributor Author

@menghanl menghanl 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 review. PTAL.

@@ -0,0 +1,214 @@
/*
*
* Copyright 2020 gRPC authors.
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.

xds/internal/balancer/priority/balancer.go Show resolved Hide resolved

type priorityBB struct{}

func (pbb *priorityBB) Build(cc balancer.ClientConn, _ balancer.BuildOptions) balancer.Balancer {
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 * and the receiver name.

xds/internal/balancer/priority/balancer.go Show resolved Hide resolved
priorityInitTimer *time.Timer
}

func (pb *priorityBalancer) UpdateClientConnState(s balancer.ClientConnState) error {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

b it is.

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := equalStringSlice(tt.a, tt.b); got != tt.want {
t.Errorf("equalStringSlice() = %v, want %v", got, tt.want)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

testRRBalancerName = "another-round-robin"
)

type anotherRR struct {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Using RR is simpler to test the child policy's behavior. It makes one SubConn per address, but passthrough makes one SubConn for all the addresses.

}

// Remove p2, no updates.
pb.UpdateClientConnState(balancer.ClientConnState{
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The values are the same, but they are not "consts" (unlike the test addresses or test proto messages). I think keeping these in the tests makes the test steps clear


func subConnFromPicker(p balancer.Picker) func() balancer.SubConn {
return func() balancer.SubConn {
scst, _ := p.Pick(balancer.PickInfo{})
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The error should always be nil here. Otherwise the test will fail because the returned subconns don't match.

I changed this function to take a testing.T, and fatal if err is not nil.

defer pb.Close()

// Two children, with priorities [0, 1], each with one backend.
pb.UpdateClientConnState(balancer.ClientConnState{
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Contributor Author

@menghanl menghanl 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 review. PTAL.

Copy link
Contributor Author

@menghanl menghanl 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 review. All done. PTAL.

xds/internal/balancer/priority/balancer.go Show resolved Hide resolved
func (pb *priorityBalancer) UpdateState(childName string, state balancer.State) {
pb.childStateUpdate.Put(&balancerStateWithPriority{
func (b *priorityBalancer) UpdateState(childName string, state balancer.State) {
b.childStateUpdate.Put(&balancerStateWithChildName{
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I assume you meant the struct name, but not sure.
So I renamed both the channel and the struct. :)

xds/internal/balancer/priority/balancer_priority.go Outdated Show resolved Hide resolved
@menghanl menghanl assigned dfawley and unassigned menghanl Feb 3, 2021
Copy link
Contributor

@easwars easwars left a comment

Choose a reason for hiding this comment

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

Mostly minor nits.

func (b *priorityBalancer) handleChildStateUpdate(childName string, s balancer.State) {
b.mu.Lock()
defer b.mu.Unlock()
if b.done.HasFired() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Should we check done before grabbing the lock?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It needs to be inside mutex, otherwise it may race with close().


priority, ok := b.childToPriority[childName]
if !ok {
b.logger.Warningf("priority: received picker update with unknown child %v", childName)
Copy link
Contributor

Choose a reason for hiding this comment

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

This log and the next one should be Error I guess.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

b.stopPriorityInitTimer()

// priorityInUse is lower than this priority, switch to this.
if b.priorityInUse > priority {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we really need this check here? switchToChild does handle the case where priorityInUse is the same as priority, by doing nothing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's right. But keep it just so that the next Infof is accurate?

return func() balancer.SubConn {
scst, err := p.Pick(balancer.PickInfo{})
if err != nil {
t.Fatalf("unexpected error from picker.Pick: %v", err)
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need to pass testing.T to this function? This function always seems to be called as the second argument to testutils.IsRoundRobin(), and if the subConns don't match, the test body goes ahead and calls t.Error/Fatal.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If Pick() returns an error, testutils.IsRoundRobin() will fail because the returned sc is nil, but we don't know why Pick() error'ed.

This t.Fatalf prints the error.


// A test balancer that updates balancer.State inline when handling ClientConn
// state.
type testInlineUpdateBalancerBuilder struct{}
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we use the stub balancer here? We should really be trying to use the stub balancer wherever possible instead of sprinkling our codebase with test balancer implementations.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good call. Done.

grpctest.RunSubTests(t, s{})
}

var (
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: remove the paranthesis

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@easwars easwars assigned menghanl and unassigned dfawley Feb 4, 2021
Copy link
Contributor Author

@menghanl menghanl 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 review. All done. PTAL.

func (b *priorityBalancer) handleChildStateUpdate(childName string, s balancer.State) {
b.mu.Lock()
defer b.mu.Unlock()
if b.done.HasFired() {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

It needs to be inside mutex, otherwise it may race with close().


priority, ok := b.childToPriority[childName]
if !ok {
b.logger.Warningf("priority: received picker update with unknown child %v", childName)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

b.stopPriorityInitTimer()

// priorityInUse is lower than this priority, switch to this.
if b.priorityInUse > priority {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's right. But keep it just so that the next Infof is accurate?

grpctest.RunSubTests(t, s{})
}

var (
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

return func() balancer.SubConn {
scst, err := p.Pick(balancer.PickInfo{})
if err != nil {
t.Fatalf("unexpected error from picker.Pick: %v", err)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

If Pick() returns an error, testutils.IsRoundRobin() will fail because the returned sc is nil, but we don't know why Pick() error'ed.

This t.Fatalf prints the error.


// A test balancer that updates balancer.State inline when handling ClientConn
// state.
type testInlineUpdateBalancerBuilder struct{}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good call. Done.

@menghanl menghanl assigned easwars and unassigned menghanl Feb 4, 2021
@easwars easwars modified the milestones: 1.36 Release, 1.37 Release Feb 10, 2021
@menghanl menghanl merged commit ad24ab5 into grpc:master Feb 10, 2021
@menghanl menghanl deleted the priority_policy_balancer branch February 10, 2021 18:38
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 10, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Type: Feature New features or improvements in behavior
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants