-
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
xds: support pick_first custom load balancing policy (A62) #6314
Conversation
Will take a look, but fyi this is failing vet and tests :). |
Forgot to remove a line from my testing...should be fixed now. |
@@ -382,6 +383,8 @@ func (s) TestPickFirst_StickyTransientFailure(t *testing.T) { | |||
} | |||
|
|||
func (s) TestPickFirst_ShuffleAddressList(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.
Top level comment about setup and expectation.
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'd rather not be detailed and explain the whole test setup and everything in the comment. The name is very self-explanatory, but I added a short 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.
That's not what I mean. I just feel like it should be briefly explain stuff like configures pick_first as top level lb of the channel, sends it address list before knob and connection isn't random, after knob it is random (with our determinisitic randomness algorithm plumbed). See
grpc-go/test/pickfirst_test.go
Line 239 in 8d2f69a
// TestPickFirst_NewAddressWhileBlocking tests the case where pick_first is |
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 comment is unnecessarily detailed and a recipe for the test & comment diverging when someone decides to change the test to add an extra check or do things in a different order because of a race, etc. I'd rather not over-document our tests, and would definitely not require detailed documentation anywhere. It's actually pretty rare to have any comments at the top of a test.
E.g. https://cs.opensource.google/go/go/+/refs/tags/go1.20.4:src/net/http/client_test.go;l=550
@@ -431,3 +434,58 @@ func (s) TestPickFirst_ShuffleAddressList(t *testing.T) { | |||
t.Fatal(err) | |||
} | |||
} | |||
|
|||
func (s) TestPickFirst_ShuffleAddressListDisabled(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.
Same here. Please add top level comment with setup and expectation.
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.
As 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.
Same comment as above.
}, | ||
}, | ||
wantConfig: `[{"pick_first": { "shuffleAddressList": true }}]`, | ||
}, |
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.
Optional: can you add a test case for it being unset and it creating false? I guess that is specific and might be more appropriate (unset and also set to false -> false as per my comment on the main code) in ParseConfig test in pick_first rather than scope it to this test here which tests the registry. Actually since the registry does incorporate this proto -> json conversion, maybe we should add tests here. Or maybe that would scale up the codebase too much but I'm now mindful of those 3 buckets (not set, set and zero, set and non zero) in both the xDS flow through proto and the user writing json itself in the file system e.g. and how those 3 buckets map to our balancer's eventual internal configuration type returned from ParseConfig().
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 no "unset" in the PF proto input. This is a boolean field. If it's missing from the struct, it defaults to its zero value of false. That said, testing that it outputs false
when the input is false
/absent-so-zero is worthwhile, so 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.
Oh. I've seen the proto bools be wrapped in a proto pointer type. Right, now if it's not explicitly set it gets it's zero (false, not nil because it's a bool value type), and it should make a false config in both 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.
I've seen the proto bools be wrapped in a proto pointer type.
There are wrapper types for use cases where presence is important. That's not in use here, because presence is not important for this field (missing=default=zero=false).
Btw there's a ton of merge conflicts with go.mod/go.sum stuff. |
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; comments addressed
@@ -382,6 +383,8 @@ func (s) TestPickFirst_StickyTransientFailure(t *testing.T) { | |||
} | |||
|
|||
func (s) TestPickFirst_ShuffleAddressList(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'd rather not be detailed and explain the whole test setup and everything in the comment. The name is very self-explanatory, but I added a short comment.
@@ -431,3 +434,58 @@ func (s) TestPickFirst_ShuffleAddressList(t *testing.T) { | |||
t.Fatal(err) | |||
} | |||
} | |||
|
|||
func (s) TestPickFirst_ShuffleAddressListDisabled(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.
As above.
@@ -198,10 +223,7 @@ func (s) TestConvertToServiceConfigSuccess(t *testing.T) { | |||
}, | |||
}, | |||
}, | |||
wantConfig: &internalserviceconfig.BalancerConfig{ | |||
Name: "myorg.MyCustomLeastRequestPolicy", | |||
Config: customLBConfig{}, |
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 understand what you're trying to say.
We need to register the LB policy in order for the code to work, right? The custom LB policy would not be emitted if it's not registered.
t.Fatalf("ConvertToServiceConfig() got unexpected output, diff (-got +want): %v", diff) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func jsonMarshal(t *testing.T, x interface{}) string { |
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.
x
LGTM for a 5 line function.
I'll fix the go.mod/sum stuff before merging |
@@ -301,6 +314,8 @@ func (s) TestConvertToServiceConfigSuccess(t *testing.T) { | |||
if err != nil { | |||
t.Fatalf("ConvertToServiceConfig(%s) failed: %v", pretty.ToJSON(test.policy), err) | |||
} | |||
// got and want must be unmarshalled since JSON strings shouldn't | |||
// generally be directly compared. |
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: briefly mention why. E.g. removes whitespaces/nondeterministic creation/functionality equiavlent JSON even with whitespaces.
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 can assume a programmer understands why JSON strings can't be directly compared.
@@ -112,7 +113,7 @@ func (b *pickfirstBalancer) UpdateClientConnState(state balancer.ClientConnState | |||
b.cfg = cfg | |||
} | |||
|
|||
if b.cfg != nil && b.cfg.ShuffleAddressList { | |||
if envconfig.PickFirstLBConfig && b.cfg != nil && b.cfg.ShuffleAddressList { |
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.
Does every language guarantee this optimization that we as a team (I use it a lot) to protect panics if x == nil || x.y (will panic but first check not exiting guarantees this won't panic). Hopefully Go never changes this early evaluation of the conditional.
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, short-circuiting is fundamental to every major programming language.
t.Fatalf("Error unmarshalling rawJSON (%q): %v", rawJSON, err) | ||
} | ||
var want []map[string]interface{} | ||
if err := json.Unmarshal(json.RawMessage(test.wantConfig), &want); err != 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.
Can you make sure this is WAI by deleting a recursion layer of wrrLocalityBalancerConfig and making sure this fails?
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 sure what you're concerned about specifically, but I did this and it failed.
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.
LGTM outside of a few trailing comments/nits.
RELEASE NOTES: