Skip to content

Commit f5fa12a

Browse files
authored
Merge pull request #762 from Wheezyx/mwedel/add-dgostatsdv11-support
Add support for DogStatsD v1.1 - value packing
2 parents c70e0a0 + e65f218 commit f5fa12a

19 files changed

+317
-222
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
41.0.0
2+
------
3+
- Add support for [DogStatsD protocol v1.1](https://docs.datadoghq.com/developers/dogstatsd/datagram_shell/?tab=metrics#dogstatsd-protocol-v11) - value packing.
4+
15
40.1.0
26
------
37
- Add support for configuration of receiver's buffer size - `receive-buffer-size`

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,17 @@ A simple way to test your installation or send metrics from a script is to use
390390

391391
echo 'abc.def.g:10|c' | nc -w1 -u localhost 8125
392392

393+
Since 41.0.0, server supports value packing from [DogStatsD protocol v1.1](https://docs.datadoghq.com/developers/dogstatsd/datagram_shell/?tab=metrics#dogstatsd-protocol-v11)
394+
It means it's possible to send multiple values within single line, for example when you do pre-consolidation on client side.
395+
It support all metric types except `SET`
396+
397+
The format of value-packing is:
398+
399+
<bucket name>:<value1>:<value2>,<value3>|<type>\n
400+
401+
You can send as much values as you would like, however remember to not exceed single packet size limits (usually size of MTU)
402+
as it will cause packets fragmentation and can lead to worse performance or lost packets.
403+
393404
Monitoring
394405
----------
395406
Many metrics for the internal processes are emitted. See METRICS.md for details. Go expvar is also

internal/fixtures/metrics.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ func SortCompare(ms []*gostatsd.Metric) func(i, j int) bool {
7070
if ms[i].Type == gostatsd.SET {
7171
return ms[i].StringValue < ms[j].StringValue
7272
} else {
73-
return ms[i].Value < ms[j].Value
73+
return ms[i].Values[0] < ms[j].Values[0]
7474
}
7575
}
7676
return len(ms[i].Tags) < len(ms[j].Tags)

internal/lexer/lexer.go

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"errors"
66
"math"
77
"strconv"
8+
"strings"
89

910
"github.com/atlassian/gostatsd"
1011
"github.com/atlassian/gostatsd/internal/pool"
@@ -101,14 +102,34 @@ func (l *Lexer) Run(input []byte, namespace string) (*gostatsd.Metric, *gostatsd
101102
if l.m != nil {
102103
l.m.Rate = l.sampling
103104
if l.m.Type != gostatsd.SET {
104-
v, err := strconv.ParseFloat(l.m.StringValue, 64)
105-
if err != nil {
106-
return nil, nil, err
105+
// Count number of values by checking colons to preallocate array
106+
var values []float64
107+
if l.m.StringValue == "" {
108+
values = make([]float64, 0, 0)
109+
} else {
110+
count := 1
111+
for i := 0; i < len(l.m.StringValue); i++ {
112+
if l.m.StringValue[i] == ':' {
113+
count++
114+
}
115+
}
116+
values = make([]float64, 0, count)
107117
}
108-
if math.IsNaN(v) {
109-
return nil, nil, errNaN
118+
for _, stringValue := range strings.Split(l.m.StringValue, ":") {
119+
if stringValue == "" {
120+
// SKip the value, it could be something like a.packing:1:2:|ms|#|:|c:xyz
121+
continue
122+
}
123+
v, err := strconv.ParseFloat(stringValue, 64)
124+
if err != nil {
125+
return nil, nil, err
126+
}
127+
if math.IsNaN(v) {
128+
return nil, nil, errNaN
129+
}
130+
values = append(values, v)
110131
}
111-
l.m.Value = v
132+
l.m.Values = values
112133
l.m.StringValue = ""
113134
}
114135
l.m.Tags = l.tags

internal/lexer/lexer_test.go

Lines changed: 58 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -14,54 +14,66 @@ import (
1414
func TestMetricsLexer(t *testing.T) {
1515
t.Parallel()
1616
tests := map[string]gostatsd.Metric{
17-
"foo.bar.baz:2|c": {Name: "foo.bar.baz", Value: 2, Type: gostatsd.COUNTER, Rate: 1.0},
18-
"abc.def.g:3|g": {Name: "abc.def.g", Value: 3, Type: gostatsd.GAUGE, Rate: 1.0},
19-
"def.g:10|ms": {Name: "def.g", Value: 10, Type: gostatsd.TIMER, Rate: 1.0},
20-
"def.h:10|h": {Name: "def.h", Value: 10, Type: gostatsd.TIMER, Rate: 1.0},
21-
"def.i:10|h|#foo": {Name: "def.i", Value: 10, Type: gostatsd.TIMER, Rate: 1.0, Tags: gostatsd.Tags{"foo"}},
22-
"smp.rte:5|c|@0.1": {Name: "smp.rte", Value: 5, Type: gostatsd.COUNTER, Rate: 0.1},
23-
"smp.rte:5|c|@0.1|#foo:bar,baz": {Name: "smp.rte", Value: 5, Type: gostatsd.COUNTER, Rate: 0.1, Tags: gostatsd.Tags{"foo:bar", "baz"}},
24-
"smp.rte:5|c|#foo:bar,baz": {Name: "smp.rte", Value: 5, Type: gostatsd.COUNTER, Rate: 1.0, Tags: gostatsd.Tags{"foo:bar", "baz"}},
17+
"foo.bar.baz:2|c": {Name: "foo.bar.baz", Values: []float64{2}, Type: gostatsd.COUNTER, Rate: 1.0},
18+
"abc.def.g:3|g": {Name: "abc.def.g", Values: []float64{3}, Type: gostatsd.GAUGE, Rate: 1.0},
19+
"def.g:10|ms": {Name: "def.g", Values: []float64{10}, Type: gostatsd.TIMER, Rate: 1.0},
20+
"def.h:10|h": {Name: "def.h", Values: []float64{10}, Type: gostatsd.TIMER, Rate: 1.0},
21+
"def.i:10|h|#foo": {Name: "def.i", Values: []float64{10}, Type: gostatsd.TIMER, Rate: 1.0, Tags: gostatsd.Tags{"foo"}},
22+
"smp.rte:5|c|@0.1": {Name: "smp.rte", Values: []float64{5}, Type: gostatsd.COUNTER, Rate: 0.1},
23+
"smp.rte:5|c|@0.1|#foo:bar,baz": {Name: "smp.rte", Values: []float64{5}, Type: gostatsd.COUNTER, Rate: 0.1, Tags: gostatsd.Tags{"foo:bar", "baz"}},
24+
"smp.rte:5|c|#foo:bar,baz": {Name: "smp.rte", Values: []float64{5}, Type: gostatsd.COUNTER, Rate: 1.0, Tags: gostatsd.Tags{"foo:bar", "baz"}},
2525
"uniq.usr:joe|s": {Name: "uniq.usr", StringValue: "joe", Type: gostatsd.SET, Rate: 1.0},
26-
"fooBarBaz:2|c": {Name: "fooBarBaz", Value: 2, Type: gostatsd.COUNTER, Rate: 1.0},
27-
"smp.rte:5|c|#Foo:Bar,baz": {Name: "smp.rte", Value: 5, Type: gostatsd.COUNTER, Rate: 1.0, Tags: gostatsd.Tags{"Foo:Bar", "baz"}},
28-
"smp.gge:1|g|#Foo:Bar": {Name: "smp.gge", Value: 1, Type: gostatsd.GAUGE, Rate: 1.0, Tags: gostatsd.Tags{"Foo:Bar"}},
29-
"smp.gge:1|g|#fo_o:ba-r": {Name: "smp.gge", Value: 1, Type: gostatsd.GAUGE, Rate: 1.0, Tags: gostatsd.Tags{"fo_o:ba-r"}},
30-
"smp gge:1|g": {Name: "smp_gge", Value: 1, Type: gostatsd.GAUGE, Rate: 1.0},
31-
"smp/gge:1|g": {Name: "smp-gge", Value: 1, Type: gostatsd.GAUGE, Rate: 1.0},
32-
"smp,gge$:1|g": {Name: "smpgge", Value: 1, Type: gostatsd.GAUGE, Rate: 1.0},
26+
"fooBarBaz:2|c": {Name: "fooBarBaz", Values: []float64{2}, Type: gostatsd.COUNTER, Rate: 1.0},
27+
"smp.rte:5|c|#Foo:Bar,baz": {Name: "smp.rte", Values: []float64{5}, Type: gostatsd.COUNTER, Rate: 1.0, Tags: gostatsd.Tags{"Foo:Bar", "baz"}},
28+
"smp.gge:1|g|#Foo:Bar": {Name: "smp.gge", Values: []float64{1}, Type: gostatsd.GAUGE, Rate: 1.0, Tags: gostatsd.Tags{"Foo:Bar"}},
29+
"smp.gge:1|g|#fo_o:ba-r": {Name: "smp.gge", Values: []float64{1}, Type: gostatsd.GAUGE, Rate: 1.0, Tags: gostatsd.Tags{"fo_o:ba-r"}},
30+
"smp gge:1|g": {Name: "smp_gge", Values: []float64{1}, Type: gostatsd.GAUGE, Rate: 1.0},
31+
"smp/gge:1|g": {Name: "smp-gge", Values: []float64{1}, Type: gostatsd.GAUGE, Rate: 1.0},
32+
"smp,gge$:1|g": {Name: "smpgge", Values: []float64{1}, Type: gostatsd.GAUGE, Rate: 1.0},
3333
"un1qu3:john|s": {Name: "un1qu3", StringValue: "john", Type: gostatsd.SET, Rate: 1.0},
3434
"un1qu3:john|s|#some:42": {Name: "un1qu3", StringValue: "john", Type: gostatsd.SET, Rate: 1.0, Tags: gostatsd.Tags{"some:42"}},
3535
"da-sh:1|s": {Name: "da-sh", StringValue: "1", Type: gostatsd.SET, Rate: 1.0},
3636
"under_score:1|s": {Name: "under_score", StringValue: "1", Type: gostatsd.SET, Rate: 1.0},
37-
"a:1|g|#f,,": {Name: "a", Value: 1, Type: gostatsd.GAUGE, Rate: 1.0, Tags: gostatsd.Tags{"f"}},
38-
"a:1|g|#,,f": {Name: "a", Value: 1, Type: gostatsd.GAUGE, Rate: 1.0, Tags: gostatsd.Tags{"f"}},
39-
"a:1|g|#f,,z": {Name: "a", Value: 1, Type: gostatsd.GAUGE, Rate: 1.0, Tags: gostatsd.Tags{"f", "z"}},
40-
"a:1|g|#": {Name: "a", Value: 1, Type: gostatsd.GAUGE, Rate: 1.0},
41-
"a:1|g|#,": {Name: "a", Value: 1, Type: gostatsd.GAUGE, Rate: 1.0},
42-
"a:1|g|#,,": {Name: "a", Value: 1, Type: gostatsd.GAUGE, Rate: 1.0},
43-
"foo.bar.baz:2|c|c:xyz": {Name: "foo.bar.baz", Value: 2, Type: gostatsd.COUNTER, Rate: 1.0},
44-
"smp.rte:5|c|@0.1|c:xyz": {Name: "smp.rte", Value: 5, Type: gostatsd.COUNTER, Rate: 0.1},
45-
"smp.rte:5|c|@0.1|#foo:bar,baz|c:xyz": {Name: "smp.rte", Value: 5, Type: gostatsd.COUNTER, Rate: 0.1, Tags: gostatsd.Tags{"foo:bar", "baz"}},
46-
"def.i:10|h|#foo|c:xyz": {Name: "def.i", Value: 10, Type: gostatsd.TIMER, Rate: 1.0, Tags: gostatsd.Tags{"foo"}},
47-
"c.after.tags:1|g|#f,,|c:xyz": {Name: "c.after.tags", Value: 1, Type: gostatsd.GAUGE, Rate: 1.0, Tags: gostatsd.Tags{"f"}},
48-
"c.after.tags:1|g|#,,f|c:xyz": {Name: "c.after.tags", Value: 1, Type: gostatsd.GAUGE, Rate: 1.0, Tags: gostatsd.Tags{"f"}},
49-
"c.after.tags:1|g|#f,,z|c:xyz": {Name: "c.after.tags", Value: 1, Type: gostatsd.GAUGE, Rate: 1.0, Tags: gostatsd.Tags{"f", "z"}},
50-
"c.after.tags:1|g|#|c:xyz": {Name: "c.after.tags", Value: 1, Type: gostatsd.GAUGE, Rate: 1.0},
51-
"c.after.tags:1|g|#,|c:xyz": {Name: "c.after.tags", Value: 1, Type: gostatsd.GAUGE, Rate: 1.0},
52-
"c.after.tags:1|g|#,,|c:xyz": {Name: "c.after.tags", Value: 1, Type: gostatsd.GAUGE, Rate: 1.0},
53-
"c.after.tags:1|g|#,,|c::,#@": {Name: "c.after.tags", Value: 1, Type: gostatsd.GAUGE, Rate: 1.0},
54-
"field.order.rev.all:1|g|c:xyz|#foo:bar|@0.1": {Name: "field.order.rev.all", Value: 1, Type: gostatsd.GAUGE, Rate: 0.1, Tags: gostatsd.Tags{"foo:bar"}},
55-
"field.order.rev.notags:1|g|c:xyz|@0.1": {Name: "field.order.rev.notags", Value: 1, Type: gostatsd.GAUGE, Rate: 0.1},
56-
"new.last.prefix:1|g|#,,|c:xyz|x:": {Name: "new.last.prefix", Value: 1, Type: gostatsd.GAUGE, Rate: 1.0},
57-
"new.last.empty:1|g|#,|c:xyz|": {Name: "new.last.empty", Value: 1, Type: gostatsd.GAUGE, Rate: 1.0},
58-
"new.last.colon:1|g|#|c:xyz|:": {Name: "new.last.colon", Value: 1, Type: gostatsd.GAUGE, Rate: 1.0},
59-
"new.first.prefix:1|g|x:#,,|c:xyz|": {Name: "new.first.prefix", Value: 1, Type: gostatsd.GAUGE, Rate: 1.0},
60-
"new.first.empty:1|g||#,|c:xyz": {Name: "new.first.empty", Value: 1, Type: gostatsd.GAUGE, Rate: 1.0},
61-
"new.first.colon:1|g|:|#|c:xyz": {Name: "new.first.colon", Value: 1, Type: gostatsd.GAUGE, Rate: 1.0},
62-
"new.mid.prefix:1|g|#,,|x:|c:xyz": {Name: "new.mid.prefix", Value: 1, Type: gostatsd.GAUGE, Rate: 1.0},
63-
"new.mid.empty:1|g|#,||c:xyz": {Name: "new.mid.empty", Value: 1, Type: gostatsd.GAUGE, Rate: 1.0},
64-
"new.mid.colon:1|g|#|:|c:xyz": {Name: "new.mid.colon", Value: 1, Type: gostatsd.GAUGE, Rate: 1.0},
37+
"a:1|g|#f,,": {Name: "a", Values: []float64{1}, Type: gostatsd.GAUGE, Rate: 1.0, Tags: gostatsd.Tags{"f"}},
38+
"a:1|g|#,,f": {Name: "a", Values: []float64{1}, Type: gostatsd.GAUGE, Rate: 1.0, Tags: gostatsd.Tags{"f"}},
39+
"a:1|g|#f,,z": {Name: "a", Values: []float64{1}, Type: gostatsd.GAUGE, Rate: 1.0, Tags: gostatsd.Tags{"f", "z"}},
40+
"a:1|g|#": {Name: "a", Values: []float64{1}, Type: gostatsd.GAUGE, Rate: 1.0},
41+
"a:1|g|#,": {Name: "a", Values: []float64{1}, Type: gostatsd.GAUGE, Rate: 1.0},
42+
"a:1|g|#,,": {Name: "a", Values: []float64{1}, Type: gostatsd.GAUGE, Rate: 1.0},
43+
"foo.bar.baz:2|c|c:xyz": {Name: "foo.bar.baz", Values: []float64{2}, Type: gostatsd.COUNTER, Rate: 1.0},
44+
"smp.rte:5|c|@0.1|c:xyz": {Name: "smp.rte", Values: []float64{5}, Type: gostatsd.COUNTER, Rate: 0.1},
45+
"smp.rte:5|c|@0.1|#foo:bar,baz|c:xyz": {Name: "smp.rte", Values: []float64{5}, Type: gostatsd.COUNTER, Rate: 0.1, Tags: gostatsd.Tags{"foo:bar", "baz"}},
46+
"def.i:10|h|#foo|c:xyz": {Name: "def.i", Values: []float64{10}, Type: gostatsd.TIMER, Rate: 1.0, Tags: gostatsd.Tags{"foo"}},
47+
"c.after.tags:1|g|#f,,|c:xyz": {Name: "c.after.tags", Values: []float64{1}, Type: gostatsd.GAUGE, Rate: 1.0, Tags: gostatsd.Tags{"f"}},
48+
"c.after.tags:1|g|#,,f|c:xyz": {Name: "c.after.tags", Values: []float64{1}, Type: gostatsd.GAUGE, Rate: 1.0, Tags: gostatsd.Tags{"f"}},
49+
"c.after.tags:1|g|#f,,z|c:xyz": {Name: "c.after.tags", Values: []float64{1}, Type: gostatsd.GAUGE, Rate: 1.0, Tags: gostatsd.Tags{"f", "z"}},
50+
"c.after.tags:1|g|#|c:xyz": {Name: "c.after.tags", Values: []float64{1}, Type: gostatsd.GAUGE, Rate: 1.0},
51+
"c.after.tags:1|g|#,|c:xyz": {Name: "c.after.tags", Values: []float64{1}, Type: gostatsd.GAUGE, Rate: 1.0},
52+
"c.after.tags:1|g|#,,|c:xyz": {Name: "c.after.tags", Values: []float64{1}, Type: gostatsd.GAUGE, Rate: 1.0},
53+
"c.after.tags:1|g|#,,|c::,#@": {Name: "c.after.tags", Values: []float64{1}, Type: gostatsd.GAUGE, Rate: 1.0},
54+
"field.order.rev.all:1|g|c:xyz|#foo:bar|@0.1": {Name: "field.order.rev.all", Values: []float64{1}, Type: gostatsd.GAUGE, Rate: 0.1, Tags: gostatsd.Tags{"foo:bar"}},
55+
"field.order.rev.notags:1|g|c:xyz|@0.1": {Name: "field.order.rev.notags", Values: []float64{1}, Type: gostatsd.GAUGE, Rate: 0.1},
56+
"new.last.prefix:1|g|#,,|c:xyz|x:": {Name: "new.last.prefix", Values: []float64{1}, Type: gostatsd.GAUGE, Rate: 1.0},
57+
"new.last.empty:1|g|#,|c:xyz|": {Name: "new.last.empty", Values: []float64{1}, Type: gostatsd.GAUGE, Rate: 1.0},
58+
"new.last.colon:1|g|#|c:xyz|:": {Name: "new.last.colon", Values: []float64{1}, Type: gostatsd.GAUGE, Rate: 1.0},
59+
"new.first.prefix:1|g|x:#,,|c:xyz|": {Name: "new.first.prefix", Values: []float64{1}, Type: gostatsd.GAUGE, Rate: 1.0},
60+
"new.first.empty:1|g||#,|c:xyz": {Name: "new.first.empty", Values: []float64{1}, Type: gostatsd.GAUGE, Rate: 1.0},
61+
"new.first.colon:1|g|:|#|c:xyz": {Name: "new.first.colon", Values: []float64{1}, Type: gostatsd.GAUGE, Rate: 1.0},
62+
"new.mid.prefix:1|g|#,,|x:|c:xyz": {Name: "new.mid.prefix", Values: []float64{1}, Type: gostatsd.GAUGE, Rate: 1.0},
63+
"new.mid.empty:1|g|#,||c:xyz": {Name: "new.mid.empty", Values: []float64{1}, Type: gostatsd.GAUGE, Rate: 1.0},
64+
"new.mid.colon:1|g|#|:|c:xyz": {Name: "new.mid.colon", Values: []float64{1}, Type: gostatsd.GAUGE, Rate: 1.0},
65+
// Value packing tests
66+
"a.packing:1:2|ms|#|:|c:xyz": {Name: "a.packing", Values: []float64{1, 2}, Type: gostatsd.TIMER, Rate: 1.0},
67+
"a.packing:1:2:3|ms|#|:|c:xyz": {Name: "a.packing", Values: []float64{1, 2, 3}, Type: gostatsd.TIMER, Rate: 1.0},
68+
"a.packing:1:2:|ms|#|:|c:xyz": {Name: "a.packing", Values: []float64{1, 2}, Type: gostatsd.TIMER, Rate: 1.0},
69+
"a.packing:|ms|#|:|c:xyz": {Name: "a.packing", Values: []float64{}, Type: gostatsd.TIMER, Rate: 1.0},
70+
"a.packing:1:2|c|#|:|c:xyz": {Name: "a.packing", Values: []float64{1, 2}, Type: gostatsd.COUNTER, Rate: 1.0},
71+
"a.packing:1:2:3|c|#|:|c:xyz": {Name: "a.packing", Values: []float64{1, 2, 3}, Type: gostatsd.COUNTER, Rate: 1.0},
72+
"a.packing:1:2:|c|#|:|c:xyz": {Name: "a.packing", Values: []float64{1, 2}, Type: gostatsd.COUNTER, Rate: 1.0},
73+
"a.packing:1:2|g|#|:|c:xyz": {Name: "a.packing", Values: []float64{1, 2}, Type: gostatsd.GAUGE, Rate: 1.0},
74+
"a.packing:1:2:3|g|#|:|c:xyz": {Name: "a.packing", Values: []float64{1, 2, 3}, Type: gostatsd.GAUGE, Rate: 1.0},
75+
"a.packing:1:2:|g|#|:|c:xyz": {Name: "a.packing", Values: []float64{1, 2}, Type: gostatsd.GAUGE, Rate: 1.0},
76+
"a.packing:::|g|#|:|c:xyz": {Name: "a.packing", Values: []float64{}, Type: gostatsd.GAUGE, Rate: 1.0},
6577
}
6678

6779
compareMetric(t, tests, "")
@@ -85,9 +97,9 @@ func TestInvalidMetricsLexer(t *testing.T) {
8597
}
8698

8799
tests := map[string]gostatsd.Metric{
88-
"foo.bar.baz:2|c": {Name: "stats.foo.bar.baz", Value: 2, Type: gostatsd.COUNTER, Rate: 1.0},
89-
"abc.def.g:3|g": {Name: "stats.abc.def.g", Value: 3, Type: gostatsd.GAUGE, Rate: 1.0},
90-
"def.g:10|ms": {Name: "stats.def.g", Value: 10, Type: gostatsd.TIMER, Rate: 1.0},
100+
"foo.bar.baz:2|c": {Name: "stats.foo.bar.baz", Values: []float64{2}, Type: gostatsd.COUNTER, Rate: 1.0},
101+
"abc.def.g:3|g": {Name: "stats.abc.def.g", Values: []float64{3}, Type: gostatsd.GAUGE, Rate: 1.0},
102+
"def.g:10|ms": {Name: "stats.def.g", Values: []float64{10}, Type: gostatsd.TIMER, Rate: 1.0},
91103
"uniq.usr:joe|s": {Name: "stats.uniq.usr", StringValue: "joe", Type: gostatsd.SET, Rate: 1.0},
92104
}
93105

metric_consolidator_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ func TestConsolidation(t *testing.T) {
2121
m1 := &Metric{
2222
Name: "foo",
2323
Type: COUNTER,
24-
Value: 1,
24+
Values: []float64{1},
2525
Rate: 1,
2626
Timestamp: 10,
2727
}
2828
m2 := &Metric{
2929
Name: "foo",
3030
Type: COUNTER,
31-
Value: 3,
31+
Values: []float64{3},
3232
Rate: 0.1,
3333
Timestamp: 20,
3434
}
@@ -76,7 +76,7 @@ func randomMetric(seed, variations int) *Metric {
7676
if m.Type == SET {
7777
m.StringValue = fmt.Sprintf("%d", seed)
7878
} else {
79-
m.Value = float64(seed)
79+
m.Values = []float64{float64(seed)}
8080
m.Rate = 1
8181
}
8282
m.Timestamp = 10

0 commit comments

Comments
 (0)