From 47d605b69a1962f2232d24c6fce0023a62cbbf07 Mon Sep 17 00:00:00 2001 From: Cory LaNou Date: Thu, 4 Jun 2015 11:22:53 -0600 Subject: [PATCH] make line protocol round or parse precision as expected --- tsdb/points.go | 49 ++++++++++++---- tsdb/points_test.go | 134 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 160 insertions(+), 23 deletions(-) diff --git a/tsdb/points.go b/tsdb/points.go index 89cb73397f..51e8987e95 100644 --- a/tsdb/points.go +++ b/tsdb/points.go @@ -8,6 +8,8 @@ import ( "strconv" "strings" "time" + + "github.com/davecgh/go-spew/spew" ) // Point defines the values that will be written to the database @@ -52,6 +54,9 @@ type point struct { // binary encoded field data data []byte + + // precision is set + precision string } var escapeCodes = map[byte][]byte{ @@ -139,15 +144,16 @@ func parsePoint(buf []byte, defaultTime time.Time, precision string) (Point, err } pt := &point{ - key: key, - fields: fields, - ts: ts, + key: key, + fields: fields, + ts: ts, + precision: precision, } if len(ts) == 0 { pt.time = defaultTime + pt.SetPrecision() } - pt.SetPrecision(precision) return pt, nil } @@ -505,7 +511,8 @@ func (p *point) Time() time.Time { if err != nil { return p.time } - p.time = time.Unix(0, ts) + p.time = time.Unix(0, ts*p.GetPrecisionMultiplier()) + spew.Dump(ts, p.precision, p.GetPrecisionMultiplier(), p.time.UTC()) } return p.time @@ -574,22 +581,40 @@ func (p *point) AddField(name string, value interface{}) { } // SetPrecision will round a time to the specified precision -func (p *point) SetPrecision(precision string) { - switch precision { +func (p *point) SetPrecision() { + switch p.precision { case "n": case "u": - p.SetTime(p.Time().Round(time.Microsecond)) + p.SetTime(p.Time().Truncate(time.Microsecond)) case "ms": - p.SetTime(p.Time().Round(time.Millisecond)) + p.SetTime(p.Time().Truncate(time.Millisecond)) case "s": - p.SetTime(p.Time().Round(time.Second)) + p.SetTime(p.Time().Truncate(time.Second)) case "m": - p.SetTime(p.Time().Round(time.Minute)) + p.SetTime(p.Time().Truncate(time.Minute)) case "h": - p.SetTime(p.Time().Round(time.Hour)) + p.SetTime(p.Time().Truncate(time.Hour)) } } +// GetPrecisionMultiplier will return a multiplier for the precision specified +func (p *point) GetPrecisionMultiplier() int64 { + d := time.Nanosecond + switch p.precision { + case "u": + d = time.Microsecond + case "ms": + d = time.Millisecond + case "s": + d = time.Second + case "m": + d = time.Minute + case "h": + d = time.Hour + } + return int64(d) +} + func (p *point) String() string { if p.Time().IsZero() { return fmt.Sprintf("%s %s", p.Key(), string(p.fields)) diff --git a/tsdb/points_test.go b/tsdb/points_test.go index 9884c200de..8a6d66145d 100644 --- a/tsdb/points_test.go +++ b/tsdb/points_test.go @@ -451,19 +451,131 @@ func TestParsePointToString(t *testing.T) { } func TestParsePointsWithPrecision(t *testing.T) { - line := `cpu,host=serverA,region=us-east value=1.0 20000000000` - pts, err := ParsePointsWithPrecision([]byte(line), time.Now().UTC(), "m") - if err != nil { - t.Fatalf(`ParsePoints() failed. got %s`, err) + tests := []struct { + name string + line string + precision string + exp string + }{ + { + name: "nanosecond by default", + line: `cpu,host=serverA,region=us-east value=1.0 946730096789012345`, + precision: "", + exp: "cpu,host=serverA,region=us-east value=1.0 946730096789012345", + }, + { + name: "nanosecond", + line: `cpu,host=serverA,region=us-east value=1.0 946730096789012345`, + precision: "n", + exp: "cpu,host=serverA,region=us-east value=1.0 946730096789012345", + }, + { + name: "microsecond", + line: `cpu,host=serverA,region=us-east value=1.0 946730096789012`, + precision: "u", + exp: "cpu,host=serverA,region=us-east value=1.0 946730096789012000", + }, + { + name: "millisecond", + line: `cpu,host=serverA,region=us-east value=1.0 946730096789`, + precision: "ms", + exp: "cpu,host=serverA,region=us-east value=1.0 946730096789000000", + }, + { + name: "second", + line: `cpu,host=serverA,region=us-east value=1.0 946730096`, + precision: "s", + exp: "cpu,host=serverA,region=us-east value=1.0 946730096000000000", + }, + { + name: "minute", + line: `cpu,host=serverA,region=us-east value=1.0 15778834`, + precision: "m", + exp: "cpu,host=serverA,region=us-east value=1.0 946730040000000000", + }, + { + name: "hour", + line: `cpu,host=serverA,region=us-east value=1.0 262980`, + precision: "h", + exp: "cpu,host=serverA,region=us-east value=1.0 946728000000000000", + }, } - if exp := 1; len(pts) != exp { - t.Errorf("ParsePoint() len mismatch: got %v, exp %v", len(pts), exp) - } - pt := pts[0] + for _, test := range tests { + pts, err := ParsePointsWithPrecision([]byte(test.line), time.Now().UTC(), test.precision) + if err != nil { + t.Fatalf(`%s: ParsePoints() failed. got %s`, test.name, err) + } + if exp := 1; len(pts) != exp { + t.Errorf("%s: ParsePoint() len mismatch: got %v, exp %v", test.name, len(pts), exp) + } + pt := pts[0] - got := pt.String() - if exp := "cpu,host=serverA,region=us-east value=1.0 0"; got != exp { - t.Errorf("ParsePoint() to string mismatch:\n got %v\n exp %v", got, exp) + got := pt.String() + if got != test.exp { + t.Errorf("%s: ParsePoint() to string mismatch:\n got %v\n exp %v", test.name, got, test.exp) + } + } +} + +func TestParsePointsWithPrecisionNoTime(t *testing.T) { + line := `cpu,host=serverA,region=us-east value=1.0` + tm, _ := time.Parse(time.RFC3339Nano, "2000-01-01T12:34:56.789012345Z") + tests := []struct { + name string + precision string + exp string + }{ + { + name: "no precision", + precision: "", + exp: "cpu,host=serverA,region=us-east value=1.0 946730096789012345", + }, + { + name: "nanosecond precision", + precision: "n", + exp: "cpu,host=serverA,region=us-east value=1.0 946730096789012345", + }, + { + name: "microsecond precision", + precision: "u", + exp: "cpu,host=serverA,region=us-east value=1.0 946730096789012000", + }, + { + name: "millisecond precision", + precision: "ms", + exp: "cpu,host=serverA,region=us-east value=1.0 946730096789000000", + }, + { + name: "second precision", + precision: "s", + exp: "cpu,host=serverA,region=us-east value=1.0 946730096000000000", + }, + { + name: "minute precision", + precision: "m", + exp: "cpu,host=serverA,region=us-east value=1.0 946730040000000000", + }, + { + name: "hour precision", + precision: "h", + exp: "cpu,host=serverA,region=us-east value=1.0 946728000000000000", + }, + } + + for _, test := range tests { + pts, err := ParsePointsWithPrecision([]byte(line), tm, test.precision) + if err != nil { + t.Fatalf(`%s: ParsePoints() failed. got %s`, test.name, err) + } + if exp := 1; len(pts) != exp { + t.Errorf("%s: ParsePoint() len mismatch: got %v, exp %v", test.name, len(pts), exp) + } + pt := pts[0] + + got := pt.String() + if got != test.exp { + t.Errorf("%s: ParsePoint() to string mismatch:\n got %v\n exp %v", test.name, got, test.exp) + } } }