Skip to content

feat(dogstatsd): decouple reader from processor#75

Merged
duncanista merged 3 commits intomainfrom
jordan.gonzalez/dogstatsd/decouple-reader-processor
Feb 17, 2026
Merged

feat(dogstatsd): decouple reader from processor#75
duncanista merged 3 commits intomainfrom
jordan.gonzalez/dogstatsd/decouple-reader-processor

Conversation

@duncanista
Copy link
Contributor

@duncanista duncanista commented Feb 12, 2026

What does this PR do?

Splits the single-threaded spin() loop into two cooperating tasks connected by a bounded channel:

  • Reader task — drains the UDP socket as fast as possible and sends raw packets into the channel
  • Processor task — receives packets from the channel, parses metrics, and forwards them to the aggregator

Also adds a configurable queue_size option to DogStatsDConfig (matching the Go agent's DD_DOGSTATSD_QUEUE_SIZE), defaulting to 1024.

Motivation

The previous spin() loop read one packet, parsed every metric in it, inserted them into the aggregator, and only then read the next packet. While the processor was busy, incoming packets accumulated in the kernel's SO_RCVBUF — which on Lambda is capped at ~416 KiB (~7
packets at 64 KB). Under burst traffic (e.g. 300k metrics), the kernel silently drops packets that overflow the buffer.

By decoupling the reader from the processor, buffering moves from kernel space (~416 KiB, not configurable on Lambda) to user space (queue_size × buffer_size ≈ 8 MB at defaults). The reader never blocks on processing, so the kernel buffer stays drained. This is where
the bulk of the metric recovery happens.

SVLS-8551

Notes

Describe how to test/QA your changes

Sending 300k metrics over a 3 second span, we get an improvement of receiving 145k to around 210k metrics

@duncanista duncanista requested review from a team as code owners February 12, 2026 19:10
@duncanista duncanista requested review from Copilot and duncanpharvey and removed request for a team February 12, 2026 19:10
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 refactors the DogStatsD server to improve throughput under burst traffic by decoupling packet reading from metric processing. Previously, the single-threaded spin() loop read one packet, parsed it, and inserted metrics into the aggregator before reading the next packet. This caused packets to accumulate in the kernel's SO_RCVBUF buffer, which is capped at ~416 KiB on Lambda, leading to packet drops.

The new architecture spawns a dedicated reader task that continuously drains the UDP socket into a bounded in-memory channel, while the main task processes packets from that channel. This moves buffering from kernel space to user space (configurable via queue_size, defaulting to ~8 MB), significantly reducing packet loss during bursts.

Changes:

  • Introduced a bounded channel (queue_size: 1024 packets) between socket reading and metric processing
  • Refactored spin() to spawn a reader task and process packets from a channel
  • Extracted insert_metrics, prepend_namespace, and process_packet as standalone functions
  • Added queue_size configuration option to DogStatsDConfig

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.

File Description
crates/dogstatsd/src/dogstatsd.rs Core refactoring: split single loop into read_loop (spawned task) and process_loop, added queue_size config, refactored helper functions to work without self reference
crates/dogstatsd/tests/integration_test.rs Added queue_size: None to all DogStatsDConfig test instances
crates/datadog-serverless-compat/src/main.rs Added queue_size: None to DogStatsDConfig initialization

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@duncanista duncanista force-pushed the jordan.gonzalez/dogstatsd/configurable-buffer-size branch from 95c907f to 6290b16 Compare February 12, 2026 22:27
@duncanista duncanista force-pushed the jordan.gonzalez/dogstatsd/decouple-reader-processor branch from 0684184 to 9fc1d1f Compare February 12, 2026 22:46
@duncanista duncanista force-pushed the jordan.gonzalez/dogstatsd/configurable-buffer-size branch from 6290b16 to 8a404ed Compare February 13, 2026 14:52
@duncanista duncanista force-pushed the jordan.gonzalez/dogstatsd/decouple-reader-processor branch from 97d3bca to d30cd5c Compare February 13, 2026 14:55
@duncanista duncanista force-pushed the jordan.gonzalez/dogstatsd/configurable-buffer-size branch from ed6c5b3 to a882e1d Compare February 13, 2026 17:47
@duncanista duncanista force-pushed the jordan.gonzalez/dogstatsd/decouple-reader-processor branch from d30cd5c to 57ef7fc Compare February 13, 2026 17:58
@duncanista duncanista requested a review from Copilot February 13, 2026 17:58
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

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@duncanista duncanista force-pushed the jordan.gonzalez/dogstatsd/configurable-buffer-size branch 2 times, most recently from c946ef0 to 1e4569b Compare February 17, 2026 17:03
Base automatically changed from jordan.gonzalez/dogstatsd/configurable-buffer-size to main February 17, 2026 17:14
@duncanista duncanista force-pushed the jordan.gonzalez/dogstatsd/decouple-reader-processor branch from 57ef7fc to 2b46ff8 Compare February 17, 2026 17:32
@duncanista duncanista requested a review from Copilot February 17, 2026 17:32
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

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@duncanista duncanista force-pushed the jordan.gonzalez/dogstatsd/decouple-reader-processor branch 2 times, most recently from 28ef884 to 47b13ad Compare February 17, 2026 21:14
Split spin() into a dedicated reader task and processor task connected
by a bounded channel. The reader drains the socket into user-space
buffering (~8 MB channel vs ~416 KiB kernel SO_RCVBUF cap), while the
processor handles parsing and aggregation independently. This prevents
head-of-line blocking where processing time caused kernel buffer
overflow and silent packet drops under high-volume metric bursts.

- Add configurable queue_size (DD_DOGSTATSD_QUEUE_SIZE, default 1024)
- Extract process_packet() and insert_metrics() as free functions
- Add read_loop() with cancellation-aware tokio::select! on reads
- Add process_loop() that drains remaining packets on shutdown
- Handle named pipe ConnectionReset as clean transport close
- Validate queue_size=0 with fallback to default

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@duncanista duncanista force-pushed the jordan.gonzalez/dogstatsd/decouple-reader-processor branch from 47b13ad to 9df7478 Compare February 17, 2026 21:22
Copy link
Collaborator

@duncanpharvey duncanpharvey left a comment

Choose a reason for hiding this comment

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

Looks good! Added a few comments for consideration.

@duncanista duncanista merged commit 91cfc1c into main Feb 17, 2026
26 checks passed
@duncanista duncanista deleted the jordan.gonzalez/dogstatsd/decouple-reader-processor branch February 17, 2026 22:17
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