Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

metrics/dogstatsd: always reset all metrics before writing them when calling WriteTo #1231

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions metrics/dogstatsd/dogstatsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,14 @@ func (d *Dogstatsd) SendLoop(ctx context.Context, c <-chan time.Time, network, a
// lost if there is a problem with the write. Clients should be sure to call
// WriteTo regularly, ideally through the WriteLoop or SendLoop helper methods.
func (d *Dogstatsd) WriteTo(w io.Writer) (count int64, err error) {
var n int

d.counters.Reset().Walk(func(name string, lvs lv.LabelValues, values []float64) bool {
var (
n int
counters = d.counters.Reset()
timings = d.timings.Reset()
histograms = d.histograms.Reset()
)

counters.Walk(func(name string, lvs lv.LabelValues, values []float64) bool {
n, err = fmt.Fprintf(w, "%s%s:%f|c%s%s\n", d.prefix, name, sum(values), sampling(d.rates.Get(name)), d.tagValues(lvs))
if err != nil {
return false
Expand All @@ -168,7 +173,7 @@ func (d *Dogstatsd) WriteTo(w io.Writer) (count int64, err error) {
}
d.mtx.RUnlock()

d.timings.Reset().Walk(func(name string, lvs lv.LabelValues, values []float64) bool {
timings.Walk(func(name string, lvs lv.LabelValues, values []float64) bool {
sampleRate := d.rates.Get(name)
for _, value := range values {
n, err = fmt.Fprintf(w, "%s%s:%f|ms%s%s\n", d.prefix, name, value, sampling(sampleRate), d.tagValues(lvs))
Expand All @@ -183,7 +188,7 @@ func (d *Dogstatsd) WriteTo(w io.Writer) (count int64, err error) {
return count, err
}

d.histograms.Reset().Walk(func(name string, lvs lv.LabelValues, values []float64) bool {
histograms.Walk(func(name string, lvs lv.LabelValues, values []float64) bool {
sampleRate := d.rates.Get(name)
for _, value := range values {
n, err = fmt.Fprintf(w, "%s%s:%f|h%s%s\n", d.prefix, name, value, sampling(sampleRate), d.tagValues(lvs))
Expand Down
73 changes: 73 additions & 0 deletions metrics/dogstatsd/dogstatsd_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package dogstatsd

import (
"errors"
"testing"

"github.com/go-kit/kit/metrics/internal/lv"
"github.com/go-kit/kit/metrics/teststat"
"github.com/go-kit/log"
)
Expand Down Expand Up @@ -88,3 +90,74 @@ func TestTimingSampled(t *testing.T) {
t.Fatal(err)
}
}

func TestDogstatsd_WriteToDiscardsMetricsOnError(t *testing.T) {
walker := func(counter *int) func(string, lv.LabelValues, []float64) bool {
return func(string, lv.LabelValues, []float64) bool {
*counter++
return true
}
}

d := New("dogstatsd.", log.NewNopLogger())

// Add some metrics.
d.NewCounter("counter-1", 1.0).Add(1.0)
d.NewTiming("timing-1", 1.0).Observe(1.0)
d.NewHistogram("histogram-1", 1.0).Observe(1.0)

// Count metrics buffered in the Dogstatsd object.
var (
countersCount int
timingsCount int
histogramsCount int
)
d.counters.Walk(walker(&countersCount))
d.timings.Walk(walker(&timingsCount))
d.histograms.Walk(walker(&histogramsCount))

// Assert we have one of each type.
if countersCount != 1 {
t.Fatalf("expected counters count to be 1; got %d", countersCount)
}
if timingsCount != 1 {
t.Fatalf("expected timings count to be 1; got %d", timingsCount)
}
if histogramsCount != 1 {
t.Fatalf("expected histograms count to be 1; got %d", histogramsCount)
}

// Simulate an error while sending metrics.
count, err := d.WriteTo(errorWriter{})
if count != 0 {
t.Fatalf("expected count to be 0; got %d", count)
}
if err == nil {
t.Fatalf("expected error to be nil; got %v", err)
}

// Reset counters and count again.
countersCount = 0
timingsCount = 0
histogramsCount = 0
d.counters.Walk(walker(&countersCount))
d.timings.Walk(walker(&timingsCount))
d.histograms.Walk(walker(&histogramsCount))

// Assert buffered metrics were cleared.
if countersCount != 0 {
t.Fatalf("expected counters count to be 0; got %d", countersCount)
}
if timingsCount != 0 {
t.Fatalf("expected timings count to be 0; got %d", timingsCount)
}
if histogramsCount != 0 {
t.Fatalf("expected histograms count to be 0; got %d", histogramsCount)
}
}

type errorWriter struct{}

func (w errorWriter) Write([]byte) (int, error) {
return 0, errors.New("boom")
}