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

Parse ServiceConfig JSON string #1515

Merged
merged 21 commits into from
Oct 19, 2017
Merged

Parse ServiceConfig JSON string #1515

merged 21 commits into from
Oct 19, 2017

Conversation

lyuxuan
Copy link
Contributor

@lyuxuan lyuxuan commented Sep 14, 2017

No description provided.

@menghanl menghanl self-assigned this Sep 14, 2017
@dfawley dfawley assigned lyuxuan and unassigned menghanl Sep 21, 2017
@lyuxuan lyuxuan closed this Sep 22, 2017
@lyuxuan lyuxuan reopened this Sep 22, 2017
@dfawley dfawley assigned menghanl and unassigned lyuxuan Sep 28, 2017
@lyuxuan lyuxuan force-pushed the handleSC branch 2 times, most recently from f539b14 to 04aa293 Compare October 5, 2017 22:51
@menghanl menghanl added the Type: Feature New features or improvements in behavior label Oct 6, 2017
clientconn.go Outdated
Method *string `json:"method,omitempty"`
}

func (j jsonName) Stringer() string {
Copy link
Contributor

Choose a reason for hiding this comment

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

s/Stringer()/String() ?
Stringer is the name of an interface with a String() function.

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

clientconn.go Outdated
res += "/" + *j.Service + "/"
}
if j.Method != nil {
res += *j.Method
Copy link
Contributor

Choose a reason for hiding this comment

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

If service == nil, this function returns "method", is this right?

When searching in the map, we search for /service/method first, then /service/, but never for method. Do we need to store the value in the map if service == nil?

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. change has been made to skip storing such method config.

clientconn.go Outdated
return err
}
cc.scRaw = js
var sc ServiceConfig
Copy link
Contributor

Choose a reason for hiding this comment

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

sc := ServiceConfig {
	LB:      rsc.LoadBalancingPolicy,
	Methods: make(map[string]MethodConfig),
}

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

clientconn.go Outdated

// handleServiceConfig parses the service config string in JSON format to Go native
// struct ServiceConfig, and store both the struct and the JSON string in ClientConn.
func (cc *ClientConn) handleServiceConfig(js string) error {
Copy link
Contributor

Choose a reason for hiding this comment

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

Move the all service config related code (structs and functions) to a new file service_config.go and also add unit tests for the json parsing function.

Put all the parsing code in a function

func parseJsonServiceConfig(js string) (ServiceConfig, error)

In ClientConn, you can do

if sc, err := parse(js); err == nil {
  cc.mu.Lock()
  cc.scRaw = js
  cc.sc = sc
  cc.mu.Unlock()
}

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

@@ -1226,104 +1230,192 @@ func newDuration(b time.Duration) (a *time.Duration) {
func TestServiceConfigGetMethodConfig(t *testing.T) {
defer leakcheck.Check(t)
for _, e := range listTestEnv() {
testGetMethodConfig(t, e)
if e.balancer == "roundrobin" {
Copy link
Contributor

Choose a reason for hiding this comment

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

It should be OK to test service config with only one of the test envs. Then we don't need this wrapper function and loop.

Just start the test with

te := testServiceConfigSetup(t, tcpClearRREnv)

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

Methods: m,
}
ch <- sc
scjs := `{
Copy link
Contributor

Choose a reason for hiding this comment

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

Move the code around so the declaration of a variable is closed to where it's used?

You actually don't need this string variable if you don't need to check against 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.

done

// Make sure service config has been processed by grpc.
mc := cc.GetMethodConfig("/grpc.testing.TestService/EmptyCall")
for {
if mc.WaitForReady == nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

for {
  if cc.GetMethodConfig("/grpc.testing.TestService/EmptyCall").WaitForReady != nil {
    break
  }
  time.Sleep(time.Millisecond)
}

Probably also add some timeout:

var processed bool
for i := 0; i < 1000; i++ {
  if cc.GetMethodConfig("/grpc.testing.TestService/EmptyCall").WaitForReady != nil {
    processed = true
    break
  }
  time.Sleep(time.Millisecond)
}
if !processed {
  t.Fatalf("service config is not processed by gRPC after 1 second")
}

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 code logic has been deleted in recent commit.

Copy link
Contributor

Choose a reason for hiding this comment

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

Did you forget to push some commits?
This code is still 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.

done

clientconn.go Outdated
// struct ServiceConfig, and store both the struct and the JSON string in ClientConn.
func (cc *ClientConn) handleServiceConfig(js string) error {
sc, err := parseServiceConfig(js)
if err == nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

Return error early instead.

sc, err := parseServiceConfig(js)
	if err != nil {
		return err
	}
	cc.mu.Lock()
	cc.scRaw = js
	cc.sc = sc
	cc.mu.Unlock()
}

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

rpc_util.go Outdated
@@ -524,7 +524,7 @@ type MethodConfig struct {
type ServiceConfig struct {
Copy link
Contributor

Choose a reason for hiding this comment

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

Move this struct to the new file, 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.

done

Method *string `json:"method,omitempty"`
}

func (j jsonName) String() (string, bool) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Typically, String() function only returns string.
Rename this to getPath().

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

"name": [
{
"service": "foo",
"method": "Bar"
Copy link
Contributor

Choose a reason for hiding this comment

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

Indentation for this and some of the following 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.

done

Copy link
Contributor

Choose a reason for hiding this comment

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

Still doesn't look right...

)

func TestParseLoadBalancer(t *testing.T) {
defer leakcheck.Check(t)
Copy link
Contributor

Choose a reason for hiding this comment

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

Leakcheck is unnecessary for tests like this.

Copy link
Contributor Author

@lyuxuan lyuxuan Oct 13, 2017

Choose a reason for hiding this comment

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

done

// Make sure service config has been processed by grpc.
mc := cc.GetMethodConfig("/grpc.testing.TestService/EmptyCall")
for {
if mc.WaitForReady == nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

Did you forget to push some commits?
This code is still here.

MaxResponseMessageBytes *int `json:"maxResponseMessageBytes,omitempty"`
}

type jsonSC struct {
Copy link
Contributor

Choose a reason for hiding this comment

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

This struct is same as the real ServiceConfig struct except for the second field. I prefer to not have this temporary struct, and reuse the existing structs.

json does a partial parsing if the struct only contains part of the fields, so we could do Unmarshal twice, first time for the service/method name, second time for the remaining configs. The second Unmarshal can go directly to the existing MethodConfig struct.

https://play.golang.org/p/GCSXcP1yQk

const testJSON = `{"name": [ {"service": "foo", "method": "bar"} ], "waitForReady": true}`

type jsonName struct {
	Service *string `json:"service,omitempty"`
	Method  *string `json:"method,omitempty"`
}

type methodConfigName struct {
	Name *[]jsonName
}

type MethodConfig struct {
	WaitForReady *bool
	Timeout      *string
}

func main() {
	buf := []byte(testJSON)

	var mcn methodConfigName
	err := json.Unmarshal(buf, &mcn) // for name only.
	if err != nil {
		panic(err.Error())
	}
	fmt.Printf("%+v\n", mcn)

	var mc MethodConfig
	err = json.Unmarshal(buf, &mc) // for everything other than name.
	if err != nil {
		panic(err.Error())
	}
	fmt.Printf("%+v\n", mc)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

filed names are different and go json package cannot handle it.

@@ -577,6 +579,7 @@ type ClientConn struct {

mu sync.RWMutex
sc ServiceConfig
scRaw string
Copy link
Contributor

Choose a reason for hiding this comment

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

Set this to "" in scWatcher? Just to be "consistent".

"name": [
{
"service": "foo",
"method": "Bar"
Copy link
Contributor

Choose a reason for hiding this comment

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

Still doesn't look right...

Copy link
Contributor

@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.

LGTM

@lyuxuan lyuxuan merged commit 6f3b6ff into grpc:master Oct 19, 2017
@menghanl menghanl added this to the 1.8 Release milestone Nov 7, 2017
@lock lock bot locked as resolved and limited conversation to collaborators Jan 18, 2019
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