feat(dogstatsd): add configurable SO_RCVBUF for UDP socket#73
feat(dogstatsd): add configurable SO_RCVBUF for UDP socket#73duncanista merged 7 commits intomainfrom
Conversation
Use the `socket2` crate to create the UDP socket with a configurable receive buffer size (`SO_RCVBUF`). On constrained environments like AWS Lambda, the kernel caps `SO_RCVBUF` at ~416 KiB (2x rmem_max), which can cause silent packet loss under burst traffic. This change lets operators tune the buffer via `DD_DOGSTATSD_SO_RCVBUF`, matching the Go agent's configuration option. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
29f7be9 to
e0ca5d1
Compare
There was a problem hiding this comment.
Pull request overview
This PR adds an optional SO_RCVBUF configuration to the DogStatsD UDP listener by switching socket creation to socket2, enabling operators/consumers to increase the UDP receive buffer and reduce packet loss under burst traffic (e.g., constrained environments like AWS Lambda).
Changes:
- Add
so_rcvbuf: Option<usize>toDogStatsDConfigand use a custom UDP socket creation path that can setSO_RCVBUF. - Update call sites/tests to populate the new config field.
- Add
socket2dependency (and update lockfile accordingly).
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/dogstatsd/src/dogstatsd.rs | Adds so_rcvbuf config and new UDP socket creation using socket2 + logging. |
| crates/dogstatsd/tests/integration_test.rs | Updates test config structs to include the new so_rcvbuf field. |
| crates/dogstatsd/Cargo.toml | Adds socket2 dependency and expands Tokio features. |
| crates/datadog-serverless-compat/src/main.rs | Updates DogStatsD config construction to include so_rcvbuf. |
| Cargo.lock | Records new dependency resolution (adds socket2 0.5.x alongside existing socket2 0.6.x). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| metric_namespace, | ||
| #[cfg(all(windows, feature = "windows-pipes"))] | ||
| windows_pipe_name, | ||
| so_rcvbuf: None, | ||
| }; |
There was a problem hiding this comment.
The PR description mentions configuring this via DD_DOGSTATSD_SO_RCVBUF, but so_rcvbuf is still hard-coded to None here. If this binary is expected to expose the knob, wire so_rcvbuf from env/config (with parsing/validation) so the option is actually usable.
There was a problem hiding this comment.
Not adding an option a project I don't manage – ensuring that it works as before
| ) | ||
| })?; | ||
|
|
||
| let socket = Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::UDP))?; |
There was a problem hiding this comment.
The socket is always created as IPv4 (Domain::IPV4). If addr is an IPv6 SocketAddr, bind will fail (and tokio::net::UdpSocket::bind previously supported IPv6). Choose the socket Domain based on whether the parsed SocketAddr is v4 or v6.
| let socket = Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::UDP))?; | |
| let domain = if socket_addr.is_ipv4() { | |
| Domain::IPV4 | |
| } else { | |
| Domain::IPV6 | |
| }; | |
| let socket = Socket::new(domain, Type::DGRAM, Some(Protocol::UDP))?; |
There was a problem hiding this comment.
For now, we are always sending localhost addresses, might add in another PR
| let socket = create_udp_socket(&addr, config.so_rcvbuf) | ||
| .await | ||
| .expect("couldn't bind to address"); | ||
| .expect("couldn't create UDP socket"); |
There was a problem hiding this comment.
Possibility of not-very-specific errors here, since the raw io::Error from bind() and set_nonblocking()
could look identical. Could be more specific with something like
socket.set_recv_buffer_size(buf_size).map_err(|e| {
std::io::Error::new(e.kind(), format!("failed to set SO_RCVBUF to {}: {}", buf_size, e))
})?;
socket.set_nonblocking(true).map_err(|e| {
std::io::Error::new(e.kind(), format!("failed to set nonblocking: {}", e))
})?;
socket.bind(&socket_addr.into()).map_err(|e| {
std::io::Error::new(e.kind(), format!("failed to bind to '{}': {}", addr, e))
})?;
Claude Comment:
failed to bind to '127.0.0.1:8125': Address already in use: couldn't create UDP socket
vs the current output which would just be:
Address already in use: couldn't create UDP socket
Non-blocking comment, low change of things breaking, but the socket2 docs do specifically mention they don't do error handling.
There was a problem hiding this comment.
Too verbose so far, might improve this in another PR with a cleaner approach
What does this PR do?
Use the
socket2crate to create the UDP socket with a configurable receive buffer size (SO_RCVBUF).On constrained environments like AWS Lambda, the kernel caps
SO_RCVBUFat ~416 KiB (2x rmem_max), which can cause silent packet loss under burst traffic.This change lets operators tune the buffer via
so_rcvbuf.Meant to be used with agents by
DD_DOGSTATSD_SO_RCVBUF, matching the Go agent's configuration option.Motivation
Avoid losing data.
SVLS-8549
Describe how to test/QA your changes
Manual testing and plugging it into other agents.