Skip to content

Commit

Permalink
profiler: test random execution trace collection with a fixed seed
Browse files Browse the repository at this point in the history
To de-flake TestExecutionTraceRandom, provide a fixed-seed random number
generator so that the results are deterministic. This is done through a
non-exported profiler option so it's easy to provide in specific test
cases (only one so far). Developers should remove this option while
working on anything that might rely on real randomness, verify that it
works as intended, and then add the option back to get reliable tests in
CI.

Fixes #2529
  • Loading branch information
nsrip-dd committed Apr 1, 2024
1 parent 6b4d009 commit 7dc4cf4
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 3 deletions.
9 changes: 9 additions & 0 deletions profiler/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"context"
"encoding/json"
"fmt"
"math/rand"
"net"
"net/http"
"net/url"
Expand Down Expand Up @@ -111,6 +112,7 @@ type config struct {
logStartup bool
traceConfig executionTraceConfig
endpointCountEnabled bool
rng *rand.Rand
}

// logStartup records the configuration to the configured logger in JSON format
Expand Down Expand Up @@ -574,3 +576,10 @@ func WithCustomProfilerLabelKeys(keys ...string) Option {
cfg.customProfilerLabels = append(cfg.customProfilerLabels, keys...)
}
}

// withRNG provides a seeded RNG, for reliable testing
func withRNG(r *rand.Rand) Option {
return func(cfg *config) {
cfg.rng = r
}
}
9 changes: 8 additions & 1 deletion profiler/profiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,13 @@ func (p *profiler) lookupProfile(name string, w io.Writer, debug int) error {
return prof.WriteTo(w, debug)
}

func (p *profiler) randFloat64() float64 {
if p.cfg.rng != nil {
return p.cfg.rng.Float64()
}
return rand.Float64()
}

// newProfiler creates a new, unstarted profiler.
func newProfiler(opts ...Option) (*profiler, error) {
if os.Getenv("AWS_LAMBDA_FUNCTION_NAME") != "" {
Expand Down Expand Up @@ -319,7 +326,7 @@ func (p *profiler) collect(ticker <-chan time.Time) {
// we will always record a trace
// We do multiplication here instead of division to defensively guard against
// division by 0
shouldTraceRandomly := rand.Float64()*float64(p.cfg.traceConfig.Period) < float64(p.cfg.period)
shouldTraceRandomly := p.randFloat64()*float64(p.cfg.traceConfig.Period) < float64(p.cfg.period)
// As a special case, we want to trace during the first
// profiling cycle since startup activity is generally much
// different than regular operation
Expand Down
3 changes: 1 addition & 2 deletions profiler/profiler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -531,14 +531,13 @@ func TestExecutionTraceMisconfiguration(t *testing.T) {
}

func TestExecutionTraceRandom(t *testing.T) {
t.Skip("flaky test, see: https://github.com/DataDog/dd-trace-go/issues/2529")

collectTraces := func(t *testing.T, profilePeriod, tracePeriod time.Duration, count int) int {
t.Setenv("DD_PROFILING_EXECUTION_TRACE_ENABLED", "true")
t.Setenv("DD_PROFILING_EXECUTION_TRACE_PERIOD", tracePeriod.String())
profiles := startTestProfiler(t, 10,
WithProfileTypes(),
WithPeriod(profilePeriod),
withRNG(rand.New(rand.NewSource(0))),
)

seenTraces := 0
Expand Down

0 comments on commit 7dc4cf4

Please sign in to comment.