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

Dogstatds: Enable consolidating different metrics in a single payload #561

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

nappairam
Copy link
Contributor

@nappairam nappairam commented Mar 3, 2025

Changes:

Length prefix is only needed in case of Unix Stream and not for Datagram whether it Unix or UDP sockets.

Reference:
https://github.com/DataDog/datadog-go/blob/bae3560e5c664d64b71c0e8ca89326afa362e12a/statsd/uds.go#L62

Earlier, it was assumed that RemoteAddr::Unixgram has to prefix length which made the code complex padding length, to avoid copy before sending.

Since length prefix is not needed for UDP sockets, made the code simple by pushing the length padding role until the end of send.

This allows me to send different metrics in a same UDP packet

TODO:

  • Pending testing
  • Add tests for consolidating metrics

@nappairam nappairam force-pushed the metrics-agg branch 2 times, most recently from abdfacf to baa97b9 Compare March 3, 2025 09:51
@tobz
Copy link
Member

tobz commented Mar 3, 2025

So before you get too far, a few things...

First, I don't want to have two write calls for the length prefix and the payload buffer when we can simply add the length header in the payload writer. I intentionally wrote the current code in this way to avoid that.

Secondly, the Datadog Agent can accept multiple metrics bundled in a single length-delimited frame, so long as they're newline-delimited so that they can be split apart. This is, overall, more efficient than trying to make each one length-delimited, since we only pay the 4 byte length delimiter cost once per overall buffer instead of for each metric.

Length prefix is only needed in case of Stream and not for Datagram whether it is Unix or UDP socket.
Reference:
https://github.com/DataDog/datadog-go/blob/bae3560e5c664d64b71c0e8ca89326afa362e12a/statsd/uds.go#L62
Earlier, it was assumed that RemoteAddr::Unixgram has to also prefix length,
which made the code complex to pad sent buffer with length, to avoid copy before sending.

Since length prefix is no longer needed, make the code simple by pushing the length padding
appending only before send.
This avoids sending each metric in a separate UDP packet and saves CPU cycles.
@nappairam
Copy link
Contributor Author

Thanks @tobz for early comments.

First, I don't want to have two write calls for the length prefix and the payload buffer when we can simply add the length header in the payload writer. I intentionally wrote the current code in this way to avoid that.

Yes, I understood it was intentional. But I was thinking, since we had length_prefix enabled for UDS Datagram, we did not want to memmove or memcopy before writing.
Cause we cannot split and send one frame with two write calls in case of Datagram. And it makes sense to use the PayloadWriter to have length_prefix as part of it.

But with Streams, IMHO there is no need for that, since we can write twice which is still fine.

And we use write_all for Stream, which can still call write system call multiple times, in case of partial success. I did not yet fully understand, what is the concern to use two Stream write here.

If we want single write, it is still possible to use length_prefix in PayloadWriter. But we had to constantly do memmove/memcopy, since we do not know when will the max_payload_len will be full and add length prefix. So I opted for two writes for Stream alone

Secondly, the Datadog Agent can accept multiple metrics bundled in a single length-delimited frame, so long as they're newline-delimited so that they can be split apart. This is, overall, more efficient than trying to make each one length-delimited, since we only pay the 4 byte length delimiter cost once per overall buffer instead of for each metric.

Yes correct. This is what I am trying to do.

  • Remove length prefix from the PayloadWriter
  • Aggregate/consolidate different metrics in a single payload
  • Send length of payload (with multiple metrics inside) ONLY in case of Unix Stream
  • And then send Payload fully

Please let me know if I miss something. Would be happy to refactor based on comments.

@tobz
Copy link
Member

tobz commented Mar 5, 2025

Yes, I understood it was intentional. But I was thinking, since we had length_prefix enabled for UDS Datagram, we did not want to memmove or memcopy before writing. Cause we cannot split and send one frame with two write calls in case of Datagram. And it makes sense to use the PayloadWriter to have length_prefix as part of it.

Actually, now that I look closer... I mistakenly had is_length_prefixed returning true for UDS datagram, when it should be false.

And we use write_all for Stream, which can still call write system call multiple times, in case of partial success. I did not yet fully understand, what is the concern to use two Stream write here.

That's true, but in the optimal case, it uses a single write syscall. If we write the length prefix in one call, and then the rest of the payload in another call... then we're always making two write syscalls. This is what we want to avoid: always making at least two, instead of sometimes making two.

If we want single write, it is still possible to use length_prefix in PayloadWriter. But we had to constantly do memmove/memcopy, since we do not know when will the max_payload_len will be full and add length prefix. So I opted for two writes for Stream alone

We don't have to move or copy anything, as PayloadWriter already tracks most of this state. We just need to add a little more state (current payload size) so that subsequent writes can determine if they can append to the current payload or if a new payload must be marked.

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.

2 participants