Skip to content

fix(ddtrace/tracer): resolve race conditions in write pipeline#3821

Merged
dd-mergequeue[bot] merged 7 commits into
DataDog:mainfrom
kakkoyun:kakkoyun/fix_race_writer_payload
Aug 27, 2025
Merged

fix(ddtrace/tracer): resolve race conditions in write pipeline#3821
dd-mergequeue[bot] merged 7 commits into
DataDog:mainfrom
kakkoyun:kakkoyun/fix_race_writer_payload

Conversation

@kakkoyun

@kakkoyun kakkoyun commented Jul 31, 2025

Copy link
Copy Markdown
Member

What does this PR do?

Eliminates race conditions in the tracer's write pipeline by implementing comprehensive thread-safe interfaces and fixing a critical deadlock bug that was introduced during the initial race condition fix.

Motivation

During analysis of the tracer's writer implementation, multiple race conditions were discovered that could cause serious issues under high concurrency:

Original Race Conditions:

  • Payload buffer races in agentTraceWriter between add() and flush() operations
  • Data races in bytes.Buffer operations detected by Go's race detector
  • Buffer corruption causing panics like slice bounds out of range
  • Lost or corrupted traces when payload operations interfere with each other

Solution

Phase 1: Fix Writer Race Conditions

  • Added mu sync.Mutex to agentTraceWriter struct
  • Protected add() operations with mutex to prevent concurrent payload access
  • Protected payload swap in flush() to ensure atomic replacement

Phase 2: Comprehensive Payload Interface Redesign

  • Added private payload interfaces (payloadWriter, payloadReader, payload)
  • Implemented safePayload wrapper with RWMutex for thread-safe operations
  • Renamed payload to unsafePayload, replaced with safe interface throughout codebase
  • Optimized itemCount() using direct atomic access for performance
  • Renamed incrementCount() to recordItem() for clarity
  • Extended thread safety to all payload operations across the entire codebase

Key Fixes

  1. Deadlock Resolution: Fixed recursive locking in agentTraceWriter.add()
  2. Thread-Safe Payload Interface: All payload operations now race-condition free
  3. Performance Optimizations: Efficient atomic operations where appropriate
  4. Comprehensive Testing: Added extensive concurrent access tests

Files Changed

Core Writer Components:

  • ddtrace/tracer/writer.go - Fixed deadlock, added mutex synchronization
  • ddtrace/tracer/payload.go - Thread-safe interface redesign
  • ddtrace/tracer/transport.go - Updated to use safe payload interface

CI Visibility Components:

  • ddtrace/tracer/civisibility_*.go - Updated for thread-safe payload usage

Testing & Configuration:

  • ddtrace/tracer/*_test.go - Added comprehensive race condition tests
  • ddtrace/tracer/option.go - Updated payload initialization

Testing

** Race Condition Tests:**

  • TestAgentWriterRaceCondition: Spawns multiple goroutines doing concurrent add/flush operations
  • TestAgentWriterTraceCountAccuracy: Validates trace counting accuracy under concurrency
  • TestWorker: Now passes (was timing out due to deadlock)
  • Payload concurrency tests: Comprehensive testing of new thread-safe interfaces

Verification:

  • Tests consistently fail with race detector on unfixed code (including panics and deadlocks)
  • Tests consistently pass with no race detector warnings after all fixes
  • All existing tests pass with no regressions
  • Performance maintained with optimized atomic operations where possible

Backward Compatibility

  • No breaking API changes - all changes are internal implementation details
  • All existing functionality preserved
  • Performance characteristics maintained or improved

Reviewer's Checklist

  • Changed code has unit tests for its functionality at or near 100% coverage.
  • System-Tests covering this feature have been added and enabled with the va.b.c-dev version tag.
  • There is a benchmark for any new code, or changes to existing code.
  • If this interacts with the agent in a new way, a system test has been added.
  • New code is free of linting errors. You can check this by running ./scripts/lint.sh locally.
  • Add an appropriate team label so this PR gets put in the right place for the release notes.
  • Non-trivial go.mod changes, e.g. adding new modules, are reviewed by @DataDog/dd-trace-go-guild.

@kakkoyun
kakkoyun requested a review from a team as a code owner July 31, 2025 10:02
Comment thread ddtrace/tracer/writer.go Outdated
@kakkoyun
kakkoyun marked this pull request as draft July 31, 2025 10:50
@kakkoyun
kakkoyun force-pushed the kakkoyun/fix_race_writer_payload branch from ed249b2 to 7c27333 Compare July 31, 2025 16:45
@kakkoyun kakkoyun changed the title fix(ddtrace/tracer): resolve race condition in agentTraceWriter fix(ddtrace/tracer): resolve race conditions in write pipeline Jul 31, 2025
@kakkoyun
kakkoyun marked this pull request as ready for review July 31, 2025 16:50
@kakkoyun
kakkoyun requested a review from darccio July 31, 2025 16:50
@kakkoyun
kakkoyun force-pushed the kakkoyun/fix_race_writer_payload branch from 7c27333 to f021440 Compare July 31, 2025 17:11
Comment thread ddtrace/tracer/civisibility_payload.go Outdated
Comment thread ddtrace/tracer/payload.go
Comment thread ddtrace/tracer/payload.go Outdated
Comment thread ddtrace/tracer/writer.go
@kakkoyun
kakkoyun marked this pull request as draft August 5, 2025 09:12
@kakkoyun
kakkoyun marked this pull request as ready for review August 5, 2025 14:47
@kakkoyun
kakkoyun force-pushed the kakkoyun/fix_race_writer_payload branch from 727f1f3 to 1cd4101 Compare August 5, 2025 15:21
@kakkoyun
kakkoyun requested a review from darccio August 6, 2025 07:45
Comment thread ddtrace/tracer/writer.go
@kakkoyun
kakkoyun marked this pull request as draft August 26, 2025 16:47
@kakkoyun
kakkoyun force-pushed the kakkoyun/fix_race_writer_payload branch 2 times, most recently from 9bf4c8c to aa94938 Compare August 26, 2025 17:03
Comment thread ddtrace/tracer/payload.go
Add TestAgentWriterRaceCondition and TestAgentWriterTraceCountAccuracy
to reproduce and validate race conditions between add() and flush() operations.

These tests demonstrate that concurrent access to payload buffers can cause:
- Data races in bytes.Buffer operations
- Buffer corruption leading to slice bounds panics
- Incorrect trace counting due to counter races

The tests consistently fail with -race detector on unfixed code, providing
a reliable way to validate the race condition and verify fixes.

Signed-off-by: Kemal Akkoyun <[email protected]>
Fix race condition between add() and flush() operations by implementing
mutex-based synchronization for payload access.

**Problem**: Multiple goroutines could access the same payload buffer
concurrently, causing:
- Data races in bytes.Buffer operations
- Buffer corruption and panics (slice bounds out of range)
- Lost or corrupted traces

**Solution**:
- Add mutex (mu) to agentTraceWriter struct
- Protect add() operations with mutex to prevent concurrent payload access
- Protect payload swap in flush() to ensure atomic replacement

**Verification**:
- Race condition tests now pass consistently with no race detector warnings
- No panics or data corruption
- All existing functionality preserved

Signed-off-by: Kemal Akkoyun <[email protected]>
…fe interface

- Add private payload interfaces (payloadWriter, payloadReader, payload)
- Implement safePayload wrapper with RWMutex for thread-safe operations
- Rename payload to unsafePayload, replace with safe interface
- Optimize itemCount() using direct atomic access
- Rename incrementCount() to recordItem() for clarity
- Add comprehensive concurrent access tests
- All payload operations now race-condition free

Signed-off-by: Kemal Akkoyun <[email protected]>
@kakkoyun
kakkoyun force-pushed the kakkoyun/fix_race_writer_payload branch from aa94938 to ccc2a6b Compare August 27, 2025 11:39
@kakkoyun
kakkoyun requested a review from darccio August 27, 2025 14:31
@kakkoyun
kakkoyun marked this pull request as ready for review August 27, 2025 14:31
@kakkoyun
kakkoyun requested a review from a team as a code owner August 27, 2025 14:31
@kakkoyun
kakkoyun force-pushed the kakkoyun/fix_race_writer_payload branch from 2db4b4f to d2ba67f Compare August 27, 2025 14:35
…tion optimizations

Adds missing benchmarks for payload, writer, and transport operations to support
data-driven performance optimization.

Benchmark results (Darwin 24.6.0 arm64, go1.25.0):

Performance comparison (main vs optimized branch):
- stats() calls: 15x slower per call (0.25ns → 3.9ns)
- Lock frequency: 40-60% reduction (5+ → 2-3 per flush)
- Payload throughput: No regression (~365ns vs ~370ns)
- Net effect: Lower lock contention under high concurrency ✅

Memory optimization experiments (all reverted after benchmarking):
- Header pools: 8% performance regression, no allocation benefit
- Buffer pre-allocation: 62% slower, 3x memory usage
- Smart buffer growth: 43% slower, 2x memory usage
- Reader pools: Overhead exceeds benefit
@kakkoyun
kakkoyun force-pushed the kakkoyun/fix_race_writer_payload branch from d2ba67f to fd699fe Compare August 27, 2025 14:43
@kakkoyun

Copy link
Copy Markdown
Member Author

/merge

@dd-devflow-routing-codex

dd-devflow-routing-codex Bot commented Aug 27, 2025

Copy link
Copy Markdown

View all feedbacks in Devflow UI.

2025-08-27 14:49:57 UTC ℹ️ Start processing command /merge


2025-08-27 14:50:13 UTC ℹ️ MergeQueue: waiting for PR to be ready

This merge request is not mergeable yet, because of pending checks/missing approvals. It will be added to the queue as soon as checks pass and/or get approvals.
Note: if you pushed new commits since the last approval, you may need additional approval.
You can remove it from the waiting list with /remove command.


2025-08-27 15:00:41 UTC ℹ️ MergeQueue: merge request added to the queue

The expected merge time in main is approximately 13m (p90).


2025-08-27 15:15:31 UTC ℹ️ MergeQueue: This merge request was merged

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants