mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-30 07:40:06 +01:00
f3e89754a9
Also reduce CPU usage when applying `series_limit` to scrape targets with constant set of metrics. The main idea is to perform the calculations on scrape_series_added and series_limit only if the set of metrics exposed by the target has been changed. Scrape targets rarely change the set of exposed metrics, so this optimization should reduce CPU usage in general case.
500 lines
12 KiB
Go
500 lines
12 KiB
Go
package prometheus
|
|
|
|
import (
|
|
"math"
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
func TestGetRowsDiff(t *testing.T) {
|
|
f := func(s1, s2, resultExpected string) {
|
|
t.Helper()
|
|
result := GetRowsDiff(s1, s2)
|
|
if result != resultExpected {
|
|
t.Fatalf("unexpected result for GetRowsDiff(%q, %q); got %q; want %q", s1, s2, result, resultExpected)
|
|
}
|
|
}
|
|
f("", "", "")
|
|
f("", "foo 1", "")
|
|
f(" ", "foo 1", "")
|
|
f("foo 123", "", "foo 0\n")
|
|
f("foo 123", "bar 3", "foo 0\n")
|
|
f("foo 123", "bar 3\nfoo 344", "")
|
|
f("foo{x=\"y\", z=\"a a a\"} 123", "bar 3\nfoo{x=\"y\", z=\"b b b\"} 344", "foo{x=\"y\",z=\"a a a\"} 0\n")
|
|
f("foo{bar=\"baz\"} 123\nx 3.4 5\ny 5 6", "x 34 342", "foo{bar=\"baz\"} 0\ny 0\n")
|
|
}
|
|
|
|
func TestAreIdenticalSeriesFast(t *testing.T) {
|
|
f := func(s1, s2 string, resultExpected bool) {
|
|
t.Helper()
|
|
result := AreIdenticalSeriesFast(s1, s2)
|
|
if result != resultExpected {
|
|
t.Fatalf("unexpected result for AreIdenticalSeries(%q, %q); got %v; want %v", s1, s2, result, resultExpected)
|
|
}
|
|
}
|
|
f("", "", true)
|
|
f("", "a 1", false) // different number of metrics
|
|
f(" ", " a 1", false) // different number of metrics
|
|
f("a 1", "", false) // different number of metrics
|
|
f(" a 1", " ", false) // different number of metrics
|
|
f("foo", "foo", false) // missing value
|
|
f("foo 1", "foo 1", true)
|
|
f("foo 1", "foo 2", true)
|
|
f("foo 1 ", "foo 2 ", true)
|
|
f("foo 1 ", "foo 2 ", false) // different number of spaces
|
|
f("foo 1 ", "foo 2 ", false) // different number of spaces
|
|
f("foo nan", "foo -inf", true)
|
|
f("foo 1 # coment x", "foo 2 #comment y", true)
|
|
f(" foo 1", " foo 1", true)
|
|
f(" foo 1", " foo 1", false) // different number of spaces in front of metric
|
|
f(" foo 1", " foo 1", false) // different number of spaces in front of metric
|
|
f("foo 1", "bar 1", false) // different metric name
|
|
f("foo 1", "fooo 1", false) // different metric name
|
|
f("foo 123", "foo 32.32", true)
|
|
f(`foo{bar="x"} -3.3e-6`, `foo{bar="x"} 23343`, true)
|
|
f(`foo{} 1`, `foo{} 234`, true)
|
|
f(`foo {x="y x" } 234`, `foo {x="y x" } 43.342`, true)
|
|
f(`foo {x="y x"} 234`, `foo{x="y x"} 43.342`, false) // different spaces
|
|
f("foo 2\nbar 3", "foo 34.43\nbar -34.3", true)
|
|
f("foo 2\nbar 3", "foo 34.43\nbarz -34.3", false) // different metric names
|
|
f("\nfoo 13\n", "\nfoo 3.4\n", true)
|
|
f("\nfoo 13", "\nfoo 3.4\n", false) // different number of blank lines
|
|
f("\nfoo 13\n", "\nfoo 3.4", false) // different number of blank lines
|
|
f("\n\nfoo 1", "\n\nfoo 34.43", true)
|
|
f("\n\nfoo 3434\n", "\n\nfoo 43\n", true)
|
|
f("\nfoo 1", "\n\nfoo 34.43", false) // different number of blank lines
|
|
f("#foo{bar}", "#baz", true)
|
|
f("", "#baz", false) // different number of comments
|
|
f("#foo{bar}", "", false) // different number of comments
|
|
f("#foo{bar}", "bar 3", false) // different number of comments
|
|
f("foo{bar} 2", "#bar 3", false) // different number of comments
|
|
f("#foo\n", "#bar", false) // different number of blank lines
|
|
f("#foo{bar}\n#baz", "#baz\n#xdsfds dsf", true)
|
|
f("# foo\nbar 234\nbaz{x=\"y\", z=\"\"} 3", "# foo\nbar 3.3\nbaz{x=\"y\", z=\"\"} 4323", true)
|
|
f("# foo\nbar 234\nbaz{x=\"z\", z=\"\"} 3", "# foo\nbar 3.3\nbaz{x=\"y\", z=\"\"} 4323", false) // different label value
|
|
f("foo {bar=\"xfdsdsffdsa\"} 1", "foo {x=\"y\"} 2", false) // different labels
|
|
f("foo {x=\"z\"} 1", "foo {x=\"y\"} 2", false) // different label value
|
|
|
|
// Lines with timestamps
|
|
f("foo 1 2", "foo 234 4334", true)
|
|
f("foo 2", "foo 3 4", false) // missing timestamp
|
|
f("foo 2 1", "foo 3", false) // missing timestamp
|
|
f("foo{bar=\"b az\"} 2 5", "foo{bar=\"b az\"} +6.3 7.43", true)
|
|
f("foo{bar=\"b az\"} 2 5 # comment ss ", "foo{bar=\"b az\"} +6.3 7.43 # comment as ", true)
|
|
f("foo{bar=\"b az\"} 2 5 #comment", "foo{bar=\"b az\"} +6.3 7.43 #comment {foo=\"bar\"} 21.44", true)
|
|
f("foo{bar=\"b az\"} +Inf 5", "foo{bar=\"b az\"} NaN 7.43", true)
|
|
f("foo{bar=\"b az\"} +Inf 5", "foo{bar=\"b az\"} nan 7.43", true)
|
|
f("foo{bar=\"b az\"} +Inf 5", "foo{bar=\"b az\"} nansf 7.43", false) // invalid value
|
|
}
|
|
|
|
func TestPrevBackslashesCount(t *testing.T) {
|
|
f := func(s string, nExpected int) {
|
|
t.Helper()
|
|
n := prevBackslashesCount(s)
|
|
if n != nExpected {
|
|
t.Fatalf("unexpected value returned from prevBackslashesCount(%q); got %d; want %d", s, n, nExpected)
|
|
}
|
|
}
|
|
f(``, 0)
|
|
f(`foo`, 0)
|
|
f(`\`, 1)
|
|
f(`\\`, 2)
|
|
f(`\\\`, 3)
|
|
f(`\\\a`, 0)
|
|
f(`foo\bar`, 0)
|
|
f(`foo\\`, 2)
|
|
f(`\\foo\`, 1)
|
|
f(`\\foo\\\\`, 4)
|
|
}
|
|
|
|
func TestFindClosingQuote(t *testing.T) {
|
|
f := func(s string, nExpected int) {
|
|
t.Helper()
|
|
n := findClosingQuote(s)
|
|
if n != nExpected {
|
|
t.Fatalf("unexpected value returned from findClosingQuote(%q); got %d; want %d", s, n, nExpected)
|
|
}
|
|
}
|
|
f(``, -1)
|
|
f(`x`, -1)
|
|
f(`"`, -1)
|
|
f(`""`, 1)
|
|
f(`foobar"`, -1)
|
|
f(`"foo"`, 4)
|
|
f(`"\""`, 3)
|
|
f(`"\\"`, 3)
|
|
f(`"\"`, -1)
|
|
f(`"foo\"bar\"baz"`, 14)
|
|
}
|
|
|
|
func TestUnescapeValue(t *testing.T) {
|
|
f := func(s, resultExpected string) {
|
|
t.Helper()
|
|
result := unescapeValue(s)
|
|
if result != resultExpected {
|
|
t.Fatalf("unexpected result; got %q; want %q", result, resultExpected)
|
|
}
|
|
}
|
|
f(``, "")
|
|
f(`f`, "f")
|
|
f(`foobar`, "foobar")
|
|
f(`\"\n\t`, "\"\n\\t")
|
|
|
|
// Edge cases
|
|
f(`foo\bar`, "foo\\bar")
|
|
f(`foo\`, "foo\\")
|
|
}
|
|
|
|
func TestRowsUnmarshalFailure(t *testing.T) {
|
|
f := func(s string) {
|
|
t.Helper()
|
|
var rows Rows
|
|
rows.Unmarshal(s)
|
|
if len(rows.Rows) != 0 {
|
|
t.Fatalf("unexpected number of rows parsed; got %d; want 0;\nrows:%#v", len(rows.Rows), rows.Rows)
|
|
}
|
|
|
|
// Try again
|
|
rows.Unmarshal(s)
|
|
if len(rows.Rows) != 0 {
|
|
t.Fatalf("unexpected number of rows parsed; got %d; want 0;\nrows:%#v", len(rows.Rows), rows.Rows)
|
|
}
|
|
}
|
|
|
|
// Empty lines and comments
|
|
f("")
|
|
f(" ")
|
|
f("\t")
|
|
f("\t \r")
|
|
f("\t\t \n\n # foobar")
|
|
f("#foobar")
|
|
f("#foobar\n")
|
|
|
|
// invalid tags
|
|
f("a{")
|
|
f("a { ")
|
|
f("a {foo")
|
|
f("a {foo} 3")
|
|
f("a {foo =")
|
|
f(`a {foo ="bar`)
|
|
f(`a {foo ="b\ar`)
|
|
f(`a {foo = "bar"`)
|
|
f(`a {foo ="bar",`)
|
|
f(`a {foo ="bar" , `)
|
|
f(`a {foo ="bar" , baz } 2`)
|
|
|
|
// empty metric name
|
|
f(`{foo="bar"}`)
|
|
|
|
// Invalid quotes for label value
|
|
f(`{foo='bar'} 23`)
|
|
f("{foo=`bar`} 23")
|
|
|
|
// Missing value
|
|
f("aaa")
|
|
f(" aaa")
|
|
f(" aaa ")
|
|
f(" aaa \n")
|
|
f(` aa{foo="bar"} ` + "\n")
|
|
|
|
// Invalid value
|
|
f("foo bar")
|
|
f("foo bar 124")
|
|
|
|
// Invalid timestamp
|
|
f("foo 123 bar")
|
|
}
|
|
|
|
func TestRowsUnmarshalSuccess(t *testing.T) {
|
|
f := func(s string, rowsExpected *Rows) {
|
|
t.Helper()
|
|
var rows Rows
|
|
rows.Unmarshal(s)
|
|
if !reflect.DeepEqual(rows.Rows, rowsExpected.Rows) {
|
|
t.Fatalf("unexpected rows;\ngot\n%+v;\nwant\n%+v", rows.Rows, rowsExpected.Rows)
|
|
}
|
|
|
|
// Try unmarshaling again
|
|
rows.Unmarshal(s)
|
|
if !reflect.DeepEqual(rows.Rows, rowsExpected.Rows) {
|
|
t.Fatalf("unexpected rows;\ngot\n%+v;\nwant\n%+v", rows.Rows, rowsExpected.Rows)
|
|
}
|
|
|
|
rows.Reset()
|
|
if len(rows.Rows) != 0 {
|
|
t.Fatalf("non-empty rows after reset: %+v", rows.Rows)
|
|
}
|
|
}
|
|
|
|
// Empty line or comment
|
|
f("", &Rows{})
|
|
f("\r", &Rows{})
|
|
f("\n\n", &Rows{})
|
|
f("\n\r\n", &Rows{})
|
|
f("\t \t\n\r\n#foobar\n # baz", &Rows{})
|
|
|
|
// Single line
|
|
f("foobar 78.9", &Rows{
|
|
Rows: []Row{{
|
|
Metric: "foobar",
|
|
Value: 78.9,
|
|
}},
|
|
})
|
|
f("foobar 123.456 789\n", &Rows{
|
|
Rows: []Row{{
|
|
Metric: "foobar",
|
|
Value: 123.456,
|
|
Timestamp: 789000,
|
|
}},
|
|
})
|
|
f("foobar{} 123.456 789.4354\n", &Rows{
|
|
Rows: []Row{{
|
|
Metric: "foobar",
|
|
Value: 123.456,
|
|
Timestamp: 789435,
|
|
}},
|
|
})
|
|
f(`# _ _
|
|
# ___ __ _ ___ ___ __ _ _ __ __| |_ __ __ _ _____ ___ __ ___ _ __| |_ ___ _ __
|
|
`+"# / __/ _` / __/ __|/ _` | '_ \\ / _` | '__/ _` |_____ / _ \\ \\/ / '_ \\ / _ \\| '__| __/ _ \\ '__|\n"+`
|
|
# | (_| (_| \__ \__ \ (_| | | | | (_| | | | (_| |_____| __/> <| |_) | (_) | | | || __/ |
|
|
# \___\__,_|___/___/\__,_|_| |_|\__,_|_| \__,_| \___/_/\_\ .__/ \___/|_| \__\___|_|
|
|
# |_|
|
|
#
|
|
# TYPE cassandra_token_ownership_ratio gauge
|
|
cassandra_token_ownership_ratio 78.9`, &Rows{
|
|
Rows: []Row{{
|
|
Metric: "cassandra_token_ownership_ratio",
|
|
Value: 78.9,
|
|
}},
|
|
})
|
|
|
|
// Incorrectly escaped backlash. This is real-world case, which must be supported.
|
|
f(`mssql_sql_server_active_transactions_sec{loginname="domain\somelogin",env="develop"} 56`, &Rows{
|
|
Rows: []Row{{
|
|
Metric: "mssql_sql_server_active_transactions_sec",
|
|
Tags: []Tag{
|
|
{
|
|
Key: "loginname",
|
|
Value: "domain\\somelogin",
|
|
},
|
|
{
|
|
Key: "env",
|
|
Value: "develop",
|
|
},
|
|
},
|
|
Value: 56,
|
|
}},
|
|
})
|
|
|
|
// Exemplars - see https://github.com/OpenObservability/OpenMetrics/blob/master/OpenMetrics.md#exemplars-1
|
|
f(`foo_bucket{le="10",a="#b"} 17 # {trace_id="oHg5SJ#YRHA0"} 9.8 1520879607.789
|
|
abc 123 456 # foobar
|
|
foo 344#bar`, &Rows{
|
|
Rows: []Row{
|
|
{
|
|
Metric: "foo_bucket",
|
|
Tags: []Tag{
|
|
{
|
|
Key: "le",
|
|
Value: "10",
|
|
},
|
|
{
|
|
Key: "a",
|
|
Value: "#b",
|
|
},
|
|
},
|
|
Value: 17,
|
|
},
|
|
{
|
|
Metric: "abc",
|
|
Value: 123,
|
|
Timestamp: 456000,
|
|
},
|
|
{
|
|
Metric: "foo",
|
|
Value: 344,
|
|
},
|
|
},
|
|
})
|
|
|
|
// "Infinity" word - this has been added in OpenMetrics.
|
|
// See https://github.com/OpenObservability/OpenMetrics/blob/master/OpenMetrics.md
|
|
// Checks for https://github.com/VictoriaMetrics/VictoriaMetrics/issues/924
|
|
inf := math.Inf(1)
|
|
f(`
|
|
foo Infinity
|
|
bar +Infinity
|
|
baz -infinity
|
|
aaa +inf
|
|
bbb -INF
|
|
ccc INF
|
|
`, &Rows{
|
|
Rows: []Row{
|
|
{
|
|
Metric: "foo",
|
|
Value: inf,
|
|
},
|
|
{
|
|
Metric: "bar",
|
|
Value: inf,
|
|
},
|
|
{
|
|
Metric: "baz",
|
|
Value: -inf,
|
|
},
|
|
{
|
|
Metric: "aaa",
|
|
Value: inf,
|
|
},
|
|
{
|
|
Metric: "bbb",
|
|
Value: -inf,
|
|
},
|
|
{
|
|
Metric: "ccc",
|
|
Value: inf,
|
|
},
|
|
},
|
|
})
|
|
|
|
// Timestamp bigger than 1<<31.
|
|
// It should be parsed in milliseconds.
|
|
f("aaa 1123 429496729600", &Rows{
|
|
Rows: []Row{{
|
|
Metric: "aaa",
|
|
Value: 1123,
|
|
Timestamp: 429496729600,
|
|
}},
|
|
})
|
|
|
|
// Floating-point timestamps in OpenMetric format.
|
|
f("aaa 1123 42949.567", &Rows{
|
|
Rows: []Row{{
|
|
Metric: "aaa",
|
|
Value: 1123,
|
|
Timestamp: 42949567,
|
|
}},
|
|
})
|
|
|
|
// Tags
|
|
f(`foo{bar="baz"} 1 2`, &Rows{
|
|
Rows: []Row{{
|
|
Metric: "foo",
|
|
Tags: []Tag{{
|
|
Key: "bar",
|
|
Value: "baz",
|
|
}},
|
|
Value: 1,
|
|
Timestamp: 2000,
|
|
}},
|
|
})
|
|
f(`foo{bar="b\"a\\z"} -1.2`, &Rows{
|
|
Rows: []Row{{
|
|
Metric: "foo",
|
|
Tags: []Tag{{
|
|
Key: "bar",
|
|
Value: "b\"a\\z",
|
|
}},
|
|
Value: -1.2,
|
|
}},
|
|
})
|
|
// Empty tags
|
|
f(`foo {bar="baz",aa="",x="y",="z"} 1 2`, &Rows{
|
|
Rows: []Row{{
|
|
Metric: "foo",
|
|
Tags: []Tag{
|
|
{
|
|
Key: "bar",
|
|
Value: "baz",
|
|
},
|
|
{
|
|
Key: "aa",
|
|
Value: "",
|
|
},
|
|
{
|
|
Key: "x",
|
|
Value: "y",
|
|
},
|
|
},
|
|
Value: 1,
|
|
Timestamp: 2000,
|
|
}},
|
|
})
|
|
|
|
// Trailing comma after tag
|
|
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/350
|
|
f(`foo{bar="baz",} 1 2`, &Rows{
|
|
Rows: []Row{{
|
|
Metric: "foo",
|
|
Tags: []Tag{{
|
|
Key: "bar",
|
|
Value: "baz",
|
|
}},
|
|
Value: 1,
|
|
Timestamp: 2000,
|
|
}},
|
|
})
|
|
|
|
// Multi lines
|
|
f("# foo\n # bar ba zzz\nfoo 0.3 2\naaa 3\nbar.baz 0.34 43\n", &Rows{
|
|
Rows: []Row{
|
|
{
|
|
Metric: "foo",
|
|
Value: 0.3,
|
|
Timestamp: 2000,
|
|
},
|
|
{
|
|
Metric: "aaa",
|
|
Value: 3,
|
|
},
|
|
{
|
|
Metric: "bar.baz",
|
|
Value: 0.34,
|
|
Timestamp: 43000,
|
|
},
|
|
},
|
|
})
|
|
|
|
// Multi lines with invalid line
|
|
f("\t foo\t { } 0.3\t 2\naaa\n bar.baz 0.34 43\n", &Rows{
|
|
Rows: []Row{
|
|
{
|
|
Metric: "foo",
|
|
Value: 0.3,
|
|
Timestamp: 2000,
|
|
},
|
|
{
|
|
Metric: "bar.baz",
|
|
Value: 0.34,
|
|
Timestamp: 43000,
|
|
},
|
|
},
|
|
})
|
|
|
|
// Spaces around tags
|
|
f(`vm_accounting { name="vminsertRows", accountID = "1" , projectID= "1" } 277779100`, &Rows{
|
|
Rows: []Row{
|
|
{
|
|
Metric: "vm_accounting",
|
|
Tags: []Tag{
|
|
{
|
|
Key: "name",
|
|
Value: "vminsertRows",
|
|
},
|
|
{
|
|
Key: "accountID",
|
|
Value: "1",
|
|
},
|
|
{
|
|
Key: "projectID",
|
|
Value: "1",
|
|
},
|
|
},
|
|
Value: 277779100,
|
|
Timestamp: 0,
|
|
},
|
|
},
|
|
})
|
|
}
|