From 6ed71a229ea9ffcf3e97178b9dd260fde9595b16 Mon Sep 17 00:00:00 2001 From: Fish-pro Date: Thu, 29 Dec 2022 23:23:07 +0800 Subject: [PATCH 01/36] Use errors.Is to check for a specific error Signed-off-by: Fish-pro --- model/textparse/protobufparse_test.go | 3 ++- scrape/scrape.go | 2 +- storage/remote/read_handler_test.go | 5 +++-- tsdb/head.go | 2 +- tsdb/wal.go | 2 +- tsdb/wlog/live_reader.go | 2 +- tsdb/wlog/reader.go | 2 +- 7 files changed, 10 insertions(+), 8 deletions(-) diff --git a/model/textparse/protobufparse_test.go b/model/textparse/protobufparse_test.go index b8b8681724..cbd803579f 100644 --- a/model/textparse/protobufparse_test.go +++ b/model/textparse/protobufparse_test.go @@ -16,6 +16,7 @@ package textparse import ( "bytes" "encoding/binary" + "errors" "io" "testing" @@ -604,7 +605,7 @@ metric: < for { et, err := p.Next() - if err == io.EOF { + if errors.Is(err, io.EOF) { break } require.NoError(t, err) diff --git a/scrape/scrape.go b/scrape/scrape.go index d3e10e6d2d..fce3ad34c8 100644 --- a/scrape/scrape.go +++ b/scrape/scrape.go @@ -1544,7 +1544,7 @@ loop: fh *histogram.FloatHistogram ) if et, err = p.Next(); err != nil { - if err == io.EOF { + if errors.Is(err, io.EOF) { err = nil } break diff --git a/storage/remote/read_handler_test.go b/storage/remote/read_handler_test.go index ba2517a409..b5b717455c 100644 --- a/storage/remote/read_handler_test.go +++ b/storage/remote/read_handler_test.go @@ -15,6 +15,7 @@ package remote import ( "bytes" + "errors" "io" "net/http" "net/http/httptest" @@ -163,7 +164,7 @@ func BenchmarkStreamReadEndpoint(b *testing.B) { for { res := &prompb.ChunkedReadResponse{} err := stream.NextProto(res) - if err == io.EOF { + if errors.Is(err, io.EOF) { break } require.NoError(b, err) @@ -253,7 +254,7 @@ func TestStreamReadEndpoint(t *testing.T) { for { res := &prompb.ChunkedReadResponse{} err := stream.NextProto(res) - if err == io.EOF { + if errors.Is(err, io.EOF) { break } require.NoError(t, err) diff --git a/tsdb/head.go b/tsdb/head.go index 37e12d8a42..e82ff1eaea 100644 --- a/tsdb/head.go +++ b/tsdb/head.go @@ -667,7 +667,7 @@ func (h *Head) Init(minValidTime int64) error { offset = snapOffset } sr, err := wlog.NewSegmentBufReaderWithOffset(offset, s) - if errors.Cause(err) == io.EOF { + if errors.Is(err, io.EOF) { // File does not exist. continue } diff --git a/tsdb/wal.go b/tsdb/wal.go index 03043c781b..38584847eb 100644 --- a/tsdb/wal.go +++ b/tsdb/wal.go @@ -1018,7 +1018,7 @@ func (r *walReader) next() bool { // If we reached the end of the reader, advance to the next one // and close. // Do not close on the last one as it will still be appended to. - if err == io.EOF { + if errors.Is(err, io.EOF) { if r.cur == len(r.files)-1 { return false } diff --git a/tsdb/wlog/live_reader.go b/tsdb/wlog/live_reader.go index fd949a9630..29467aef4a 100644 --- a/tsdb/wlog/live_reader.go +++ b/tsdb/wlog/live_reader.go @@ -96,7 +96,7 @@ type LiveReader struct { // not be used again. It is up to the user to decide when to stop trying should // io.EOF be returned. func (r *LiveReader) Err() error { - if r.eofNonErr && r.err == io.EOF { + if r.eofNonErr && errors.Is(r.err, io.EOF) { return nil } return r.err diff --git a/tsdb/wlog/reader.go b/tsdb/wlog/reader.go index e2b50d4b2a..cba2167646 100644 --- a/tsdb/wlog/reader.go +++ b/tsdb/wlog/reader.go @@ -43,7 +43,7 @@ func NewReader(r io.Reader) *Reader { // It must not be called again after it returned false. func (r *Reader) Next() bool { err := r.next() - if errors.Cause(err) == io.EOF { + if errors.Is(err, io.EOF) { // The last WAL segment record shouldn't be torn(should be full or last). // The last record would be torn after a crash just before // the last record part could be persisted to disk. From 3c468cd4e82b89d1d0e0e90a267b6107016ec917 Mon Sep 17 00:00:00 2001 From: Saeid Bostandoust <49065072+ssbostan@users.noreply.github.com> Date: Mon, 2 Jan 2023 14:17:11 +0000 Subject: [PATCH 02/36] update ServiceAccount for new kubernetes versions Signed-off-by: Saeid Bostandoust <49065072+ssbostan@users.noreply.github.com> --- documentation/examples/rbac-setup.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/documentation/examples/rbac-setup.yml b/documentation/examples/rbac-setup.yml index 350d3eda28..1e3ee12a26 100644 --- a/documentation/examples/rbac-setup.yml +++ b/documentation/examples/rbac-setup.yml @@ -32,6 +32,17 @@ kind: ServiceAccount metadata: name: prometheus namespace: default +secrets: + - name: prometheus-sa-token +--- +apiVersion: v1 +kind: Secret +metadata: + name: prometheus-sa-token + namespace: default + annotations: + kubernetes.io/service-account.name: prometheus +type: kubernetes.io/service-account-token --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding From 80ac0d7c82cc325932c1934ad9f7e55e8ed3e479 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Fri, 23 Dec 2022 20:24:20 +0000 Subject: [PATCH 03/36] promql: add benchmark for match against blank string Blank strings are not handled efficiently by tsdb. Signed-off-by: Bryan Boreham --- promql/bench_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/promql/bench_test.go b/promql/bench_test.go index 6fb20d1aba..8abfcfdd20 100644 --- a/promql/bench_test.go +++ b/promql/bench_test.go @@ -174,6 +174,15 @@ func rangeQueryCases() []benchCase { { expr: "a_X + on(l) group_right a_one", }, + // Label compared to blank string. + { + expr: "count({__name__!=\"\"})", + steps: 1, + }, + { + expr: "count({__name__!=\"\",l=\"\"})", + steps: 1, + }, } // X in an expr will be replaced by different metric sizes. From cf92cd26880b5bd1c694d5a0cf644eea76b5f3ea Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Wed, 4 Jan 2023 17:45:53 +0000 Subject: [PATCH 04/36] tsdb: sort values for Postings only when required In the head and in v1 postings on disk, it makes no difference whether postings are sorted. Only for v2 does the code step through in order. So, move the sorting to where it is required, and thus skip it entirely in the head. Label values in on-disk blocks are already sorted, but `slices.Sort` is very fast on already-sorted data so we don't bother checking. Signed-off-by: Bryan Boreham --- tsdb/block.go | 2 +- tsdb/index/index.go | 1 + tsdb/querier.go | 18 ------------------ 3 files changed, 2 insertions(+), 19 deletions(-) diff --git a/tsdb/block.go b/tsdb/block.go index 401ab59e0a..d7b344ab5d 100644 --- a/tsdb/block.go +++ b/tsdb/block.go @@ -72,7 +72,7 @@ type IndexReader interface { // Postings returns the postings list iterator for the label pairs. // The Postings here contain the offsets to the series inside the index. // Found IDs are not strictly required to point to a valid Series, e.g. - // during background garbage collections. Input values must be sorted. + // during background garbage collections. Postings(name string, values ...string) (index.Postings, error) // SortedPostings returns a postings list that is reordered to be sorted diff --git a/tsdb/index/index.go b/tsdb/index/index.go index 407b5380fa..4d68f240a4 100644 --- a/tsdb/index/index.go +++ b/tsdb/index/index.go @@ -1643,6 +1643,7 @@ func (r *Reader) Postings(name string, values ...string) (Postings, error) { return EmptyPostings(), nil } + slices.Sort(values) // Values must be in order so we can step through the table on disk. res := make([]Postings, 0, len(values)) skip := 0 valueIndex := 0 diff --git a/tsdb/querier.go b/tsdb/querier.go index 16e774b0b5..f6d3ec16c6 100644 --- a/tsdb/querier.go +++ b/tsdb/querier.go @@ -21,7 +21,6 @@ import ( "github.com/oklog/ulid" "github.com/pkg/errors" - "golang.org/x/exp/slices" "github.com/prometheus/prometheus/model/histogram" "github.com/prometheus/prometheus/model/labels" @@ -322,7 +321,6 @@ func postingsForMatcher(ix IndexReader, m *labels.Matcher) (index.Postings, erro if m.Type == labels.MatchRegexp { setMatches := findSetMatches(m.GetRegexString()) if len(setMatches) > 0 { - slices.Sort(setMatches) return ix.Postings(m.Name, setMatches...) } } @@ -333,14 +331,9 @@ func postingsForMatcher(ix IndexReader, m *labels.Matcher) (index.Postings, erro } var res []string - lastVal, isSorted := "", true for _, val := range vals { if m.Matches(val) { res = append(res, val) - if isSorted && val < lastVal { - isSorted = false - } - lastVal = val } } @@ -348,9 +341,6 @@ func postingsForMatcher(ix IndexReader, m *labels.Matcher) (index.Postings, erro return index.EmptyPostings(), nil } - if !isSorted { - slices.Sort(res) - } return ix.Postings(m.Name, res...) } @@ -362,20 +352,12 @@ func inversePostingsForMatcher(ix IndexReader, m *labels.Matcher) (index.Posting } var res []string - lastVal, isSorted := "", true for _, val := range vals { if !m.Matches(val) { res = append(res, val) - if isSorted && val < lastVal { - isSorted = false - } - lastVal = val } } - if !isSorted { - slices.Sort(res) - } return ix.Postings(m.Name, res...) } From e61348d9f3ac4e1e579db244ee9bab48fc18aaba Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Thu, 5 Jan 2023 14:05:29 +0000 Subject: [PATCH 05/36] tsdb/index: fast-track postings for label="" We need to special-case ""="" too, which is used in some tests to mean "everything". Signed-off-by: Bryan Boreham --- tsdb/querier.go | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/tsdb/querier.go b/tsdb/querier.go index f6d3ec16c6..042fe76de8 100644 --- a/tsdb/querier.go +++ b/tsdb/querier.go @@ -239,7 +239,14 @@ func PostingsForMatchers(ix IndexReader, ms ...*labels.Matcher) (index.Postings, } for _, m := range ms { - if labelMustBeSet[m.Name] { + if m.Name == "" && m.Value == "" { // Special-case for AllPostings, used in tests at least. + k, v := index.AllPostingsKey() + allPostings, err := ix.Postings(k, v) + if err != nil { + return nil, err + } + its = append(its, allPostings) + } else if labelMustBeSet[m.Name] { // If this matcher must be non-empty, we can be smarter. matchesEmpty := m.Matches("") isNot := m.Type == labels.MatchNotEqual || m.Type == labels.MatchNotRegexp @@ -352,9 +359,14 @@ func inversePostingsForMatcher(ix IndexReader, m *labels.Matcher) (index.Posting } var res []string - for _, val := range vals { - if !m.Matches(val) { - res = append(res, val) + // If the inverse match is ="", we just want all the values. + if m.Type == labels.MatchEqual && m.Value == "" { + res = vals + } else { + for _, val := range vals { + if !m.Matches(val) { + res = append(res, val) + } } } From e9d9bb1b083aa8b199a945e722a11807b84bcf78 Mon Sep 17 00:00:00 2001 From: beorn7 Date: Thu, 5 Jan 2023 15:21:18 +0100 Subject: [PATCH 06/36] textparse: Handle unknown metric types in protobuf gracefully So far, the parser hasn't validated that the type is valid in the `Next()` call. Later, in the `Series()` call, however, it assumes that we will only see valid types and therefore panics with `encountered unexpected metric type, this is a bug`. This commit fixes said bug by adding validation to the `Next()` call. Signed-off-by: beorn7 --- model/textparse/protobufparse.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/model/textparse/protobufparse.go b/model/textparse/protobufparse.go index 37c6f0ebb0..d18afa04dc 100644 --- a/model/textparse/protobufparse.go +++ b/model/textparse/protobufparse.go @@ -331,7 +331,7 @@ func (p *ProtobufParser) Next() (Entry, error) { } // We are at the beginning of a metric family. Put only the name - // into metricBytes and validate only name and help for now. + // into metricBytes and validate only name, help, and type for now. name := p.mf.GetName() if !model.IsValidMetricName(model.LabelValue(name)) { return EntryInvalid, errors.Errorf("invalid metric name: %s", name) @@ -339,6 +339,16 @@ func (p *ProtobufParser) Next() (Entry, error) { if help := p.mf.GetHelp(); !utf8.ValidString(help) { return EntryInvalid, errors.Errorf("invalid help for metric %q: %s", name, help) } + switch p.mf.GetType() { + case dto.MetricType_COUNTER, + dto.MetricType_GAUGE, + dto.MetricType_HISTOGRAM, + dto.MetricType_SUMMARY, + dto.MetricType_UNTYPED: + // All good. + default: + return EntryInvalid, errors.Errorf("unknown metric type for metric %q: %s", name, p.mf.GetType()) + } p.metricBytes.Reset() p.metricBytes.WriteString(name) From b5d4a94e9d686c1c208106f481c33087ee94e94d Mon Sep 17 00:00:00 2001 From: beorn7 Date: Thu, 5 Jan 2023 15:39:10 +0100 Subject: [PATCH 07/36] textparse: Add gauge histogram support to protobuf parsing With this commit, the parser stops to see a gauge histogram (whether native or conventional) as an unexpected metric type. It ingests it normally, it even sets the `GaugeHistogram` type in the metadata (as it has already done for a conventional gauge histogram scraped using OpenMetrics), but it otherwise treats it as a normal counter-like histogram. Once #11783 is merged, though, it should be very easy to utilize the type information. Signed-off-by: beorn7 --- model/textparse/protobufparse.go | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/model/textparse/protobufparse.go b/model/textparse/protobufparse.go index d18afa04dc..003f88456e 100644 --- a/model/textparse/protobufparse.go +++ b/model/textparse/protobufparse.go @@ -105,7 +105,7 @@ func (p *ProtobufParser) Series() ([]byte, *int64, float64) { default: v = s.GetQuantile()[p.fieldPos].GetValue() } - case dto.MetricType_HISTOGRAM: + case dto.MetricType_HISTOGRAM, dto.MetricType_GAUGE_HISTOGRAM: // This should only happen for a legacy histogram. h := m.GetHistogram() switch p.fieldPos { @@ -225,6 +225,8 @@ func (p *ProtobufParser) Type() ([]byte, MetricType) { return n, MetricTypeGauge case dto.MetricType_HISTOGRAM: return n, MetricTypeHistogram + case dto.MetricType_GAUGE_HISTOGRAM: + return n, MetricTypeGaugeHistogram case dto.MetricType_SUMMARY: return n, MetricTypeSummary } @@ -273,7 +275,7 @@ func (p *ProtobufParser) Exemplar(ex *exemplar.Exemplar) bool { switch p.mf.GetType() { case dto.MetricType_COUNTER: exProto = m.GetCounter().GetExemplar() - case dto.MetricType_HISTOGRAM: + case dto.MetricType_HISTOGRAM, dto.MetricType_GAUGE_HISTOGRAM: bb := m.GetHistogram().GetBucket() if p.fieldPos < 0 { if p.state == EntrySeries { @@ -343,6 +345,7 @@ func (p *ProtobufParser) Next() (Entry, error) { case dto.MetricType_COUNTER, dto.MetricType_GAUGE, dto.MetricType_HISTOGRAM, + dto.MetricType_GAUGE_HISTOGRAM, dto.MetricType_SUMMARY, dto.MetricType_UNTYPED: // All good. @@ -356,7 +359,8 @@ func (p *ProtobufParser) Next() (Entry, error) { case EntryHelp: p.state = EntryType case EntryType: - if p.mf.GetType() == dto.MetricType_HISTOGRAM && + t := p.mf.GetType() + if (t == dto.MetricType_HISTOGRAM || t == dto.MetricType_GAUGE_HISTOGRAM) && isNativeHistogram(p.mf.GetMetric()[0].GetHistogram()) { p.state = EntryHistogram } else { @@ -366,8 +370,11 @@ func (p *ProtobufParser) Next() (Entry, error) { return EntryInvalid, err } case EntryHistogram, EntrySeries: + t := p.mf.GetType() if p.state == EntrySeries && !p.fieldsDone && - (p.mf.GetType() == dto.MetricType_SUMMARY || p.mf.GetType() == dto.MetricType_HISTOGRAM) { + (t == dto.MetricType_SUMMARY || + t == dto.MetricType_HISTOGRAM || + t == dto.MetricType_GAUGE_HISTOGRAM) { p.fieldPos++ } else { p.metricPos++ @@ -428,7 +435,7 @@ func (p *ProtobufParser) getMagicName() string { if p.fieldPos == -1 { return p.mf.GetName() + "_sum" } - if t == dto.MetricType_HISTOGRAM { + if t == dto.MetricType_HISTOGRAM || t == dto.MetricType_GAUGE_HISTOGRAM { return p.mf.GetName() + "_bucket" } return p.mf.GetName() @@ -446,7 +453,7 @@ func (p *ProtobufParser) getMagicLabel() (bool, string, string) { q := qq[p.fieldPos] p.fieldsDone = p.fieldPos == len(qq)-1 return true, model.QuantileLabel, formatOpenMetricsFloat(q.GetQuantile()) - case dto.MetricType_HISTOGRAM: + case dto.MetricType_HISTOGRAM, dto.MetricType_GAUGE_HISTOGRAM: bb := p.mf.GetMetric()[p.metricPos].GetHistogram().GetBucket() if p.fieldPos >= len(bb) { p.fieldsDone = true From da6c0140391f5c854a76c9779fd23d2f1a06f517 Mon Sep 17 00:00:00 2001 From: Saeid Bostandoust <49065072+ssbostan@users.noreply.github.com> Date: Thu, 5 Jan 2023 15:43:48 +0000 Subject: [PATCH 08/36] update ServiceAccount for new kubernetes versions Signed-off-by: Saeid Bostandoust <49065072+ssbostan@users.noreply.github.com> --- documentation/examples/rbac-setup.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/documentation/examples/rbac-setup.yml b/documentation/examples/rbac-setup.yml index 1e3ee12a26..e02c0ca319 100644 --- a/documentation/examples/rbac-setup.yml +++ b/documentation/examples/rbac-setup.yml @@ -32,8 +32,6 @@ kind: ServiceAccount metadata: name: prometheus namespace: default -secrets: - - name: prometheus-sa-token --- apiVersion: v1 kind: Secret From 97626c95830aac287a24dca233d4a8aeaa92128f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gy=C3=B6rgy=20Krajcsovits?= Date: Sun, 8 Jan 2023 16:29:02 +0100 Subject: [PATCH 09/36] Fix comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Comment was not updated when code changed from labels to builder in #11717 Signed-off-by: György Krajcsovits --- tsdb/index/index.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsdb/index/index.go b/tsdb/index/index.go index 407b5380fa..4a57a49f18 100644 --- a/tsdb/index/index.go +++ b/tsdb/index/index.go @@ -1836,7 +1836,7 @@ func (dec *Decoder) LabelValueFor(b []byte, label string) (string, error) { } // Series decodes a series entry from the given byte slice into builder and chks. -// Previous contents of lbls can be overwritten - make sure you copy before retaining. +// Previous contents of builder can be overwritten - make sure you copy before retaining. func (dec *Decoder) Series(b []byte, builder *labels.ScratchBuilder, chks *[]chunks.Meta) error { builder.Reset() *chks = (*chks)[:0] From 3b4cbf8da4c5b9b8c6c2a0a30ae182dfbb0b8213 Mon Sep 17 00:00:00 2001 From: Levi Harrison Date: Sun, 8 Jan 2023 18:04:00 -0500 Subject: [PATCH 10/36] Inject readiness state through context (#11617) Signed-off-by: Levi Harrison Signed-off-by: Levi Harrison --- web/ui/react-app/public/index.html | 4 + web/ui/react-app/src/App.test.tsx | 2 +- web/ui/react-app/src/App.tsx | 84 ++++++++++--------- .../src/components/withStartingIndicator.tsx | 4 +- .../react-app/src/contexts/ReadyContext.tsx | 9 ++ web/ui/react-app/src/index.tsx | 7 +- web/web.go | 1 + 7 files changed, 68 insertions(+), 43 deletions(-) create mode 100644 web/ui/react-app/src/contexts/ReadyContext.tsx diff --git a/web/ui/react-app/public/index.html b/web/ui/react-app/public/index.html index a3f7b7a850..57131a26b6 100755 --- a/web/ui/react-app/public/index.html +++ b/web/ui/react-app/public/index.html @@ -16,10 +16,14 @@ - PROMETHEUS_AGENT_MODE is replaced by a boolean indicating if Prometheus is running in agent mode. It true, it will disable querying capacities in the UI and generally adapt the UI to the agent mode. It has to be represented as a string, because booleans can be mangled to !1 in production builds. + - PROMETHEUS_READY is replaced by a boolean indicating whether Prometheus was ready at the time the + web app was served. It has to be represented as a string, because booleans can be mangled to !1 in + production builds. -->