diff --git a/app/victoria-metrics/testdata/graphite/name-plus-negative-filter.json b/app/victoria-metrics/testdata/graphite/name-plus-negative-filter.json new file mode 100644 index 000000000..32cddbee6 --- /dev/null +++ b/app/victoria-metrics/testdata/graphite/name-plus-negative-filter.json @@ -0,0 +1,16 @@ +{ + "name": "name-plus-negative-filter", + "issue": "", + "data": [ + "name-plus-negative-filter;foo=123 1 {TIME_S-1m}", + "name-plus-negative-filter;bar=123 2 {TIME_S-1m}", + "name-plus-negative-filter;foo=qwe 3 {TIME_S-1m}" + ], + "query": ["/api/v1/query?query={__name__='name-plus-negative-filter',foo!='123'}&time={TIME_S-1m}"], + "result_query": { + "status":"success", + "data":{"resultType":"vector","result":[ + {"metric":{"__name__":"name-plus-negative-filter","foo":"qwe"},"value":["{TIME_S-1m}","3"]} + ]} + } +} diff --git a/docs/Articles.md b/docs/Articles.md index ed2a1f486..4a4248610 100644 --- a/docs/Articles.md +++ b/docs/Articles.md @@ -25,6 +25,7 @@ * [High-performance Graphite storage solution on top of VictoriaMetrics](https://golangexample.com/a-high-performance-graphite-storage-solution/) * [Cloud Native Model Driven Telemetry Stack on OpenShift](https://cer6erus.medium.com/cloud-native-model-driven-telemetry-stack-on-openshift-80712621f5bc) * [Observability, Availability & DORA’s Research Program](https://medium.com/alteos-tech-blog/observability-availability-and-doras-research-program-85deb6680e78) +* [Tame Kubernetes Costs with Percona Monitoring and Management and Prometheus Operator](https://www.percona.com/blog/2021/02/12/tame-kubernetes-costs-with-percona-monitoring-and-management-and-prometheus-operator/) ## Our articles diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 3688633f1..361d7d61f 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -3,6 +3,11 @@ # tip +# [v1.54.1](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.54.1) + +* BUGFIX: properly handle queries containing a filter on metric name plus any number of negative filters and zero non-negative filters. For example, `node_cpu_seconds_total{mode!="idle"}`. The bug was introduced in [v1.54.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.54.0). + + # [v1.54.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.54.0) * FEATURE: optimize searching for matching metrics for `metric{}` queries if `` contains at least a single filter. For example, the query `up{job="foobar"}` should find the matching time series much faster than previously. diff --git a/lib/storage/tag_filters.go b/lib/storage/tag_filters.go index f4e3a11c0..6f88b6e54 100644 --- a/lib/storage/tag_filters.go +++ b/lib/storage/tag_filters.go @@ -28,14 +28,15 @@ func convertToCompositeTagFilterss(tfss []*TagFilters) []*TagFilters { func convertToCompositeTagFilters(tfs *TagFilters) *TagFilters { // Search for metric name filter, which must be used for creating composite filters. var name []byte + hasPositiveFilter := false for _, tf := range tfs.tfs { if len(tf.key) == 0 && !tf.isNegative && !tf.isRegexp { name = tf.value - break + } else if !tf.isNegative { + hasPositiveFilter = true } } if len(name) == 0 { - // There is no metric name filter, so composite filters cannot be created. atomic.AddUint64(&compositeFilterMissingConversions, 1) return tfs } @@ -44,7 +45,7 @@ func convertToCompositeTagFilters(tfs *TagFilters) *TagFilters { compositeFilters := 0 for _, tf := range tfs.tfs { if len(tf.key) == 0 { - if tf.isNegative || tf.isRegexp || string(tf.value) != string(name) { + if !hasPositiveFilter || tf.isNegative || tf.isRegexp || string(tf.value) != string(name) { tfsNew = append(tfsNew, tf) } continue diff --git a/lib/storage/tag_filters_test.go b/lib/storage/tag_filters_test.go index 80cc7fee9..fe4716c8a 100644 --- a/lib/storage/tag_filters_test.go +++ b/lib/storage/tag_filters_test.go @@ -148,6 +148,70 @@ func TestConvertToCompositeTagFilters(t *testing.T) { }, }) + // A name filter with a single negative filter + f([]TagFilter{ + { + Key: nil, + Value: []byte("bar"), + IsNegative: false, + IsRegexp: false, + }, + { + Key: []byte("foo"), + Value: []byte("abc"), + IsNegative: true, + IsRegexp: false, + }, + }, []TagFilter{ + { + Key: nil, + Value: []byte("bar"), + IsNegative: false, + IsRegexp: false, + }, + { + Key: []byte("\xfe\x03barfoo"), + Value: []byte("abc"), + IsNegative: true, + IsRegexp: false, + }, + }) + + // A name filter with a negative and a positive filter + f([]TagFilter{ + { + Key: nil, + Value: []byte("bar"), + IsNegative: false, + IsRegexp: false, + }, + { + Key: []byte("foo"), + Value: []byte("abc"), + IsNegative: true, + IsRegexp: false, + }, + { + Key: []byte("a"), + Value: []byte("b.+"), + IsNegative: false, + IsRegexp: true, + }, + }, []TagFilter{ + { + Key: []byte("\xfe\x03barfoo"), + Value: []byte("abc"), + IsNegative: true, + IsRegexp: false, + }, + { + Key: []byte("\xfe\x03bara"), + Value: []byte("b.+"), + IsNegative: false, + IsRegexp: true, + }, + }) + // Two name filters with non-name filter. f([]TagFilter{ { @@ -171,19 +235,19 @@ func TestConvertToCompositeTagFilters(t *testing.T) { }, []TagFilter{ { Key: nil, - Value: []byte("baz"), + Value: []byte("bar"), IsNegative: false, IsRegexp: false, }, { - Key: []byte("\xfe\x03barfoo"), + Key: []byte("\xfe\x03bazfoo"), Value: []byte("abc"), IsNegative: false, IsRegexp: false, }, }) - // A name filter with negative regexp non-name filter, which can be converted to non-regexp. + // A name filter with regexp non-name filter, which can be converted to non-regexp. f([]TagFilter{ { Key: nil, @@ -194,19 +258,19 @@ func TestConvertToCompositeTagFilters(t *testing.T) { { Key: []byte("foo"), Value: []byte("abc"), - IsNegative: true, + IsNegative: false, IsRegexp: true, }, }, []TagFilter{ { Key: []byte("\xfe\x03barfoo"), Value: []byte("abc"), - IsNegative: true, + IsNegative: false, IsRegexp: false, }, }) - // A name filter with negative regexp non-name filter. + // A name filter with regexp non-name filter. f([]TagFilter{ { Key: nil, @@ -217,14 +281,14 @@ func TestConvertToCompositeTagFilters(t *testing.T) { { Key: []byte("foo"), Value: []byte("abc.+"), - IsNegative: true, + IsNegative: false, IsRegexp: true, }, }, []TagFilter{ { Key: []byte("\xfe\x03barfoo"), Value: []byte("abc.+"), - IsNegative: true, + IsNegative: false, IsRegexp: true, }, }) @@ -269,7 +333,7 @@ func TestConvertToCompositeTagFilters(t *testing.T) { { Key: []byte("foo"), Value: []byte("abc"), - IsNegative: true, + IsNegative: false, IsRegexp: true, }, { @@ -282,7 +346,7 @@ func TestConvertToCompositeTagFilters(t *testing.T) { { Key: []byte("\xfe\x03barfoo"), Value: []byte("abc"), - IsNegative: true, + IsNegative: false, IsRegexp: false, }, { @@ -304,14 +368,14 @@ func TestConvertToCompositeTagFilters(t *testing.T) { { Key: []byte("foo"), Value: []byte("abc"), - IsNegative: true, + IsNegative: false, IsRegexp: false, }, }, []TagFilter{ { Key: []byte("\xfe\x03barfoo"), Value: []byte("abc"), - IsNegative: true, + IsNegative: false, IsRegexp: false, }, })