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

Watch: inefficient event distribution #4627

Closed
gyuho opened this issue Feb 26, 2016 · 9 comments
Closed

Watch: inefficient event distribution #4627

gyuho opened this issue Feb 26, 2016 · 9 comments
Milestone

Comments

@gyuho
Copy link
Contributor

gyuho commented Feb 26, 2016

Separated from #3848.

inefficiency of event distribution through watch

problem: when reading value and sending out event through gRPC, we now unmarshal it first, then marshal it to byte slice again to distribute each time. This is inefficient.

@xiang90 xiang90 added this to the unplanned milestone Mar 4, 2016
@ajityagaty
Copy link
Contributor

@gyuho @xiang90 Would it be possible to provide more pointers for this one?

@xiang90
Copy link
Contributor

xiang90 commented Jun 17, 2016

@ajityagaty

Let's say we have 1000 clients watch on the same key foo. If there is a change on key foo, we need to broadcast the event to all 1000 clients. The core content, the key change, is same for 1000 clients. Right now, we still allocation space and encode the key changes 1000 times which is inefficient. Ideally, we should just do the key change encoding once.

@ajityagaty
Copy link
Contributor

@xiang90 Thanks for the explanation ! Could you also point me to the code path?

@xiang90 xiang90 modified the milestones: v3.2.0, unplanned Jul 12, 2016
@shuqilou
Copy link

shuqilou commented Sep 1, 2016

Do you have token some measures to resolve such inefficient watch mechanism?

@xiang90
Copy link
Contributor

xiang90 commented Sep 1, 2016

@shuqilou This can be an easy fix. We have not measure how efficient it is, it is actually not common to have 1000s of watchers watching on the same key. It would be great if you can:

  1. measure the overhead
  2. fix it

@xiang90
Copy link
Contributor

xiang90 commented Dec 14, 2016

I talked this issue with @awalterschulze : here is his suggestion

Disclaimer: This is a hack, but its not the worst hack, since clients won't notice.
Solution: You can concatenate two messages and unmarshal them as one.
Explanation:

message WatchResponse1 {
   int64 watch_id = 1;
}
message WatchResponse2 {
  repeated mvccpb.Event events = 2;
}

Marshal these two messages and concatenate their bytes and then send it.
Now this can be unmarshaled by

message WatchResponse {
  int64 watch_id = 1;

  repeated mvccpb.Event events = 2;
}

So this means that you can Marshal WatchResponse2 once and simply Marshal WatchResponse1 for every client.
The Marshaled bytes will look the same. You can even test this.
This is because each field is marshaled separately and simply concatenated. This is how the > serialization format works.

You can also swap the order in which you send these.
So first send the bytes for WatchResponse2 and then Marshal and send WatchResponse1.
The Unmarshalers of all protobuf libraries are capable (or should be by spec) of dealing with > receiving fields in different orders.

Also you don't even have to declare two messages right.
Because unset fields are not sent.
You can simply set the fields you want to Marshal.
So have one message

message WatchResponse {
  int64 watch_id = 1;

  repeated mvccpb.Event events = 2;
}

Set watch_id to nil or zero, depending on whether you are using proto2 or proto3 and then set the > events and Marshal.
Set events to nil and set watch_id to its unique value and Marshal
Concatenate the bytes and should have the same bytes you would have had, had you done this in > one go.

nullable=false in proto2, might introduce problems with this approach as watch_id will then always > be set.
But if you send the Marshaled events first and then append the Marshaled watch_id, the watch_id > value will be overridden with the new value. This is called merging of messages. I am not a fan of > this, but it works.

Side note repeated fields when not packed are also each marshaled separately.
So you could even marshal these separately and simply append them, but I don't think that will be > useful in this case.
But maybe if you have maybe duplicate events you could use this to optimize the serialization.

Also

Just embed your message into another struct which has the cached bytes.

type cached struct {
   WatchResponse *WatchResponse
   cache []byte
}

func (this *cached) Marshal() ([]byte, error) {
  // do the marshaling in the way you want by calling proto.Marshal / MarshalTo in appropriate ways
}

func (this *cached) IsProto() {}

You will need to implement the proto.Message interface.

If you are using the gogo/protobuf/codec
It probably has a bug gogo/protobuf#205
But when its fixed you might want to rather implement the MarshalTo method to get extra speed.

@xiang90
Copy link
Contributor

xiang90 commented Dec 14, 2016

@mqliang @sinsharat @ajityagaty @shuqilou

Hope this can help to explain this issue more.

@xiang90
Copy link
Contributor

xiang90 commented Mar 22, 2017

move to 3.3. with the help of gRPC proxy, watch load is not a huge issue.

@xiang90 xiang90 modified the milestones: v3.3.0, v3.2.0 Mar 22, 2017
@xiang90
Copy link
Contributor

xiang90 commented Jun 11, 2017

after second thought, we should just close this one. gRPC proxy should just solve this problem.

@xiang90 xiang90 closed this as completed Jun 11, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

4 participants