lib/protoparser/datadog: do not re-use previously parsed field values if they are missing in the currently parsed message

Updates https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3432
This commit is contained in:
Aliaksandr Valialkin 2022-12-11 13:09:23 -08:00
parent 87723ef0e1
commit 87390443d1
No known key found for this signature in database
GPG Key ID: A72BEC6CD3D0DED1
3 changed files with 74 additions and 8 deletions

View File

@ -15,6 +15,8 @@ The following tip changes can be tested by building VictoriaMetrics components f
## tip
* BUGFIX: [DataDog protocol parser](https://docs.victoriametrics.com/#how-to-send-data-from-datadog-agent): do not re-use `host` and `device` fields from the previously parsed messages if these fields are missing in the currently parsed message. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3432).
## [v1.85.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.85.0)

View File

@ -28,7 +28,15 @@ type Request struct {
}
func (req *Request) reset() {
req.Series = req.Series[:0]
// recursively reset all the fields in req in order to avoid field value
// re-use in json.Unmarshal() when the corresponding field is missing
// in the unmarshaled JSON.
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3432
series := req.Series
for i := range series {
series[i].reset()
}
req.Series = series[:0]
}
// Unmarshal unmarshals DataDog /api/v1/series request body from b to req.
@ -59,23 +67,42 @@ func (req *Request) Unmarshal(b []byte) error {
//
// See https://docs.datadoghq.com/api/latest/metrics/#submit-metrics
type Series struct {
Metric string `json:"metric"`
Host string `json:"host"`
// Do not decode Interval, since it isn't used by VictoriaMetrics
// Interval int64 `json:"interval"`
Metric string `json:"metric"`
Points []Point `json:"points"`
Tags []string `json:"tags"`
// The device field does not appear in the datadog docs, but datadog-agent does use it.
// Datadog agent (v7 at least), removes the tag "device" and adds it as its own field. Why? That I don't know!
// https://github.com/DataDog/datadog-agent/blob/0ada7a97fed6727838a6f4d9c87123d2aafde735/pkg/metrics/series.go#L84-L105
Device string `json:"device"`
// Do not decode Interval, since it isn't used by VictoriaMetrics
// Interval int64 `json:"interval"`
Points []Point `json:"points"`
Tags []string `json:"tags"`
// Do not decode Type, since it isn't used by VictoriaMetrics
// Type string `json:"type"`
}
func (s *Series) reset() {
s.Metric = ""
s.Host = ""
s.Device = ""
points := s.Points
for i := range points {
points[i] = Point{}
}
s.Points = points[:0]
tags := s.Tags
for i := range tags {
tags[i] = ""
}
s.Tags = tags[:0]
}
// Point represents a point from DataDog POST request to /api/v1/series
type Point [2]float64

View File

@ -22,6 +22,43 @@ func TestSplitTag(t *testing.T) {
f(":bar", "", "bar")
}
func TestRequestUnmarshalMissingHost(t *testing.T) {
// This tests https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3432
req := Request{
Series: []Series{{
Host: "prev-host",
Device: "prev-device",
}},
}
data := `
{
"series": [
{
"metric": "system.load.1",
"points": [[
1575317847,
0.5
]]
}
]
}`
if err := req.Unmarshal([]byte(data)); err != nil {
t.Fatalf("unexpected error: %s", err)
}
reqExpected := Request{
Series: []Series{{
Metric: "system.load.1",
Points: []Point{{
1575317847,
0.5,
}},
}},
}
if !reflect.DeepEqual(&req, &reqExpected) {
t.Fatalf("unexpected request parsed;\ngot\n%+v\nwant\n%+v", req, reqExpected)
}
}
func TestRequestUnmarshalFailure(t *testing.T) {
f := func(s string) {
t.Helper()