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

xds: handle weighted cluster as route action #3613

Merged
merged 7 commits into from
May 26, 2020

Conversation

menghanl
Copy link
Contributor

@menghanl menghanl commented May 12, 2020

This change adds support for route action being weighted cluster (instead of just one cluster). We still only support the default route (the last in the list).

Changes:

  • xds_client: handle RDS response's weighted cluster fields, and propagate the update to xds resolver (by adding a fields to ServiceUpdate.
    • This makes ServiceUpdate not comparable by ==, thus the updates to use cmp.Equal
  • xds_resolver: marshal weighted cluster into balancer config, which picks weighted_target as the top level balancer

This change is Reviewable

@menghanl menghanl requested a review from easwars May 12, 2020 19:06
@menghanl menghanl added the Type: Feature New features or improvements in behavior label May 12, 2020
@menghanl menghanl added this to the 1.30 Release milestone May 12, 2020
@menghanl
Copy link
Contributor Author

We don't have e2e xds tests. We were relying on the integration tests. I will manually test this locally to make sure it works.

@menghanl
Copy link
Contributor Author

Manually tested that it works. Found a expiry timer bug which will be fixed by #3615. This is ready for review.

@menghanl menghanl force-pushed the xds_handle_weighted_target branch from bf18cbd to 29d64a4 Compare May 14, 2020 18:40
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.

Reviewed 8 of 13 files at r1, 1 of 1 files at r2.
Reviewable status: 6 of 14 files reviewed, 15 unresolved discussions (waiting on @easwars and @menghanl)


service_config.go, line 424 at r2 (raw file):

Quoted 8 lines of code…
	aaRaw := aa.rawJSONString
	aa.rawJSONString = ""
	bbRaw := bb.rawJSONString
	bb.rawJSONString = ""
	defer func() {
		aa.rawJSONString = aaRaw
		bb.rawJSONString = bbRaw
	}()

Is comparing just the rawJSONString field of a and b not sufficient? If so, why?


service_config.go, line 430 at r2 (raw file):

		bb.rawJSONString = bbRaw
	}()
	return reflect.DeepEqual(aa, bb)

Any specific reason not to use cmp.Equal here?


internal/internal.go, line 53 at r2 (raw file):

	// This function removes rawJSON before compareing, and adds back
	// afterwards.

Is this comment line about removing and adding back necessary here?


xds/internal/client/client_watchers_rds.go, line 27 at r2 (raw file):

	clusterName     string
	weightedCluster map[string]uint32

Will only one of these two be populated? If so, could you clarify with a comment.
And what is the key in the map? Could you clarify with a comment.


xds/internal/client/client_watchers_service.go, line 28 at r2 (raw file):

	Cluster         string
	WeightedCluster map[string]uint32

Comments here too please.


xds/internal/client/v2client_rds.go, line 109 at r2 (raw file):

		// The case sensitive is set to false. Not set or set to true are both
		// valid.
		return rdsUpdate{}, fmt.Errorf("matches virtual host's default route set case-sensitive to false")

s/matches/matched


xds/internal/client/v2client_rds.go, line 116 at r2 (raw file):

	}

	if wc := route.GetWeightedClusters(); wc != nil {

Is it a valid config if we get both weightedClusters and the singleton cluster?
Should we reject the config in that case?


xds/internal/client/v2client_rds.go, line 119 at r2 (raw file):

		m, err := weightedClustersProtoToMap(wc)
		if err != nil {
			return rdsUpdate{}, fmt.Errorf("match weighted cluster is invalid: %v", err)

s/match/matched


xds/internal/client/v2client_rds.go, line 130 at r2 (raw file):

	if t := wc.TotalWeight; t != nil {
		totalWeight = t.Value
	}

GetValue method on UInt32Value in generated code handles the latter being nil. So, you would simply use that and not check for nils.


xds/internal/client/v2client_rds.go, line 134 at r2 (raw file):

	}
	for _, cw := range wc.Clusters {
		w := cw.Weight.Value

Prefer using getters in the generated code rather than directly accessing fields.


xds/internal/client/v2client_rds_test.go, line 197 at r2 (raw file):

									}}}}}}}}},
			wantUpdate: rdsUpdate{weightedCluster: map[string]uint32{"a": 2, "b": 3, "c": 5}},
		},

A test case for weights not adding up?
And a case for totalWeight not being specified and making sure that weights add to 100.
Or if all cases for issues with weights are being handled in TestWeightedClustersProtoToMap, we should have one failure case at least here.


xds/internal/client/v2client_rds_test.go, line 429 at r2 (raw file):

}

func (s) TestWeightedClustersProtoToMap(t *testing.T) {

Why do we need the 's' receiver here.


xds/internal/client/v2client_rds_test.go, line 460 at r2 (raw file):

			},
			want:    map[string]uint32{"a": 2, "b": 3, "c": 5},
			wantErr: false,

Maybe not required to set wantErr to false since that is the default. Here and below.


xds/internal/client/v2client_rds_test.go, line 474 at r2 (raw file):

			want:    map[string]uint32{"a": 20, "b": 30, "c": 50},
			wantErr: false,
		},

A test case for default total weight not adding up to 100


xds/internal/client/v2client_rds_test.go, line 483 at r2 (raw file):

				return
			}
			if !reflect.DeepEqual(got, tt.want) {

Why not cmp.Equal?

@easwars easwars assigned menghanl and unassigned easwars May 14, 2020
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.

Reviewable status: 6 of 14 files reviewed, 15 unresolved discussions (waiting on @easwars and @menghanl)


service_config.go, line 424 at r2 (raw file):

Previously, easwars (Easwar Swaminathan) wrote…
	aaRaw := aa.rawJSONString
	aa.rawJSONString = ""
	bbRaw := bb.rawJSONString
	bb.rawJSONString = ""
	defer func() {
		aa.rawJSONString = aaRaw
		bb.rawJSONString = bbRaw
	}()

Is comparing just the rawJSONString field of a and b not sufficient? If so, why?

Because of white space..


service_config.go, line 430 at r2 (raw file):

Previously, easwars (Easwar Swaminathan) wrote…

Any specific reason not to use cmp.Equal here?

The service config structs contains a bunch of balancer configs, typed interface{}, with real type unexported.
For cmp.Equal to compare unexported fields, we need cmp.AllowUnexported, which needs a struct with the type. But the balancer config types aren't exported, this is becomes impossible.


internal/internal.go, line 53 at r2 (raw file):

Previously, easwars (Easwar Swaminathan) wrote…
	// This function removes rawJSON before compareing, and adds back
	// afterwards.

Is this comment line about removing and adding back necessary here?

Removed.
Replaced with "rawJSON strippd"


xds/internal/client/client_watchers_rds.go, line 27 at r2 (raw file):

Previously, easwars (Easwar Swaminathan) wrote…
	clusterName     string
	weightedCluster map[string]uint32

Will only one of these two be populated? If so, could you clarify with a comment.
And what is the key in the map? Could you clarify with a comment.

In fact clusterName is never set. I deleted the field, and corresponding field in other structs.


xds/internal/client/client_watchers_service.go, line 28 at r2 (raw file):

Previously, easwars (Easwar Swaminathan) wrote…
	Cluster         string
	WeightedCluster map[string]uint32

Comments here too please.

Field removed


xds/internal/client/v2client_rds.go, line 109 at r2 (raw file):

Previously, easwars (Easwar Swaminathan) wrote…

s/matches/matched

Done.


xds/internal/client/v2client_rds.go, line 116 at r2 (raw file):

Previously, easwars (Easwar Swaminathan) wrote…

Is it a valid config if we get both weightedClusters and the singleton cluster?
Should we reject the config in that case?

It's an oneof, will never happen.


xds/internal/client/v2client_rds.go, line 119 at r2 (raw file):

Previously, easwars (Easwar Swaminathan) wrote…

s/match/matched

Done.


xds/internal/client/v2client_rds.go, line 130 at r2 (raw file):

Previously, easwars (Easwar Swaminathan) wrote…
	if t := wc.TotalWeight; t != nil {
		totalWeight = t.Value
	}

GetValue method on UInt32Value in generated code handles the latter being nil. So, you would simply use that and not check for nils.

Still need to check nil. If total_weight is nil, total weight's default value is 100.


xds/internal/client/v2client_rds.go, line 134 at r2 (raw file):

Previously, easwars (Easwar Swaminathan) wrote…

Prefer using getters in the generated code rather than directly accessing fields.

Done.


xds/internal/client/v2client_rds_test.go, line 197 at r2 (raw file):

Previously, easwars (Easwar Swaminathan) wrote…

A test case for weights not adding up?
And a case for totalWeight not being specified and making sure that weights add to 100.
Or if all cases for issues with weights are being handled in TestWeightedClustersProtoToMap, we should have one failure case at least here.

Done.


xds/internal/client/v2client_rds_test.go, line 429 at r2 (raw file):

Previously, easwars (Easwar Swaminathan) wrote…

Why do we need the 's' receiver here.

We don't. It's probably added as part of the test logger refactor, all (most?) tests all have the s receiver now. So we have better logging when it fails?


xds/internal/client/v2client_rds_test.go, line 460 at r2 (raw file):

Previously, easwars (Easwar Swaminathan) wrote…

Maybe not required to set wantErr to false since that is the default. Here and below.

Done.


xds/internal/client/v2client_rds_test.go, line 474 at r2 (raw file):

Previously, easwars (Easwar Swaminathan) wrote…

A test case for default total weight not adding up to 100

Done.


xds/internal/client/v2client_rds_test.go, line 483 at r2 (raw file):

Previously, easwars (Easwar Swaminathan) wrote…

Why not cmp.Equal?

Done.

@menghanl menghanl assigned easwars and unassigned menghanl May 14, 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.

Reviewable status: 2 of 14 files reviewed, 4 unresolved discussions (waiting on @easwars and @menghanl)


service_config.go, line 424 at r2 (raw file):

Previously, menghanl (Menghan Li) wrote…

Because of white space..

Maybe add a comment? Because otherwise it is not very clear.


service_config.go, line 430 at r2 (raw file):

Previously, menghanl (Menghan Li) wrote…

The service config structs contains a bunch of balancer configs, typed interface{}, with real type unexported.
For cmp.Equal to compare unexported fields, we need cmp.AllowUnexported, which needs a struct with the type. But the balancer config types aren't exported, this is becomes impossible.

Same. A quick comment would be useful. Thanks.


xds/internal/client/client_watchers_service.go, line 28 at r2 (raw file):

Previously, menghanl (Menghan Li) wrote…

Field removed

A comment here saying what is the key would still be useful. Thanks.


xds/internal/client/v2client_rds.go, line 130 at r2 (raw file):

Previously, menghanl (Menghan Li) wrote…

Still need to check nil. If total_weight is nil, total weight's default value is 100.

I might write it as:

	var totalWeight uint32 = 100
	if t := wc.GetTotalWeight().GetValue(); t != 0 {
		totalWeight = t.Value
	}

I find this cleaner. Although, could just be personal opinion.

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.

Reviewed 2 of 13 files at r1, 10 of 10 files at r4.
Reviewable status: all files reviewed, 6 unresolved discussions (waiting on @menghanl)


xds/internal/client/v2client_rds.go, line 130 at r2 (raw file):

var totalWeight uint32 = 100
if t := wc.GetTotalWeight().GetValue(); t != 0 {
totalWeight = t.Value
}
I guess I wanted to say the following:

	var totalWeight uint32 = 100
	if t := wc.GetTotalWeight().GetValue(); t != 0 {
		totalWeight = t
	}

xds/internal/resolver/serviceconfig.go, line 76 at r4 (raw file):

	bs, err := json.Marshal(sc)
	if err != nil {
		panic(err.Error())

Do we really need to panic here?
Could we change this function to return (string, error) and have the resolver call cc.ReportError instead?


xds/internal/resolver/xds_resolver_test.go, line 348 at r4 (raw file):

			t.Errorf("ClientConn.UpdateState received different service config")
			t.Error("got: ", cmp.Diff(nil, rState.ServiceConfig.Config))
			t.Error("want: ", cmp.Diff(nil, wantSCParsed.Config))

Can't we have a single call to t.Errorf and do a cmp.Diff(gotConfig, wantConfig).
The same comment applied to the test in TestServiceUpdateToJSON

@easwars easwars assigned menghanl and unassigned easwars May 18, 2020
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.

Reviewable status: all files reviewed, 6 unresolved discussions (waiting on @easwars)


service_config.go, line 424 at r2 (raw file):

Previously, easwars (Easwar Swaminathan) wrote…

Maybe add a comment? Because otherwise it is not very clear.

Done.


service_config.go, line 430 at r2 (raw file):

Previously, easwars (Easwar Swaminathan) wrote…

Same. A quick comment would be useful. Thanks.

Done.


xds/internal/client/client_watchers_service.go, line 28 at r2 (raw file):

Previously, easwars (Easwar Swaminathan) wrote…

A comment here saying what is the key would still be useful. Thanks.

Done.


xds/internal/client/v2client_rds.go, line 130 at r2 (raw file):

Previously, easwars (Easwar Swaminathan) wrote…

var totalWeight uint32 = 100
if t := wc.GetTotalWeight().GetValue(); t != 0 {
totalWeight = t.Value
}
I guess I wanted to say the following:

	var totalWeight uint32 = 100
	if t := wc.GetTotalWeight().GetValue(); t != 0 {
		totalWeight = t
	}

Done.


xds/internal/resolver/serviceconfig.go, line 76 at r4 (raw file):

Previously, easwars (Easwar Swaminathan) wrote…

Do we really need to panic here?
Could we change this function to return (string, error) and have the resolver call cc.ReportError instead?

Done.


xds/internal/resolver/xds_resolver_test.go, line 348 at r4 (raw file):

Previously, easwars (Easwar Swaminathan) wrote…
			t.Errorf("ClientConn.UpdateState received different service config")
			t.Error("got: ", cmp.Diff(nil, rState.ServiceConfig.Config))
			t.Error("want: ", cmp.Diff(nil, wantSCParsed.Config))

Can't we have a single call to t.Errorf and do a cmp.Diff(gotConfig, wantConfig).
The same comment applied to the test in TestServiceUpdateToJSON

We cannot cmp.Diff two service configs, because of the unexported balancer config types.
This cmp.Diff here is just to pretty print the content.

@menghanl menghanl assigned easwars and unassigned menghanl May 18, 2020
// because they may diff in white spaces.
//
// If any of them is NOT *ServiceConfig, return false.
func equalServiceConfig(a, b serviceconfig.Config) bool {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it really required that this function be exposed though the internal package?
I see that this only used by a couple of xds_tests. Could we rather move this to xds/testutils?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To clear the rawJSONString field, it has to be here.
Or, we export a method to clear the field...

@easwars easwars removed their assignment May 26, 2020
@menghanl menghanl merged commit d071d56 into grpc:master May 26, 2020
@menghanl menghanl deleted the xds_handle_weighted_target branch May 26, 2020 20:58
@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 25, 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.

2 participants