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
Conversation
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.
Is it possible to split this into smaller pieces. I understand it might be difficult, but this PR is super huge.
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 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.
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 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 |
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.
How about a grpcsync.Event
instead of a context
?
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
|
||
priority, ok := pb.childToPriority[childName] | ||
if !ok { | ||
pb.logger.Infof("priority: received picker update with unknown child") |
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.
Include childName
in the log message.
Also, this log message should be error/warning.
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
} | ||
|
||
if pb.childInUse == "" { | ||
pb.logger.Infof("priority: no childInUse when picker update is received", pb.childInUse) |
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.
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.
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
} | ||
|
||
// priorityInUse is higher than this priority. | ||
if pb.priorityInUse < priority { |
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.
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.
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 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. |
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.
Maybe an error/warning log 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.
There should be connectivity state change in child balancers. I think we can safely ignore this here.
cancel context.CancelFunc | ||
childStateUpdate *buffer.Unbounded | ||
|
||
config *lbConfig |
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 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?
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.
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) |
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.
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.
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.
Hmm, yeah, directly assigning works! Done
func newPriorityBalancer(cc balancer.ClientConn) *priorityBalancer { | ||
b := &priorityBalancer{ | ||
cc: cc, | ||
childToPriority: make(map[string]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.
I'm guessing we don't need to make
the childToPriority
map here since it is always being made again in UpdateClientConnState
.
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.
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 |
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 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?
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 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) |
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 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?
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.
Service config parsing should already checked this, and would not accept unregistered balancers. So this should never happen.
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 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.
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
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.
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
.
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 type BalancerConfig
is a specially defined type
grpc-go/xds/internal/balancer/priority/config.go
Lines 29 to 31 in f005af0
type child struct { | |
Config *internalserviceconfig.BalancerConfig | |
} |
Its UnmarshalJSON
is where the magic happens:
grpc-go/internal/serviceconfig/serviceconfig.go
Lines 49 to 57 in f005af0
// 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: |
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.
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.
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.
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) |
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 should ideally not be possible. We should check it in ParseConfig
and UpdateClientConnState
.
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 is check in config parsing. This log is just in case.
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.
If we think it should be impossible, this should be Errorf
; if it happens in tests, it will fail.
} | ||
}) | ||
} | ||
|
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.
Nix newline.
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.
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{ |
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 ignore this 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.
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] |
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.
Is this condition possible at all?
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.
Not in a normal case I think.
|
||
func subConnFromPicker(p balancer.Picker) func() balancer.SubConn { | ||
return func() balancer.SubConn { | ||
scst, _ := p.Pick(balancer.PickInfo{}) |
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 error from Pick
ignored? If required, testutils.IsRoundRobin
should be fixed to accept a function f func() (balancer.SubConn, 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.
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 { |
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 balancer? Why can't we use pick-first
or passthrough
when testing changing of child policies?
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.
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{ |
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.
Please do not ignore errors. We should catch them and call Fatal
.
This comment applies to all calls to UpdateClientConnState
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
} | ||
|
||
// Remove p2, no updates. | ||
pb.UpdateClientConnState(balancer.ClientConnState{ |
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.
If some of these client conn updates are the same across tests, maybe they can be declared as vars, and shared between tests.
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 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
56941a4
to
5da3571
Compare
5da3571
to
0264721
Compare
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) |
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 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 { |
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: p
or b
? pb
makes me think of the common protobuf abbreviation every time I see it.
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.
b
it is.
|
||
type priorityBB struct{} | ||
|
||
func (pbb *priorityBB) Build(cc balancer.ClientConn, _ balancer.BuildOptions) balancer.Balancer { |
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.
FWIW since there are no fields in the struct, I believe removing all the pointers around priorityBB
is more efficient and more "Go-standard".
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 *
and the receiver name.
pb.mu.Lock() | ||
defer pb.mu.Unlock() |
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.
Might it be important to acquire the lock before checking 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.
Right. Fixed.
|
||
// priorityInUse is higher than this priority. | ||
if pb.priorityInUse < priority { | ||
// Lower priorities should all be closed, this is an unexpected update. |
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.
Unexpected how? If we think it's impossible, Error log 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.
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) |
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.
"lower thAn"
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
for i, v := range a { | ||
if v != b[i] { | ||
return false | ||
} | ||
} |
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.
Remove v
and save some small amount of copying?
for i := range a {
if a[i] != b[i] {
return false
}
}
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.
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) |
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.
Print the inputs for easier debugging:
t.Errorf("equalStringSlice(%v, %v) = %v, want %v", tt.a, tt.b, got, tt.want)
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
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 the review. PTAL.
@@ -0,0 +1,214 @@ | |||
/* | |||
* | |||
* Copyright 2020 gRPC authors. |
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.
Updated.
|
||
type priorityBB struct{} | ||
|
||
func (pbb *priorityBB) Build(cc balancer.ClientConn, _ balancer.BuildOptions) balancer.Balancer { |
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 *
and the receiver name.
priorityInitTimer *time.Timer | ||
} | ||
|
||
func (pb *priorityBalancer) UpdateClientConnState(s balancer.ClientConnState) 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.
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) |
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
testRRBalancerName = "another-round-robin" | ||
) | ||
|
||
type anotherRR struct { |
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.
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{ |
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 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{}) |
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 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{ |
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
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 the review. PTAL.
80c2423
to
efd0418
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.
Thanks for the review. All done. PTAL.
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{ |
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 assume you meant the struct name, but not sure.
So I renamed both the channel and the struct. :)
efd0418
to
44e47b5
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.
Mostly minor nits.
func (b *priorityBalancer) handleChildStateUpdate(childName string, s balancer.State) { | ||
b.mu.Lock() | ||
defer b.mu.Unlock() | ||
if b.done.HasFired() { |
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: Should we check done
before grabbing the lock?
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 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) |
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 log and the next one should be Error
I guess.
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
b.stopPriorityInitTimer() | ||
|
||
// priorityInUse is lower than this priority, switch to this. | ||
if b.priorityInUse > priority { |
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 really need this check here? switchToChild
does handle the case where priorityInUse
is the same as priority
, by doing nothing.
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.
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) |
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 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
.
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.
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{} |
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.
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.
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.
Good call. Done.
grpctest.RunSubTests(t, s{}) | ||
} | ||
|
||
var ( |
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: remove the paranthesis
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
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 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() { |
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 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) |
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
b.stopPriorityInitTimer() | ||
|
||
// priorityInUse is lower than this priority, switch to this. | ||
if b.priorityInUse > priority { |
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.
That's right. But keep it just so that the next Infof
is accurate?
grpctest.RunSubTests(t, s{}) | ||
} | ||
|
||
var ( |
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
return func() balancer.SubConn { | ||
scst, err := p.Pick(balancer.PickInfo{}) | ||
if err != nil { | ||
t.Fatalf("unexpected error from picker.Pick: %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.
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{} |
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.
Good call. Done.
Functionality is similar to the priority inside EDS.