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

send raw table update to clients #14

Closed
wants to merge 1 commit into from
Closed

send raw table update to clients #14

wants to merge 1 commit into from

Conversation

vtolstov
Copy link

Signed-off-by: Vasiliy Tolstov v.tolstov@unistack.org

@vtolstov
Copy link
Author

Don't over allocate memory for json Marshal/Unmarshal, but fill needed structs directly via reflect.
Also send native row structs in updates, so go-ovn does not need to convert it back, but use only type assertion.
Pr to go-ovn repo prepeared (now only convert logical switch operations)

@vtolstov vtolstov requested a review from hzhou8 July 28, 2019 23:03
@vtolstov
Copy link
Author

later we can generate row_types.go file from ovnnb schema files via go generate

@vtolstov
Copy link
Author

now it have ugly things to parse empty sets and bools. why in Connection table max_backoff passed via set ?

@hzhou8
Copy link

hzhou8 commented Jul 30, 2019

Thanks for working on this! I have some major comments:

  1. We should not introduce any OVN specific code in libovsdb. In rawToRowUpdates(), it should be generic enough. All OVN specific code should be in go-ovn.

  2. We may consider auto generate the code for any given OVSDB schema. The code generation tool should be in libovsdb. In go-ovn it can use this tool to generate code for processing.

What do you think?

(it's ok to continue your test this way as POC)

@vtolstov
Copy link
Author

@hzhou8

  1. this changes not only ovn related. This changes ovsdb related. So i think that this is proper place. For example i create SSL Connection and other structs to libovsdb. So when we create client for openvswitch db, we can work with native go structs and not maps
  2. code generator for libovsdb is fine it can generate needed structs for all message updates. But why it needed for go-ovn?

@vtolstov vtolstov changed the title [WIP] send native structs in table updates send raw table update to clients Jul 31, 2019
@vtolstov
Copy link
Author

vtolstov commented Jul 31, 2019

@hzhou8 i'm finish pr.
i think that now it completed, also i'm push to go-ovn repo some files that already uses this pr

@hzhou8
Copy link

hzhou8 commented Jul 31, 2019

Thanks @vtolstov ! I need to review it in more detail, but here are just some minor things:

  1. Could you add more detailed description in the commit message?

  2. The SSL/TCP const rename is not related to this change, so please split to another PR.

  3. There are still "ovn" in comments. It is better to be removed.

@vtolstov
Copy link
Author

@hzhou8 thanks, if fix suggested

rpc_test.go Outdated Show resolved Hide resolved
@@ -49,12 +48,12 @@ var (
const (
defaultTCPAddress = "127.0.0.1:6640"
defaultUnixAddress = "/var/run/openvswitch/ovnnb_db.sock"
SSL = "ssl"
Copy link

Choose a reason for hiding this comment

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

Still unrelated changes :)

convert.go Show resolved Hide resolved
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 <v.tolstov@unistack.org>
@vtolstov vtolstov requested a review from hzhou8 August 8, 2019 18:43
@vtolstov
Copy link
Author

vtolstov commented Aug 8, 2019

@hzhou8 any chance to get this merged? or i need to fix something more?

@hzhou8
Copy link

hzhou8 commented Aug 8, 2019

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

@vtolstov
Copy link
Author

vtolstov commented Aug 8, 2019

Thanks, this is very small change for sending updates, most of the changes needed for go-ovn to get native structs from map.
Also this adds some basic validation of format of row updates that must be like in rfc. Older version of test sent invalid row update.
Format of row update not changed, mostly this changes to avoid marshal/unmarshal. And fills UUID

@vtolstov
Copy link
Author

vtolstov commented Aug 8, 2019

So this is compatible change that not change for consumers anything.

@vtolstov
Copy link
Author

vtolstov commented Aug 8, 2019

Mostly this is not have major speedup, but minimize unneeded allocations.

Copy link

@hzhou8 hzhou8 left a 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:

  1. Please update the example/play_with_ovs.go, which is now broken by this change.

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

  3. 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
Copy link

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{})
Copy link

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{})
Copy link

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{})
Copy link

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 {
Copy link

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 {
Copy link

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)
Copy link

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{})
Copy link

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)
Copy link

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) {
Copy link

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.

@vtolstov
Copy link
Author

vtolstov commented Feb 3, 2021

close in favour of #28

@vtolstov vtolstov closed this Feb 3, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants