From a8d4224828f2369ece31c0d8a8854f79c89c4453 Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Tue, 18 Jun 2019 19:04:02 +0300 Subject: [PATCH] app/vminsert/graphite: allow skipping timestamps in Graphite plaintext protocol In this case VictoriaMetrics uses the ingestion time as a timestamp. --- README.md | 1 + app/vminsert/graphite/parser.go | 4 +++- app/vminsert/graphite/parser_test.go | 17 +++++++++++++---- app/vminsert/graphite/request_handler.go | 17 ++++++++++++++--- 4 files changed, 31 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index ba1dd4af7..a67120ec2 100644 --- a/README.md +++ b/README.md @@ -264,6 +264,7 @@ Example for writing data with Graphite plaintext protocol to local VictoriaMetri echo "foo.bar.baz;tag1=value1;tag2=value2 123 `date +%s`" | nc -N localhost 2003 ``` +VictoriaMetrics sets the current time if timestamp is omitted. Arbitrary number of lines delimited by `\n` may be sent in one go. After that the data may be read via [/api/v1/export](#how-to-export-time-series) endpoint: diff --git a/app/vminsert/graphite/parser.go b/app/vminsert/graphite/parser.go index d32f63bd1..77bad97c5 100644 --- a/app/vminsert/graphite/parser.go +++ b/app/vminsert/graphite/parser.go @@ -86,7 +86,9 @@ func (r *Row) unmarshal(s string, tagsPool []Tag) ([]Tag, error) { n = strings.IndexByte(tail, ' ') if n < 0 { - return tagsPool, fmt.Errorf("cannot find whitespace between value and timestamp in %q", s) + // There is no timestamp. Use default timestamp instead. + r.Value = fastfloat.ParseBestEffort(tail) + return tagsPool, nil } r.Value = fastfloat.ParseBestEffort(tail[:n]) r.Timestamp = fastfloat.ParseInt64BestEffort(tail[n+1:]) diff --git a/app/vminsert/graphite/parser_test.go b/app/vminsert/graphite/parser_test.go index ac021e49b..635abc77f 100644 --- a/app/vminsert/graphite/parser_test.go +++ b/app/vminsert/graphite/parser_test.go @@ -22,9 +22,6 @@ func TestRowsUnmarshalFailure(t *testing.T) { // Missing value f("aaa") - // Missing timestamp - f("aaa 1123") - // Invalid multiline f("aaa\nbbb 123 34") @@ -81,6 +78,14 @@ func TestRowsUnmarshalSuccess(t *testing.T) { }}, }) + // Missing timestamp + f("aaa 1123", &Rows{ + Rows: []Row{{ + Metric: "aaa", + Value: 1123, + }}, + }) + // Tags f("foo;bar=baz 1 2", &Rows{ Rows: []Row{{ @@ -116,13 +121,17 @@ func TestRowsUnmarshalSuccess(t *testing.T) { }) // Multi lines - f("foo 0.3 2\nbar.baz 0.34 43\n", &Rows{ + f("foo 0.3 2\naaa 3\nbar.baz 0.34 43\n", &Rows{ Rows: []Row{ { Metric: "foo", Value: 0.3, Timestamp: 2, }, + { + Metric: "aaa", + Value: 3, + }, { Metric: "bar.baz", Value: 0.34, diff --git a/app/vminsert/graphite/request_handler.go b/app/vminsert/graphite/request_handler.go index 67f74cbb5..114838939 100644 --- a/app/vminsert/graphite/request_handler.go +++ b/app/vminsert/graphite/request_handler.go @@ -87,10 +87,21 @@ func (ctx *pushCtx) Read(r io.Reader) bool { return false } - // Convert timestamps from seconds to milliseconds - for i := range ctx.Rows.Rows { - ctx.Rows.Rows[i].Timestamp *= 1e3 + // Fill missing timestamps with the current timestamp rounded to seconds. + currentTimestamp := time.Now().Unix() + rows := ctx.Rows.Rows + for i := range rows { + r := &rows[i] + if r.Timestamp == 0 { + r.Timestamp = currentTimestamp + } } + + // Convert timestamps from seconds to milliseconds. + for i := range rows { + rows[i].Timestamp *= 1e3 + } + return true }