-
Notifications
You must be signed in to change notification settings - Fork 244
Open
Labels
enhancementlibraryIssues pertaining to the use of the packages as a library more than the exporter itselfIssues pertaining to the use of the packages as a library more than the exporter itself
Description
Description
The current implementation of the LineToEvents method in pkg/line/line.go returns multiple events, which results in a lot of allocations in the statsd-exporter. We propose changing this method to return a single event instead.
On the same refactoring step, we should introduce a "exploder" or "sampling" layer (or helper method on the event) which will keep the same behavior as we have now.
This is a breaking change on purpose which will force client projects using this repository as a library, to adapt to the new method signature.
Here is an example profile with samples using sampling rate, where you can see it takes more than 21% of total memory used:

Goals
- Reduce Allocations: By returning a single event, we aim to reduce the number of allocations and improve performance.
- Enforce Build Breaks: Changing the method signature will force dependent projects to update their code, ensuring compatibility with the new implementation.
- Introduce New Layer: Push the decision about what to do with
CountorSamplingRateto another layer of the exporter.
Proposed Solution
- Change Method Signature: Modify the
LineToEventsmethod to return a single event. - Update Event Structure: Include
CountorSamplingRatein the event structure. - Introduce New Layer: Implement a new layer in the exporter to handle the decision-making process for
CountorSamplingRate.
Repro script
dogClient, err := statsd.New(
opts.statsdServer,
statsd.WithoutTelemetry(),
statsd.WithNamespace("flood_statsd"),
statsd.WithTags([]string{"pod_name:" + os.Getenv("POD_NAME")}),
statsd.WithoutClientSideAggregation(),
)
if err != nil {
level.Error(logger).Log("msg", "Error creating dogClient client", "err", err)
os.Exit(1)
}
samplesSent := 0
start := time.Now()
for {
randomInt := rand.Int63n(opts.cardinalityLimit)
err := dogClient.Count("sample_counter", 1, []string{"mybad_label:co_" + strconv.FormatInt(randomInt, 10)}, 0.01)
if err != nil {
level.Error(logger).Log("msg", "Error sending metric", "err", err)
}
_ = dogClient.Distribution("synthetic", float64(randomInt), []string{"mybad_label:co" + strconv.FormatInt(randomInt, 10)}, 0.001)
_ = dogClient.Timing(
"some_timing",
time.Duration(rand.Intn(3000))*time.Millisecond,
[]string{"cardinality:" + strconv.FormatInt(randomInt, 10)},
0.01,
)
_ = dogClient.Gauge("some_gauge", float64(randomInt), []string{"mybad_label:co" + strconv.FormatInt(randomInt, 10)}, 0.01)
_ = dogClient.Distribution("heavily_sampled_distribution", float64(randomInt), []string{"mybad_label:co" + strconv.FormatInt(randomInt, 10)}, 0.001)
// control the rate of the synthetic metrics
samplesSent += 4
if samplesSent >= int(opts.samplesPerSec) {
elapsed := time.Since(start)
if elapsed < time.Second {
level.Info(logger).Log("msg", "Sleeping for", "duration", time.Second-elapsed)
time.Sleep(time.Second - elapsed)
}
level.Info(logger).Log("msg", "Sending", "samples", samplesSent, "elapsed", elapsed)
samplesSent = 0
start = time.Now()
}
}
Metadata
Metadata
Assignees
Labels
enhancementlibraryIssues pertaining to the use of the packages as a library more than the exporter itselfIssues pertaining to the use of the packages as a library more than the exporter itself