func TestParsePointsWithPrecisionComments(t *testing.T) { tests := []struct { name string batch string exp string lenPoints int }{ { name: "comment only", batch: `# comment only`, exp: "cpu,host=serverA,region=us-east value=1.0 946730096789012345", lenPoints: 0, }, { name: "point with comment above", batch: `# a point is below cpu,host=serverA,region=us-east value=1.0 946730096789012345`, exp: "cpu,host=serverA,region=us-east value=1.0 946730096789012345", lenPoints: 1, }, { name: "point with comment below", batch: `cpu,host=serverA,region=us-east value=1.0 946730096789012345 # end of points`, exp: "cpu,host=serverA,region=us-east value=1.0 946730096789012345", lenPoints: 1, }, { name: "indented comment", batch: ` # a point is below cpu,host=serverA,region=us-east value=1.0 946730096789012345`, exp: "cpu,host=serverA,region=us-east value=1.0 946730096789012345", lenPoints: 1, }, } for _, test := range tests { pts, err := tsdb.ParsePointsWithPrecision([]byte(test.batch), time.Now().UTC(), "") if err != nil { t.Fatalf(`%s: ParsePoints() failed. got %s`, test.name, err) } pointsLength := len(pts) if exp := test.lenPoints; pointsLength != exp { t.Errorf("%s: ParsePoint() len mismatch: got %v, exp %v", test.name, pointsLength, exp) } if pointsLength > 0 { 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) } } } }
func test(t *testing.T, line string, point tsdb.Point) { pts, err := tsdb.ParsePointsWithPrecision([]byte(line), time.Unix(0, 0), "n") if err != nil { t.Fatalf(`ParsePoints("%s") mismatch. got %v, exp nil`, line, err) } if exp := 1; len(pts) != exp { t.Fatalf(`ParsePoints("%s") len mismatch. got %d, exp %d`, line, len(pts), exp) } if exp := point.Key(); !bytes.Equal(pts[0].Key(), exp) { t.Errorf("ParsePoints(\"%s\") key mismatch.\ngot %v\nexp %v", line, string(pts[0].Key()), string(exp)) } if exp := len(point.Tags()); len(pts[0].Tags()) != exp { t.Errorf(`ParsePoints("%s") tags mismatch. got %v, exp %v`, line, pts[0].Tags(), exp) } for tag, value := range point.Tags() { if pts[0].Tags()[tag] != value { t.Errorf(`ParsePoints("%s") tags mismatch. got %v, exp %v`, line, pts[0].Tags()[tag], value) } } for name, value := range point.Fields() { val := pts[0].Fields()[name] expfval, ok := val.(float64) if ok && math.IsNaN(expfval) { gotfval, ok := value.(float64) if ok && !math.IsNaN(gotfval) { t.Errorf(`ParsePoints("%s") field '%s' mismatch. exp NaN`, line, name) } } else if !reflect.DeepEqual(pts[0].Fields()[name], value) { t.Errorf(`ParsePoints("%s") field '%s' mismatch. got %v, exp %v`, line, name, pts[0].Fields()[name], value) } } if !pts[0].Time().Equal(point.Time()) { t.Errorf(`ParsePoints("%s") time mismatch. got %v, exp %v`, line, pts[0].Time(), point.Time()) } if !strings.HasPrefix(pts[0].String(), line) { t.Errorf("ParsePoints string mismatch.\ngot: %v\nexp: %v", pts[0].String(), line) } }
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 := tsdb.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) } } }
func TestParsePointsWithPrecision(t *testing.T) { 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", }, } for _, test := range tests { pts, err := tsdb.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 got != test.exp { t.Errorf("%s: ParsePoint() to string mismatch:\n got %v\n exp %v", test.name, got, test.exp) } } }
// Ensure points can be written to the engine and queried in reverse order. func TestEngine_WritePoints_Reverse(t *testing.T) { e := OpenDefaultEngine() defer e.Close() // Create metadata. mf := &tsdb.MeasurementFields{Fields: make(map[string]*tsdb.Field)} mf.CreateFieldIfNotExists("value", influxql.Float) seriesToCreate := []*tsdb.SeriesCreate{ {Series: tsdb.NewSeries(string(tsdb.MakeKey([]byte("temperature"), nil)), nil)}, } // Parse point. points, err := tsdb.ParsePointsWithPrecision([]byte("temperature value=100 0"), time.Now().UTC(), "s") if err != nil { t.Fatal(err) } else if data, err := mf.Codec.EncodeFields(points[0].Fields()); err != nil { t.Fatal(err) } else { points[0].SetData(data) } // Write original value. if err := e.WritePoints(points, map[string]*tsdb.MeasurementFields{"temperature": mf}, seriesToCreate); err != nil { t.Fatal(err) } // Flush to disk. if err := e.Flush(0); err != nil { t.Fatal(err) } // Parse new point. points, err = tsdb.ParsePointsWithPrecision([]byte("temperature value=200 1"), time.Now().UTC(), "s") if err != nil { t.Fatal(err) } else if data, err := mf.Codec.EncodeFields(points[0].Fields()); err != nil { t.Fatal(err) } else { points[0].SetData(data) } // Write the new points existing value. if err := e.WritePoints(points, nil, nil); err != nil { t.Fatal(err) } // Ensure only the updated value is read. tx := e.MustBegin(false) defer tx.Rollback() c := tx.Cursor("temperature", tsdb.Reverse) if k, _ := c.Seek(u64tob(math.MaxInt64)); !bytes.Equal(k, u64tob(uint64(time.Unix(1, 0).UnixNano()))) { t.Fatalf("unexpected key: %v", btou64(k)) } else if k, v := c.Next(); !bytes.Equal(k, u64tob(uint64(time.Unix(0, 0).UnixNano()))) { t.Fatalf("unexpected key: %#v", k) } else if m, err := mf.Codec.DecodeFieldsWithNames(v); err != nil { t.Fatal(err) } else if m["value"] != float64(100) { t.Errorf("unexpected value: %#v", m) } if k, v := c.Next(); k != nil { t.Fatalf("unexpected key/value: %#v / %#v", k, v) } }
// serveWriteLine receives incoming series data in line protocol format and writes it to the database. func (h *Handler) serveWriteLine(w http.ResponseWriter, r *http.Request, body []byte, user *meta.UserInfo) { // Some clients may not set the content-type header appropriately and send JSON with a non-json // content-type. If the body looks JSON, try to handle it as as JSON instead if len(body) > 0 { var i int for { // JSON requests must start w/ an opening bracket if body[i] == '{' { h.serveWriteJSON(w, r, body, user) return } // check that the byte is in the standard ascii code range if body[i] > 32 { break } i += 1 } } precision := r.FormValue("precision") if precision == "" { precision = "n" } points, err := tsdb.ParsePointsWithPrecision(body, time.Now().UTC(), precision) if err != nil { if err.Error() == "EOF" { w.WriteHeader(http.StatusOK) return } h.writeError(w, influxql.Result{Err: err}, http.StatusBadRequest) return } database := r.FormValue("db") if database == "" { h.writeError(w, influxql.Result{Err: fmt.Errorf("database is required")}, http.StatusBadRequest) return } if di, err := h.MetaStore.Database(database); err != nil { h.writeError(w, influxql.Result{Err: fmt.Errorf("metastore database error: %s", err)}, http.StatusInternalServerError) return } else if di == nil { h.writeError(w, influxql.Result{Err: fmt.Errorf("database not found: %q", database)}, http.StatusNotFound) return } if h.requireAuthentication && user == nil { h.writeError(w, influxql.Result{Err: fmt.Errorf("user is required to write to database %q", database)}, http.StatusUnauthorized) return } if h.requireAuthentication && !user.Authorize(influxql.WritePrivilege, database) { h.writeError(w, influxql.Result{Err: fmt.Errorf("%q user is not authorized to write to database %q", user.Name, database)}, http.StatusUnauthorized) return } // Determine required consistency level. consistency := cluster.ConsistencyLevelOne switch r.Form.Get("consistency") { case "all": consistency = cluster.ConsistencyLevelAll case "any": consistency = cluster.ConsistencyLevelAny case "one": consistency = cluster.ConsistencyLevelOne case "quorum": consistency = cluster.ConsistencyLevelQuorum } // Write points. if err := h.PointsWriter.WritePoints(&cluster.WritePointsRequest{ Database: database, RetentionPolicy: r.FormValue("rp"), ConsistencyLevel: consistency, Points: points, }); influxdb.IsClientError(err) { h.writeError(w, influxql.Result{Err: err}, http.StatusBadRequest) return } else if err != nil { h.writeError(w, influxql.Result{Err: err}, http.StatusInternalServerError) return } w.WriteHeader(http.StatusNoContent) }