-
Notifications
You must be signed in to change notification settings - Fork 15
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
send raw table update to clients #14
Conversation
Don't over allocate memory for json Marshal/Unmarshal, but fill needed structs directly via reflect. |
later we can generate row_types.go file from ovnnb schema files via go generate |
now it have ugly things to parse empty sets and bools. why in Connection table max_backoff passed via set ? |
Thanks for working on this! I have some major comments:
What do you think? (it's ok to continue your test this way as POC) |
|
@hzhou8 i'm finish pr. |
Thanks @vtolstov ! I need to review it in more detail, but here are just some minor things:
|
@hzhou8 thanks, if fix suggested |
@@ -49,12 +48,12 @@ var ( | |||
const ( | |||
defaultTCPAddress = "127.0.0.1:6640" | |||
defaultUnixAddress = "/var/run/openvswitch/ovnnb_db.sock" | |||
SSL = "ssl" |
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.
Still unrelated changes :)
dont try to convert between types via json marshal/unmarshal send rpc payload directly as table updates allow to consumers convert it to native structs via provided functions MapToStruct and StructToMap Signed-off-by: Vasiliy Tolstov <[email protected]>
@hzhou8 any chance to get this merged? or i need to fix something more? |
@vtolstov sorry that I was distracted last week, but I did spent some time reviewing it, and I am still not very clear about the purpose of this change. Is it purely for performance optimization (i.e. to avoid marshal/unmarshal) or is it also an interface change (i.e. the format changed of tableUpdates generated by update())? If it is for performance, then is there test result telling how much did it improve? It did add a lot more complexity and if the performance gain is small I would be hesitating. I didn't see enough tests to tell the purpose of the change. I probably need more time to write some tests myself to understand more about it. I am also requesting adding more tests for such big change to make sure there is no regression problem. |
Thanks, this is very small change for sending updates, most of the changes needed for go-ovn to get native structs from map. |
So this is compatible change that not change for consumers anything. |
Mostly this is not have major speedup, but minimize unneeded allocations. |
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.
Hi @vtolstov, sorry for the slow response. I did some tests myself to understand the intent of the change. Please see my comments/questions inlined. In addition, here are some more general comments:
-
Please update the example/play_with_ovs.go, which is now broken by this change.
-
The module convert.go seems not used in this repo, so I guess it is supposed to be used as an API by other project, e.g. go-ovn, right? If so, please add basic test cases to test the interfaces and also as example to demonstrate how to use them.
-
If we want to treat this as a prototype and if adding documentation and test cases seem to be too heavy for the prototype phase, then I'd suggest to merge it to a branch instead of master. When the prototype is proved mature and all the documentation and tests are ready, we can merge it to master.
validRowUpdate := make(map[string]RowUpdate) | ||
validRowUpdate["uuid"] = RowUpdate{} | ||
validUpdate["table"] = validRowUpdate | ||
// Valid dummy update should pass https://tools.ietf.org/html/rfc7047#section-4.1.6 |
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 update the comments, since you are now changing the test from testing a dummy update to testing a non-dummy update, i.e. with a new row insertion.
validUpdate["table"] = validRowUpdate | ||
// Valid dummy update should pass https://tools.ietf.org/html/rfc7047#section-4.1.6 | ||
updates := make(map[string]interface{}) | ||
updates["Test_Table"] = make(map[string]interface{}) |
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 not needed.
|
||
err = update(nil, []interface{}{"hello", validUpdate}, &reply) | ||
tableUpdate := make(map[string]interface{}) | ||
tableUpdate["Test_UUID"] = make(map[string]interface{}) |
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 needed
tableUpdate["Test_UUID"] = make(map[string]interface{}) | ||
|
||
newUpdate := make(map[string]interface{}) | ||
newUpdate["new"] = make(map[string]interface{}) |
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 needed
func rawToRowUpdates(raw map[string]interface{}) (map[string]map[string]RowUpdate, error) { | ||
rowUpdates := make(map[string]map[string]RowUpdate) | ||
|
||
for tbl, opt := range raw { |
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 the variable named "opt" here? Is it more straightforward to be: "tblUpdate"?
continue | ||
} | ||
rowUpdates[tbl] = make(map[string]RowUpdate) | ||
for uuid, val := range opts { |
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.
Suggest to rename "val" to "rowUpdate"
for tbl, opt := range raw { | ||
opts, ok := opt.(map[string]interface{}) | ||
if !ok { | ||
return nil, fmt.Errorf("invalid row update: %v", opt) |
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.
Error message can be enhanced to "invalid TableUpdate"
rowUpdates := make(map[string]map[string]RowUpdate) | ||
|
||
for tbl, opt := range raw { | ||
opts, ok := opt.(map[string]interface{}) |
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.
opts => rowUpdates
for k, vv := range vals { | ||
v, ok := vv.(map[string]interface{}) | ||
if !ok { | ||
return nil, fmt.Errorf("invalid row update: %v", vv) |
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 is better to print "old" or "new" in the error message to help debugging
@@ -182,25 +181,58 @@ func echo(client *rpc2.Client, args []interface{}, reply *[]interface{}) error { | |||
return nil | |||
} | |||
|
|||
func rawToRowUpdates(raw map[string]interface{}) (map[string]map[string]RowUpdate, 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.
Originally, the rowUpdate uses Row struct, which has OvsMap, OvsSet, etc. embedded. Now this patch use a slice to represent the value of each column. For example, originally a column "external_ids" is like:
"external_ids":libovsdb.OvsMap{GoMap:map[interface {}]interface {}{"docker":"made-for-each-other", "go":"awesome"}}
Now it is changed to:
"external_ids":[]interface {}{"map", []interface {}{[]interface {}{"docker", "made-for-each-other"}, []interface {}{"go", "awesome"}}}
The first element in the slice is the string "map", probably representing the data type, and the second element is a slice of interface{}, each element representing a key-value pair in the "external_ids" column. Each key-value pair is again represented by a slice of interface{} which has two element: the first element is the key, and the second element is the value.
Is this slices of slices of slices the desired output of this function? How is it more "native" than the original implementation? Could you explain a little more? I think it is better to have some formal documentation to describe the design. I'd also insist to have some basic test cases to verify if the function behaves as expected, i.e. given some typical input of "raw", make sure the return value is expected as per the design.
close in favour of #28 |
Signed-off-by: Vasiliy Tolstov [email protected]