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

Implement capnproto replication #7659

Merged
merged 19 commits into from
Oct 17, 2024

Conversation

fpetkovski
Copy link
Contributor

@fpetkovski fpetkovski commented Aug 22, 2024

Our profiles from production show that a lot of CPU and memory in receivers is used for unmarshaling protobuf messages. Although it is not possible to change the remote-write format, we have the freedom to change the protocol used for replicating timeseries data.

This commit introduces a new feature in receivers where replication can be done using Cap'n Proto instead of gRPC + Protobuf. The advantage of the former protocol is that deserialization is far cheaper and fields can be accessed directly from the received message (byte slice) without allocating intermediate objects. There is an additional cost for serialization because we have to convert from Protobuf to the Cap'n proto format, but in our setup this still results in a net reduction in resource usage.

We have a split router-receiver setup and this is our resource usage in staging after enabling the new replication method:

image

Router usage did go up as well, but we still got an overall net reduction:

image

We can also experiment with other formats by having a more generic flag: receive.replication-format=...

  • I added CHANGELOG entry for this change.
  • Change is not relevant to the end user.

Changes

Verification

@fpetkovski fpetkovski force-pushed the capnproto-replication branch 4 times, most recently from 253f417 to 7042bea Compare August 22, 2024 15:06
Copy link
Member

@GiedriusS GiedriusS left a comment

Choose a reason for hiding this comment

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

Doesn't this include a whole new RPC framework? I'm very interested in this. Glad to finally see #7071 tackled. How do you think this compared to dRPC? And is it worth eschewing gRPC altogether?

pkg/receive/capnp_server.go Show resolved Hide resolved
@saswatamcode
Copy link
Member

Really interested in this too! Our profiles show similar for unmarshaling

@fpetkovski
Copy link
Contributor Author

fpetkovski commented Aug 26, 2024

Doesn't this include a whole new RPC framework? I'm very interested in this. Glad to finally see #7071 tackled. How do you think this compared to dRPC? And is it worth eschewing gRPC altogether?

Yes this comes with its own RPC framework which can be a double edge sword. It's one more thing to learn and troubleshoot if it goes wrong. I haven't looked into dRPC yet in practice, but maybe there is a way to abstract the RPC framework and not expose internals to end users, this way we can swap it out in future releases in a transparent manner. In this PR we expose the listen port as a parameter since we need separate connections for Cap'n Proto.

@squat
Copy link
Member

squat commented Aug 26, 2024

DRPC allows serving both gRPC and DRPC on the same socket to allow for incrementally upgrading/migrating a fleet. We might consider doing something similar whether we go with capnproto or DRPC

@GiedriusS
Copy link
Member

Mhm, we probably need to experiment more with the transparent approach before deciding what to do. For example, we could maybe deprecate --grpc* command line parameters and have a generic listener for RPCs. Perhaps let's just continue as is and mark this as experimental everywhere?

My only suggestion would be:

          {"address": "node-1:10901", "capnproto_address": "node-1:19391"},

Instead of having two addresses here, let's have "rpc_protocol": "grpc/capnproto" or something like that. I think it would be more future-proof with regard to transparent switching between RPC protocols on the receiving end.

@fpetkovski
Copy link
Contributor Author

Perhaps we can use something like https://github.com/soheilhy/cmux to multiplex multiple protocols on the same port. This way we don't need to pollute arguments with flags for each protocol. Let me see if this is easily doable, and also add some tests for the new RPC.

@fpetkovski fpetkovski force-pushed the capnproto-replication branch 2 times, most recently from d22809b to 5cfae4c Compare August 28, 2024 09:01
@fpetkovski fpetkovski marked this pull request as draft August 29, 2024 08:24
@fpetkovski fpetkovski force-pushed the capnproto-replication branch 2 times, most recently from 3df360a to 2fdeba7 Compare September 3, 2024 16:05
@fpetkovski fpetkovski force-pushed the capnproto-replication branch 4 times, most recently from b46ec5d to 79a7aa2 Compare September 27, 2024 11:30
@fpetkovski fpetkovski force-pushed the capnproto-replication branch 8 times, most recently from 2bea7c9 to b340ea0 Compare October 15, 2024 14:31
@fpetkovski fpetkovski marked this pull request as ready for review October 15, 2024 15:59
@GiedriusS
Copy link
Member

Adding e2e test: fpetkovski#7

Copy link
Contributor

@pedro-stanaka pedro-stanaka left a comment

Choose a reason for hiding this comment

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

lg

b.Run("unmarshal", func(b *testing.B) {
for i := 0; i < b.N; i++ {
msg, err := capnp.Unmarshal(bs)
require.NoError(b, err)
Copy link
Member

Choose a reason for hiding this comment

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

This gives me:

    /home/giedriusstatkevicius/dev/thanos/pkg/receive/writecapnp/marshal_bench_test.go:101: 
        	Error Trace:	/home/giedriusstatkevicius/dev/thanos/pkg/receive/writecapnp/marshal_bench_test.go:101
        	            				/usr/local/go/src/testing/benchmark.go:193
        	            				/usr/local/go/src/testing/benchmark.go:215
        	            				/usr/local/go/src/runtime/asm_amd64.s:1700
        	Error:      	Received unexpected error:
        	            	unmarshal: short header section

Does this benchmark work for you?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Looks like we are unmarshaling proto bytes into a capnproto request. I've fixed the benchmark, all of them should be passing now.

GiedriusS
GiedriusS previously approved these changes Oct 17, 2024
Copy link
Member

@GiedriusS GiedriusS left a comment

Choose a reason for hiding this comment

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

make docs failing 😬

fpetkovski and others added 19 commits October 17, 2024 10:56
Our profiles from production show that a lot of CPU and memory in receivers
is used for unmarshaling protobuf messages. Although it is not possible to change
the remote-write format, we have the freedom to change the protocol used
for replicating timeseries data.

This commit introduces a new feature in receivers where replication can be done
using Cap'n Proto instead of gRPC + Protobuf. The advantage of the former protocol
is that deserialization is far cheaper and fields can be accessed directly from
the received message (byte slice) without allocating intermediate objects.
There is an additional cost for serialization because we have to convert from
Protobuf to the Cap'n proto format, but in our setup this still results in a net
reduction in resource usage.

Signed-off-by: Filip Petkovski <filip.petkovsky@gmail.com>
Signed-off-by: Filip Petkovski <filip.petkovsky@gmail.com>
Signed-off-by: Filip Petkovski <filip.petkovsky@gmail.com>
Signed-off-by: Filip Petkovski <filip.petkovsky@gmail.com>
Signed-off-by: Filip Petkovski <filip.petkovsky@gmail.com>
Signed-off-by: Filip Petkovski <filip.petkovsky@gmail.com>
Signed-off-by: Filip Petkovski <filip.petkovsky@gmail.com>
Signed-off-by: Filip Petkovski <filip.petkovsky@gmail.com>
Signed-off-by: Filip Petkovski <filip.petkovsky@gmail.com>
Co-authored-by: Pedro Tanaka <pedro.tanaka@shopify.com>
Signed-off-by: Filip Petkovski <filip.petkovsky@gmail.com>
Signed-off-by: Filip Petkovski <filip.petkovsky@gmail.com>
Signed-off-by: Giedrius Statkevičius <giedrius.statkevicius@vinted.com>
Signed-off-by: Giedrius Statkevičius <giedrius.statkevicius@vinted.com>
Signed-off-by: Filip Petkovski <filip.petkovsky@gmail.com>
Signed-off-by: Filip Petkovski <filip.petkovsky@gmail.com>
Signed-off-by: Filip Petkovski <filip.petkovsky@gmail.com>
Signed-off-by: Filip Petkovski <filip.petkovsky@gmail.com>
Signed-off-by: Filip Petkovski <filip.petkovsky@gmail.com>
Signed-off-by: Filip Petkovski <filip.petkovsky@gmail.com>
@fpetkovski
Copy link
Contributor Author

Rebased on main and regenerated docs. We should be good to go!

We can keep iterating over time, I'd like to see how we can leverage interning in the future.

@GiedriusS GiedriusS enabled auto-merge (squash) October 17, 2024 09:39
@GiedriusS GiedriusS merged commit 65b664c into thanos-io:main Oct 17, 2024
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants