Skip to content

Commit

Permalink
Relative gauges
Browse files Browse the repository at this point in the history
  • Loading branch information
James Cohen authored and James Cohen committed Dec 14, 2014
1 parent 1ac36f2 commit 4a76977
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 6 deletions.
70 changes: 67 additions & 3 deletions statsdaemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ type Packet struct {
Sampling float32
}

type GaugeData struct {
Relative bool
Negative bool
Value uint64
}

type Uint64Slice []uint64

func (s Uint64Slice) Len() int { return len(s) }
Expand Down Expand Up @@ -80,6 +86,7 @@ var (
In = make(chan *Packet, MAX_UNPROCESSED_PACKETS)
counters = make(map[string]int64)
gauges = make(map[string]uint64)
trackedGauges = make(map[string]uint64)
timers = make(map[string]Uint64Slice)
countInactivity = make(map[string]int64)
)
Expand Down Expand Up @@ -122,7 +129,39 @@ func packetHandler(s *Packet) {
}
timers[s.Bucket] = append(timers[s.Bucket], s.Value.(uint64))
} else if s.Modifier == "g" {
gauges[s.Bucket] = s.Value.(uint64)
var gaugeValue uint64 = 0
_, ok := gauges[s.Bucket]

// initialise gaugeValue as either 0 or existing value
if ok {
gaugeValue = gauges[s.Bucket]
} else {
gaugeValue = 0
}

gaugeData := s.Value.(GaugeData)
if gaugeData.Relative {
if gaugeData.Negative {
// subtract checking for -ve numbers
if gaugeData.Value > gaugeValue {
gaugeValue = 0
} else {
gaugeValue -= gaugeData.Value
}
} else {
// watch out for overflows
if gaugeData.Value > (math.MaxUint64 - gaugeValue) {
gaugeValue = math.MaxUint64
} else {
gaugeValue += gaugeData.Value
}
}
} else {
gaugeValue = gaugeData.Value
}

gauges[s.Bucket] = gaugeValue

} else if s.Modifier == "c" {
_, ok := counters[s.Bucket]
if !ok {
Expand Down Expand Up @@ -212,12 +251,15 @@ func processCounters(buffer *bytes.Buffer, now int64) int64 {

func processGauges(buffer *bytes.Buffer, now int64) int64 {
var num int64

for g, c := range gauges {
if c == math.MaxUint64 {
lastValue, ok := trackedGauges[g]

if ok && c == lastValue {
continue
}
fmt.Fprintf(buffer, "%s %d %d\n", g, c, now)
gauges[g] = math.MaxUint64
trackedGauges[g] = c
num++
}
return num
Expand Down Expand Up @@ -349,6 +391,28 @@ func parseMessage(data []byte) []*Packet {
log.Printf("ERROR: failed to ParseInt %s - %s", string(val), err)
continue
}
} else if mtypeStr[0] == 'g' {
var relative, negative bool
var stringToParse string

switch val[0] {
case '+', '-':
relative = true
negative = val[0] == '-'
stringToParse = string(val[1:])
default:
relative = false
negative = false
stringToParse = string(val)
}

gaugeValue, err := strconv.ParseUint(stringToParse, 10, 64)
if err != nil {
log.Printf("ERROR: failed to ParseUint %s - %s", string(val), err)
continue
}

value = GaugeData{relative, negative, gaugeValue}
} else {
value, err = strconv.ParseUint(string(val), 10, 64)
if err != nil {
Expand Down
59 changes: 56 additions & 3 deletions statsdaemon_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"bytes"
"github.com/bmizerany/assert"
"math"
"math/rand"
"strconv"
"testing"
Expand All @@ -22,7 +23,35 @@ func TestPacketParse(t *testing.T) {
assert.Equal(t, len(packets), 1)
packet := packets[0]
assert.Equal(t, "gaugor", packet.Bucket)
assert.Equal(t, uint64(333), packet.Value.(uint64))
assert.Equal(t, GaugeData{false, false, 333}, packet.Value)
assert.Equal(t, "g", packet.Modifier)
assert.Equal(t, float32(1), packet.Sampling)

d = []byte("gaugor:-10|g")
packets = parseMessage(d)
assert.Equal(t, len(packets), 1)
packet = packets[0]
assert.Equal(t, "gaugor", packet.Bucket)
assert.Equal(t, GaugeData{true, true, 10}, packet.Value)
assert.Equal(t, "g", packet.Modifier)
assert.Equal(t, float32(1), packet.Sampling)

d = []byte("gaugor:+4|g")
packets = parseMessage(d)
assert.Equal(t, len(packets), 1)
packet = packets[0]
assert.Equal(t, "gaugor", packet.Bucket)
assert.Equal(t, GaugeData{true, false, 4}, packet.Value)
assert.Equal(t, "g", packet.Modifier)
assert.Equal(t, float32(1), packet.Sampling)

// >max(int64) && <max(uint64)
d = []byte("gaugor:18446744073709551606|g")
packets = parseMessage(d)
assert.Equal(t, len(packets), 1)
packet = packets[0]
assert.Equal(t, "gaugor", packet.Bucket)
assert.Equal(t, GaugeData{false, false, 18446744073709551606}, packet.Value)
assert.Equal(t, "g", packet.Modifier)
assert.Equal(t, float32(1), packet.Sampling)

Expand Down Expand Up @@ -82,7 +111,7 @@ func TestPacketParse(t *testing.T) {

packet = packets[1]
assert.Equal(t, "gauge", packet.Bucket)
assert.Equal(t, uint64(3), packet.Value.(uint64))
assert.Equal(t, GaugeData{false, false, 3}, packet.Value)
assert.Equal(t, "g", packet.Modifier)
assert.Equal(t, float32(1), packet.Sampling)

Expand Down Expand Up @@ -171,12 +200,36 @@ func TestGaugePacketHandling(t *testing.T) {

p := &Packet{
Bucket: "gaugor",
Value: uint64(333),
Value: GaugeData{false, false, 333},
Modifier: "g",
Sampling: float32(1),
}
packetHandler(p)
assert.Equal(t, gauges["gaugor"], uint64(333))

// -10
p.Value = GaugeData{true, true, 10}
packetHandler(p)
assert.Equal(t, gauges["gaugor"], uint64(323))

// +4
p.Value = GaugeData{true, false, 4}
packetHandler(p)
assert.Equal(t, gauges["gaugor"], uint64(327))

// <0 overflow
p.Value = GaugeData{false, false, 10}
packetHandler(p)
p.Value = GaugeData{true, true, 20}
packetHandler(p)
assert.Equal(t, gauges["gaugor"], uint64(0))

// >2^64 overflow
p.Value = GaugeData{false, false, uint64(math.MaxUint64 - 10)}
packetHandler(p)
p.Value = GaugeData{true, false, 20}
packetHandler(p)
assert.Equal(t, gauges["gaugor"], uint64(math.MaxUint64))
}

func TestTimerPacketHandling(t *testing.T) {
Expand Down

0 comments on commit 4a76977

Please sign in to comment.