Skip to content

feat(dogstatsd): add configurable SO_RCVBUF for UDP socket#73

Merged
duncanista merged 7 commits intomainfrom
jordan.gonzalez/dogstatsd/configurable-so-rcvbuf
Feb 13, 2026
Merged

feat(dogstatsd): add configurable SO_RCVBUF for UDP socket#73
duncanista merged 7 commits intomainfrom
jordan.gonzalez/dogstatsd/configurable-so-rcvbuf

Conversation

@duncanista
Copy link
Contributor

@duncanista duncanista commented Feb 12, 2026

What does this PR do?

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

@duncanista duncanista requested review from a team as code owners February 12, 2026 18:24
@duncanista duncanista requested review from Lewis-E and removed request for a team February 12, 2026 18:24
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>
@duncanista duncanista force-pushed the jordan.gonzalez/dogstatsd/configurable-so-rcvbuf branch from 29f7be9 to e0ca5d1 Compare February 12, 2026 18:26
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

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> to DogStatsDConfig and use a custom UDP socket creation path that can set SO_RCVBUF.
  • Update call sites/tests to populate the new config field.
  • Add socket2 dependency (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.

Comment on lines 232 to 236
metric_namespace,
#[cfg(all(windows, feature = "windows-pipes"))]
windows_pipe_name,
so_rcvbuf: None,
};
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

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

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
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))?;

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

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");
Copy link
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Too verbose so far, might improve this in another PR with a cleaner approach

@duncanista duncanista merged commit 383fd14 into main Feb 13, 2026
25 checks passed
@duncanista duncanista deleted the jordan.gonzalez/dogstatsd/configurable-so-rcvbuf branch February 13, 2026 14:49
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.

3 participants