2026-01-05 07:46:21 -05:00
|
|
|
// Copyright The Prometheus Authors
|
2021-06-28 11:00:55 -04:00
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
|
//
|
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
//
|
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
|
|
package histogram
|
|
|
|
|
|
2021-07-03 13:34:34 -04:00
|
|
|
import (
|
Add histogram validation in remote-read and during reducing resolution (#17561)
ReduceResolution is currently called before validation during
ingestion. This will cause a panic if there are not enough buckets in
the histogram. If there are too many buckets, the spurious buckets are
ignored, and therefore the error in the input histogram is masked.
Furthermore, invalid negative offsets might cause problems, too.
Therefore, we need to do some minimal validation in reduceResolution.
Fortunately, it is easy and shouldn't slow things down. Sadly, it
requires to return errors, which triggers a bunch of code changes.
Even here is a bright side, we can get rud of a few panics. (Remember:
Don't panic!)
In different news, we haven't done a full validation of histograms
read via remote-read. This is not so much a security concern (as you
can throw off Prometheus easily by feeding it bogus data via
remote-read) but more that remote-read sources might be makeshift and
could accidentally create invalid histograms. We really don't want to
panic in that case. So this commit does not only add a check of the
spans and buckets as needed for resolution reduction but also a full
validation during remote-read.
Signed-off-by: beorn7 <beorn@grafana.com>
2025-11-20 18:22:24 -05:00
|
|
|
"errors"
|
2021-11-09 15:45:04 -05:00
|
|
|
"fmt"
|
2021-07-03 13:34:34 -04:00
|
|
|
"math"
|
2024-01-15 11:24:46 -05:00
|
|
|
"slices"
|
2021-11-09 15:45:04 -05:00
|
|
|
"strings"
|
2021-07-03 13:34:34 -04:00
|
|
|
)
|
2021-07-03 09:53:56 -04:00
|
|
|
|
2023-01-04 04:58:18 -05:00
|
|
|
// CounterResetHint contains the known information about a counter reset,
|
|
|
|
|
// or alternatively that we are dealing with a gauge histogram, where counter resets do not apply.
|
|
|
|
|
type CounterResetHint byte
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
UnknownCounterReset CounterResetHint = iota // UnknownCounterReset means we cannot say if this histogram signals a counter reset or not.
|
|
|
|
|
CounterReset // CounterReset means there was definitely a counter reset starting from this histogram.
|
|
|
|
|
NotCounterReset // NotCounterReset means there was definitely no counter reset with this histogram.
|
|
|
|
|
GaugeType // GaugeType means this is a gauge histogram, where counter resets do not happen.
|
|
|
|
|
)
|
|
|
|
|
|
Style cleanup of all the changes in sparsehistogram so far
A lot of this code was hacked together, literally during a
hackathon. This commit intends not to change the code substantially,
but just make the code obey the usual style practices.
A (possibly incomplete) list of areas:
* Generally address linter warnings.
* The `pgk` directory is deprecated as per dev-summit. No new packages should
be added to it. I moved the new `pkg/histogram` package to `model`
anticipating what's proposed in #9478.
* Make the naming of the Sparse Histogram more consistent. Including
abbreviations, there were just too many names for it: SparseHistogram,
Histogram, Histo, hist, his, shs, h. The idea is to call it "Histogram" in
general. Only add "Sparse" if it is needed to avoid confusion with
conventional Histograms (which is rare because the TSDB really has no notion
of conventional Histograms). Use abbreviations only in local scope, and then
really abbreviate (not just removing three out of seven letters like in
"Histo"). This is in the spirit of
https://github.com/golang/go/wiki/CodeReviewComments#variable-names
* Several other minor name changes.
* A lot of formatting of doc comments. For one, following
https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences
, but also layout question, anticipating how things will look like
when rendered by `godoc` (even where `godoc` doesn't render them
right now because they are for unexported types or not a doc comment
at all but just a normal code comment - consistency is queen!).
* Re-enabled `TestQueryLog` and `TestEndopints` (they pass now,
leaving them disabled was presumably an oversight).
* Bucket iterator for histogram.Histogram is now created with a
method.
* HistogramChunk.iterator now allows iterator recycling. (I think
@dieterbe only commented it out because he was confused by the
question in the comment.)
* HistogramAppender.Append panics now because we decided to treat
staleness marker differently.
Signed-off-by: beorn7 <beorn@grafana.com>
2021-10-09 09:57:07 -04:00
|
|
|
// Histogram encodes a sparse, high-resolution histogram. See the design
|
|
|
|
|
// document for full details:
|
|
|
|
|
// https://docs.google.com/document/d/1cLNv3aufPZb3fNfaJgdaRBZsInZKKIHo9E6HinJVbpM/edit#
|
2021-07-02 11:37:40 -04:00
|
|
|
//
|
Style cleanup of all the changes in sparsehistogram so far
A lot of this code was hacked together, literally during a
hackathon. This commit intends not to change the code substantially,
but just make the code obey the usual style practices.
A (possibly incomplete) list of areas:
* Generally address linter warnings.
* The `pgk` directory is deprecated as per dev-summit. No new packages should
be added to it. I moved the new `pkg/histogram` package to `model`
anticipating what's proposed in #9478.
* Make the naming of the Sparse Histogram more consistent. Including
abbreviations, there were just too many names for it: SparseHistogram,
Histogram, Histo, hist, his, shs, h. The idea is to call it "Histogram" in
general. Only add "Sparse" if it is needed to avoid confusion with
conventional Histograms (which is rare because the TSDB really has no notion
of conventional Histograms). Use abbreviations only in local scope, and then
really abbreviate (not just removing three out of seven letters like in
"Histo"). This is in the spirit of
https://github.com/golang/go/wiki/CodeReviewComments#variable-names
* Several other minor name changes.
* A lot of formatting of doc comments. For one, following
https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences
, but also layout question, anticipating how things will look like
when rendered by `godoc` (even where `godoc` doesn't render them
right now because they are for unexported types or not a doc comment
at all but just a normal code comment - consistency is queen!).
* Re-enabled `TestQueryLog` and `TestEndopints` (they pass now,
leaving them disabled was presumably an oversight).
* Bucket iterator for histogram.Histogram is now created with a
method.
* HistogramChunk.iterator now allows iterator recycling. (I think
@dieterbe only commented it out because he was confused by the
question in the comment.)
* HistogramAppender.Append panics now because we decided to treat
staleness marker differently.
Signed-off-by: beorn7 <beorn@grafana.com>
2021-10-09 09:57:07 -04:00
|
|
|
// The most tricky bit is how bucket indices represent real bucket boundaries.
|
|
|
|
|
// An example for schema 0 (by which each bucket is twice as wide as the
|
|
|
|
|
// previous bucket):
|
2021-07-02 11:37:40 -04:00
|
|
|
//
|
histograms: Add Compact method to the normal integer Histogram
And use the new method to call to compact Histograms during
parsing. This happens for both `Histogram` and `FloatHistogram`. In
this way, if targets decide to optimize the exposition size by merging
spans with empty buckets in between, we still get a normalized
results. It will also normalize away any valid but weird
representations like empty spans, spans with offset zero, and empty
buckets at the start or end of a span.
The implementation seemed easy at first as it just turns the
`compactBuckets` helper into a generic function (which now got its own
file). However, the integer Histograms have delta buckets instead of
absolute buckets, which had to be treated specially in the generic
`compactBuckets` function. To make sure it works, I have added plenty
of explicit tests for `Histogram` in addition to the `FloatHistogram`
tests.
I have also updated the doc comment for the `Compact` method.
Based on the insights now expressed in the doc comment, compacting
with a maxEmptyBuckets > 0 is rarely useful. Therefore, this commit
also sets the value to 0 in the two cases we were using 3 so far. We
might still want to reconsider, so I don't want to remove the
maxEmptyBuckets parameter right now.
Signed-off-by: beorn7 <beorn@grafana.com>
2022-09-27 07:04:16 -04:00
|
|
|
// Bucket boundaries → [-2,-1) [-1,-0.5) [-0.5,-0.25) ... [-0.001,0.001] ... (0.25,0.5] (0.5,1] (1,2] ....
|
|
|
|
|
// ↑ ↑ ↑ ↑ ↑ ↑ ↑
|
|
|
|
|
// Zero bucket (width e.g. 0.001) → | | | ZB | | |
|
|
|
|
|
// Positive bucket indices → | | | ... -1 0 1 2 3
|
|
|
|
|
// Negative bucket indices → 3 2 1 0 -1 ...
|
Style cleanup of all the changes in sparsehistogram so far
A lot of this code was hacked together, literally during a
hackathon. This commit intends not to change the code substantially,
but just make the code obey the usual style practices.
A (possibly incomplete) list of areas:
* Generally address linter warnings.
* The `pgk` directory is deprecated as per dev-summit. No new packages should
be added to it. I moved the new `pkg/histogram` package to `model`
anticipating what's proposed in #9478.
* Make the naming of the Sparse Histogram more consistent. Including
abbreviations, there were just too many names for it: SparseHistogram,
Histogram, Histo, hist, his, shs, h. The idea is to call it "Histogram" in
general. Only add "Sparse" if it is needed to avoid confusion with
conventional Histograms (which is rare because the TSDB really has no notion
of conventional Histograms). Use abbreviations only in local scope, and then
really abbreviate (not just removing three out of seven letters like in
"Histo"). This is in the spirit of
https://github.com/golang/go/wiki/CodeReviewComments#variable-names
* Several other minor name changes.
* A lot of formatting of doc comments. For one, following
https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences
, but also layout question, anticipating how things will look like
when rendered by `godoc` (even where `godoc` doesn't render them
right now because they are for unexported types or not a doc comment
at all but just a normal code comment - consistency is queen!).
* Re-enabled `TestQueryLog` and `TestEndopints` (they pass now,
leaving them disabled was presumably an oversight).
* Bucket iterator for histogram.Histogram is now created with a
method.
* HistogramChunk.iterator now allows iterator recycling. (I think
@dieterbe only commented it out because he was confused by the
question in the comment.)
* HistogramAppender.Append panics now because we decided to treat
staleness marker differently.
Signed-off-by: beorn7 <beorn@grafana.com>
2021-10-09 09:57:07 -04:00
|
|
|
//
|
2022-03-22 11:02:13 -04:00
|
|
|
// Which bucket indices are actually used is determined by the spans.
|
Style cleanup of all the changes in sparsehistogram so far
A lot of this code was hacked together, literally during a
hackathon. This commit intends not to change the code substantially,
but just make the code obey the usual style practices.
A (possibly incomplete) list of areas:
* Generally address linter warnings.
* The `pgk` directory is deprecated as per dev-summit. No new packages should
be added to it. I moved the new `pkg/histogram` package to `model`
anticipating what's proposed in #9478.
* Make the naming of the Sparse Histogram more consistent. Including
abbreviations, there were just too many names for it: SparseHistogram,
Histogram, Histo, hist, his, shs, h. The idea is to call it "Histogram" in
general. Only add "Sparse" if it is needed to avoid confusion with
conventional Histograms (which is rare because the TSDB really has no notion
of conventional Histograms). Use abbreviations only in local scope, and then
really abbreviate (not just removing three out of seven letters like in
"Histo"). This is in the spirit of
https://github.com/golang/go/wiki/CodeReviewComments#variable-names
* Several other minor name changes.
* A lot of formatting of doc comments. For one, following
https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences
, but also layout question, anticipating how things will look like
when rendered by `godoc` (even where `godoc` doesn't render them
right now because they are for unexported types or not a doc comment
at all but just a normal code comment - consistency is queen!).
* Re-enabled `TestQueryLog` and `TestEndopints` (they pass now,
leaving them disabled was presumably an oversight).
* Bucket iterator for histogram.Histogram is now created with a
method.
* HistogramChunk.iterator now allows iterator recycling. (I think
@dieterbe only commented it out because he was confused by the
question in the comment.)
* HistogramAppender.Append panics now because we decided to treat
staleness marker differently.
Signed-off-by: beorn7 <beorn@grafana.com>
2021-10-09 09:57:07 -04:00
|
|
|
type Histogram struct {
|
2023-01-04 04:58:18 -05:00
|
|
|
// Counter reset information.
|
|
|
|
|
CounterResetHint CounterResetHint
|
2024-02-28 08:06:43 -05:00
|
|
|
// Currently valid schema numbers are -4 <= n <= 8 for exponential buckets,
|
|
|
|
|
// They are all for base-2 bucket schemas, where 1 is a bucket boundary in
|
|
|
|
|
// each case, and then each power of two is divided into 2^n logarithmic buckets.
|
|
|
|
|
// Or in other words, each bucket boundary is the previous boundary times
|
2024-03-22 09:36:39 -04:00
|
|
|
// 2^(2^-n). Another valid schema number is -53 for custom buckets, defined by
|
|
|
|
|
// the CustomValues field.
|
Style cleanup of all the changes in sparsehistogram so far
A lot of this code was hacked together, literally during a
hackathon. This commit intends not to change the code substantially,
but just make the code obey the usual style practices.
A (possibly incomplete) list of areas:
* Generally address linter warnings.
* The `pgk` directory is deprecated as per dev-summit. No new packages should
be added to it. I moved the new `pkg/histogram` package to `model`
anticipating what's proposed in #9478.
* Make the naming of the Sparse Histogram more consistent. Including
abbreviations, there were just too many names for it: SparseHistogram,
Histogram, Histo, hist, his, shs, h. The idea is to call it "Histogram" in
general. Only add "Sparse" if it is needed to avoid confusion with
conventional Histograms (which is rare because the TSDB really has no notion
of conventional Histograms). Use abbreviations only in local scope, and then
really abbreviate (not just removing three out of seven letters like in
"Histo"). This is in the spirit of
https://github.com/golang/go/wiki/CodeReviewComments#variable-names
* Several other minor name changes.
* A lot of formatting of doc comments. For one, following
https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences
, but also layout question, anticipating how things will look like
when rendered by `godoc` (even where `godoc` doesn't render them
right now because they are for unexported types or not a doc comment
at all but just a normal code comment - consistency is queen!).
* Re-enabled `TestQueryLog` and `TestEndopints` (they pass now,
leaving them disabled was presumably an oversight).
* Bucket iterator for histogram.Histogram is now created with a
method.
* HistogramChunk.iterator now allows iterator recycling. (I think
@dieterbe only commented it out because he was confused by the
question in the comment.)
* HistogramAppender.Append panics now because we decided to treat
staleness marker differently.
Signed-off-by: beorn7 <beorn@grafana.com>
2021-10-09 09:57:07 -04:00
|
|
|
Schema int32
|
|
|
|
|
// Width of the zero bucket.
|
|
|
|
|
ZeroThreshold float64
|
|
|
|
|
// Observations falling into the zero bucket.
|
|
|
|
|
ZeroCount uint64
|
|
|
|
|
// Total number of observations.
|
|
|
|
|
Count uint64
|
2021-11-15 15:49:25 -05:00
|
|
|
// Sum of observations. This is also used as the stale marker.
|
Style cleanup of all the changes in sparsehistogram so far
A lot of this code was hacked together, literally during a
hackathon. This commit intends not to change the code substantially,
but just make the code obey the usual style practices.
A (possibly incomplete) list of areas:
* Generally address linter warnings.
* The `pgk` directory is deprecated as per dev-summit. No new packages should
be added to it. I moved the new `pkg/histogram` package to `model`
anticipating what's proposed in #9478.
* Make the naming of the Sparse Histogram more consistent. Including
abbreviations, there were just too many names for it: SparseHistogram,
Histogram, Histo, hist, his, shs, h. The idea is to call it "Histogram" in
general. Only add "Sparse" if it is needed to avoid confusion with
conventional Histograms (which is rare because the TSDB really has no notion
of conventional Histograms). Use abbreviations only in local scope, and then
really abbreviate (not just removing three out of seven letters like in
"Histo"). This is in the spirit of
https://github.com/golang/go/wiki/CodeReviewComments#variable-names
* Several other minor name changes.
* A lot of formatting of doc comments. For one, following
https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences
, but also layout question, anticipating how things will look like
when rendered by `godoc` (even where `godoc` doesn't render them
right now because they are for unexported types or not a doc comment
at all but just a normal code comment - consistency is queen!).
* Re-enabled `TestQueryLog` and `TestEndopints` (they pass now,
leaving them disabled was presumably an oversight).
* Bucket iterator for histogram.Histogram is now created with a
method.
* HistogramChunk.iterator now allows iterator recycling. (I think
@dieterbe only commented it out because he was confused by the
question in the comment.)
* HistogramAppender.Append panics now because we decided to treat
staleness marker differently.
Signed-off-by: beorn7 <beorn@grafana.com>
2021-10-09 09:57:07 -04:00
|
|
|
Sum float64
|
|
|
|
|
// Spans for positive and negative buckets (see Span below).
|
|
|
|
|
PositiveSpans, NegativeSpans []Span
|
|
|
|
|
// Observation counts in buckets. The first element is an absolute
|
|
|
|
|
// count. All following ones are deltas relative to the previous
|
|
|
|
|
// element.
|
2021-06-28 11:00:55 -04:00
|
|
|
PositiveBuckets, NegativeBuckets []int64
|
2024-02-28 08:06:43 -05:00
|
|
|
// Holds the custom (usually upper) bounds for bucket definitions, otherwise nil.
|
|
|
|
|
// This slice is interned, to be treated as immutable and copied by reference.
|
|
|
|
|
// These numbers should be strictly increasing. This field is only used when the
|
2024-03-22 09:36:39 -04:00
|
|
|
// schema is for custom buckets, and the ZeroThreshold, ZeroCount, NegativeSpans
|
2024-06-07 06:50:59 -04:00
|
|
|
// and NegativeBuckets fields are not used in that case.
|
2024-03-22 09:36:39 -04:00
|
|
|
CustomValues []float64
|
2021-06-28 11:00:55 -04:00
|
|
|
}
|
|
|
|
|
|
Style cleanup of all the changes in sparsehistogram so far
A lot of this code was hacked together, literally during a
hackathon. This commit intends not to change the code substantially,
but just make the code obey the usual style practices.
A (possibly incomplete) list of areas:
* Generally address linter warnings.
* The `pgk` directory is deprecated as per dev-summit. No new packages should
be added to it. I moved the new `pkg/histogram` package to `model`
anticipating what's proposed in #9478.
* Make the naming of the Sparse Histogram more consistent. Including
abbreviations, there were just too many names for it: SparseHistogram,
Histogram, Histo, hist, his, shs, h. The idea is to call it "Histogram" in
general. Only add "Sparse" if it is needed to avoid confusion with
conventional Histograms (which is rare because the TSDB really has no notion
of conventional Histograms). Use abbreviations only in local scope, and then
really abbreviate (not just removing three out of seven letters like in
"Histo"). This is in the spirit of
https://github.com/golang/go/wiki/CodeReviewComments#variable-names
* Several other minor name changes.
* A lot of formatting of doc comments. For one, following
https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences
, but also layout question, anticipating how things will look like
when rendered by `godoc` (even where `godoc` doesn't render them
right now because they are for unexported types or not a doc comment
at all but just a normal code comment - consistency is queen!).
* Re-enabled `TestQueryLog` and `TestEndopints` (they pass now,
leaving them disabled was presumably an oversight).
* Bucket iterator for histogram.Histogram is now created with a
method.
* HistogramChunk.iterator now allows iterator recycling. (I think
@dieterbe only commented it out because he was confused by the
question in the comment.)
* HistogramAppender.Append panics now because we decided to treat
staleness marker differently.
Signed-off-by: beorn7 <beorn@grafana.com>
2021-10-09 09:57:07 -04:00
|
|
|
// A Span defines a continuous sequence of buckets.
|
2021-06-28 11:00:55 -04:00
|
|
|
type Span struct {
|
Style cleanup of all the changes in sparsehistogram so far
A lot of this code was hacked together, literally during a
hackathon. This commit intends not to change the code substantially,
but just make the code obey the usual style practices.
A (possibly incomplete) list of areas:
* Generally address linter warnings.
* The `pgk` directory is deprecated as per dev-summit. No new packages should
be added to it. I moved the new `pkg/histogram` package to `model`
anticipating what's proposed in #9478.
* Make the naming of the Sparse Histogram more consistent. Including
abbreviations, there were just too many names for it: SparseHistogram,
Histogram, Histo, hist, his, shs, h. The idea is to call it "Histogram" in
general. Only add "Sparse" if it is needed to avoid confusion with
conventional Histograms (which is rare because the TSDB really has no notion
of conventional Histograms). Use abbreviations only in local scope, and then
really abbreviate (not just removing three out of seven letters like in
"Histo"). This is in the spirit of
https://github.com/golang/go/wiki/CodeReviewComments#variable-names
* Several other minor name changes.
* A lot of formatting of doc comments. For one, following
https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences
, but also layout question, anticipating how things will look like
when rendered by `godoc` (even where `godoc` doesn't render them
right now because they are for unexported types or not a doc comment
at all but just a normal code comment - consistency is queen!).
* Re-enabled `TestQueryLog` and `TestEndopints` (they pass now,
leaving them disabled was presumably an oversight).
* Bucket iterator for histogram.Histogram is now created with a
method.
* HistogramChunk.iterator now allows iterator recycling. (I think
@dieterbe only commented it out because he was confused by the
question in the comment.)
* HistogramAppender.Append panics now because we decided to treat
staleness marker differently.
Signed-off-by: beorn7 <beorn@grafana.com>
2021-10-09 09:57:07 -04:00
|
|
|
// Gap to previous span (always positive), or starting index for the 1st
|
|
|
|
|
// span (which can be negative).
|
2021-06-28 11:00:55 -04:00
|
|
|
Offset int32
|
Style cleanup of all the changes in sparsehistogram so far
A lot of this code was hacked together, literally during a
hackathon. This commit intends not to change the code substantially,
but just make the code obey the usual style practices.
A (possibly incomplete) list of areas:
* Generally address linter warnings.
* The `pgk` directory is deprecated as per dev-summit. No new packages should
be added to it. I moved the new `pkg/histogram` package to `model`
anticipating what's proposed in #9478.
* Make the naming of the Sparse Histogram more consistent. Including
abbreviations, there were just too many names for it: SparseHistogram,
Histogram, Histo, hist, his, shs, h. The idea is to call it "Histogram" in
general. Only add "Sparse" if it is needed to avoid confusion with
conventional Histograms (which is rare because the TSDB really has no notion
of conventional Histograms). Use abbreviations only in local scope, and then
really abbreviate (not just removing three out of seven letters like in
"Histo"). This is in the spirit of
https://github.com/golang/go/wiki/CodeReviewComments#variable-names
* Several other minor name changes.
* A lot of formatting of doc comments. For one, following
https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences
, but also layout question, anticipating how things will look like
when rendered by `godoc` (even where `godoc` doesn't render them
right now because they are for unexported types or not a doc comment
at all but just a normal code comment - consistency is queen!).
* Re-enabled `TestQueryLog` and `TestEndopints` (they pass now,
leaving them disabled was presumably an oversight).
* Bucket iterator for histogram.Histogram is now created with a
method.
* HistogramChunk.iterator now allows iterator recycling. (I think
@dieterbe only commented it out because he was confused by the
question in the comment.)
* HistogramAppender.Append panics now because we decided to treat
staleness marker differently.
Signed-off-by: beorn7 <beorn@grafana.com>
2021-10-09 09:57:07 -04:00
|
|
|
// Length of the span.
|
2021-06-28 11:00:55 -04:00
|
|
|
Length uint32
|
|
|
|
|
}
|
2021-06-30 06:45:43 -04:00
|
|
|
|
2024-02-28 08:06:43 -05:00
|
|
|
func (h *Histogram) UsesCustomBuckets() bool {
|
|
|
|
|
return IsCustomBucketsSchema(h.Schema)
|
|
|
|
|
}
|
|
|
|
|
|
Style cleanup of all the changes in sparsehistogram so far
A lot of this code was hacked together, literally during a
hackathon. This commit intends not to change the code substantially,
but just make the code obey the usual style practices.
A (possibly incomplete) list of areas:
* Generally address linter warnings.
* The `pgk` directory is deprecated as per dev-summit. No new packages should
be added to it. I moved the new `pkg/histogram` package to `model`
anticipating what's proposed in #9478.
* Make the naming of the Sparse Histogram more consistent. Including
abbreviations, there were just too many names for it: SparseHistogram,
Histogram, Histo, hist, his, shs, h. The idea is to call it "Histogram" in
general. Only add "Sparse" if it is needed to avoid confusion with
conventional Histograms (which is rare because the TSDB really has no notion
of conventional Histograms). Use abbreviations only in local scope, and then
really abbreviate (not just removing three out of seven letters like in
"Histo"). This is in the spirit of
https://github.com/golang/go/wiki/CodeReviewComments#variable-names
* Several other minor name changes.
* A lot of formatting of doc comments. For one, following
https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences
, but also layout question, anticipating how things will look like
when rendered by `godoc` (even where `godoc` doesn't render them
right now because they are for unexported types or not a doc comment
at all but just a normal code comment - consistency is queen!).
* Re-enabled `TestQueryLog` and `TestEndopints` (they pass now,
leaving them disabled was presumably an oversight).
* Bucket iterator for histogram.Histogram is now created with a
method.
* HistogramChunk.iterator now allows iterator recycling. (I think
@dieterbe only commented it out because he was confused by the
question in the comment.)
* HistogramAppender.Append panics now because we decided to treat
staleness marker differently.
Signed-off-by: beorn7 <beorn@grafana.com>
2021-10-09 09:57:07 -04:00
|
|
|
// Copy returns a deep copy of the Histogram.
|
2021-11-23 13:40:49 -05:00
|
|
|
func (h *Histogram) Copy() *Histogram {
|
2024-01-23 11:02:14 -05:00
|
|
|
c := Histogram{
|
|
|
|
|
CounterResetHint: h.CounterResetHint,
|
|
|
|
|
Schema: h.Schema,
|
|
|
|
|
Count: h.Count,
|
|
|
|
|
Sum: h.Sum,
|
|
|
|
|
}
|
2021-06-30 06:45:43 -04:00
|
|
|
|
2024-02-28 08:06:43 -05:00
|
|
|
if h.UsesCustomBuckets() {
|
2025-05-07 08:29:35 -04:00
|
|
|
// Custom values are interned, it's ok to copy by reference.
|
|
|
|
|
c.CustomValues = h.CustomValues
|
2024-02-28 08:06:43 -05:00
|
|
|
} else {
|
|
|
|
|
c.ZeroThreshold = h.ZeroThreshold
|
|
|
|
|
c.ZeroCount = h.ZeroCount
|
|
|
|
|
|
|
|
|
|
if len(h.NegativeSpans) != 0 {
|
|
|
|
|
c.NegativeSpans = make([]Span, len(h.NegativeSpans))
|
|
|
|
|
copy(c.NegativeSpans, h.NegativeSpans)
|
|
|
|
|
}
|
|
|
|
|
if len(h.NegativeBuckets) != 0 {
|
|
|
|
|
c.NegativeBuckets = make([]int64, len(h.NegativeBuckets))
|
|
|
|
|
copy(c.NegativeBuckets, h.NegativeBuckets)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-29 02:54:23 -05:00
|
|
|
if len(h.PositiveSpans) != 0 {
|
Style cleanup of all the changes in sparsehistogram so far
A lot of this code was hacked together, literally during a
hackathon. This commit intends not to change the code substantially,
but just make the code obey the usual style practices.
A (possibly incomplete) list of areas:
* Generally address linter warnings.
* The `pgk` directory is deprecated as per dev-summit. No new packages should
be added to it. I moved the new `pkg/histogram` package to `model`
anticipating what's proposed in #9478.
* Make the naming of the Sparse Histogram more consistent. Including
abbreviations, there were just too many names for it: SparseHistogram,
Histogram, Histo, hist, his, shs, h. The idea is to call it "Histogram" in
general. Only add "Sparse" if it is needed to avoid confusion with
conventional Histograms (which is rare because the TSDB really has no notion
of conventional Histograms). Use abbreviations only in local scope, and then
really abbreviate (not just removing three out of seven letters like in
"Histo"). This is in the spirit of
https://github.com/golang/go/wiki/CodeReviewComments#variable-names
* Several other minor name changes.
* A lot of formatting of doc comments. For one, following
https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences
, but also layout question, anticipating how things will look like
when rendered by `godoc` (even where `godoc` doesn't render them
right now because they are for unexported types or not a doc comment
at all but just a normal code comment - consistency is queen!).
* Re-enabled `TestQueryLog` and `TestEndopints` (they pass now,
leaving them disabled was presumably an oversight).
* Bucket iterator for histogram.Histogram is now created with a
method.
* HistogramChunk.iterator now allows iterator recycling. (I think
@dieterbe only commented it out because he was confused by the
question in the comment.)
* HistogramAppender.Append panics now because we decided to treat
staleness marker differently.
Signed-off-by: beorn7 <beorn@grafana.com>
2021-10-09 09:57:07 -04:00
|
|
|
c.PositiveSpans = make([]Span, len(h.PositiveSpans))
|
|
|
|
|
copy(c.PositiveSpans, h.PositiveSpans)
|
2021-06-30 06:45:43 -04:00
|
|
|
}
|
2021-11-29 02:54:23 -05:00
|
|
|
if len(h.PositiveBuckets) != 0 {
|
Style cleanup of all the changes in sparsehistogram so far
A lot of this code was hacked together, literally during a
hackathon. This commit intends not to change the code substantially,
but just make the code obey the usual style practices.
A (possibly incomplete) list of areas:
* Generally address linter warnings.
* The `pgk` directory is deprecated as per dev-summit. No new packages should
be added to it. I moved the new `pkg/histogram` package to `model`
anticipating what's proposed in #9478.
* Make the naming of the Sparse Histogram more consistent. Including
abbreviations, there were just too many names for it: SparseHistogram,
Histogram, Histo, hist, his, shs, h. The idea is to call it "Histogram" in
general. Only add "Sparse" if it is needed to avoid confusion with
conventional Histograms (which is rare because the TSDB really has no notion
of conventional Histograms). Use abbreviations only in local scope, and then
really abbreviate (not just removing three out of seven letters like in
"Histo"). This is in the spirit of
https://github.com/golang/go/wiki/CodeReviewComments#variable-names
* Several other minor name changes.
* A lot of formatting of doc comments. For one, following
https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences
, but also layout question, anticipating how things will look like
when rendered by `godoc` (even where `godoc` doesn't render them
right now because they are for unexported types or not a doc comment
at all but just a normal code comment - consistency is queen!).
* Re-enabled `TestQueryLog` and `TestEndopints` (they pass now,
leaving them disabled was presumably an oversight).
* Bucket iterator for histogram.Histogram is now created with a
method.
* HistogramChunk.iterator now allows iterator recycling. (I think
@dieterbe only commented it out because he was confused by the
question in the comment.)
* HistogramAppender.Append panics now because we decided to treat
staleness marker differently.
Signed-off-by: beorn7 <beorn@grafana.com>
2021-10-09 09:57:07 -04:00
|
|
|
c.PositiveBuckets = make([]int64, len(h.PositiveBuckets))
|
|
|
|
|
copy(c.PositiveBuckets, h.PositiveBuckets)
|
2021-06-30 06:45:43 -04:00
|
|
|
}
|
|
|
|
|
|
2021-11-09 15:45:04 -05:00
|
|
|
return &c
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-23 11:02:14 -05:00
|
|
|
// CopyTo makes a deep copy into the given Histogram object.
|
|
|
|
|
// The destination object has to be a non-nil pointer.
|
|
|
|
|
func (h *Histogram) CopyTo(to *Histogram) {
|
|
|
|
|
to.CounterResetHint = h.CounterResetHint
|
|
|
|
|
to.Schema = h.Schema
|
|
|
|
|
to.Count = h.Count
|
|
|
|
|
to.Sum = h.Sum
|
|
|
|
|
|
2024-02-28 08:06:43 -05:00
|
|
|
if h.UsesCustomBuckets() {
|
|
|
|
|
to.ZeroThreshold = 0
|
|
|
|
|
to.ZeroCount = 0
|
|
|
|
|
|
|
|
|
|
to.NegativeSpans = clearIfNotNil(to.NegativeSpans)
|
|
|
|
|
to.NegativeBuckets = clearIfNotNil(to.NegativeBuckets)
|
2025-05-07 08:29:35 -04:00
|
|
|
// Custom values are interned, it's ok to copy by reference.
|
|
|
|
|
to.CustomValues = h.CustomValues
|
2024-02-28 08:06:43 -05:00
|
|
|
} else {
|
|
|
|
|
to.ZeroThreshold = h.ZeroThreshold
|
|
|
|
|
to.ZeroCount = h.ZeroCount
|
|
|
|
|
|
|
|
|
|
to.NegativeSpans = resize(to.NegativeSpans, len(h.NegativeSpans))
|
|
|
|
|
copy(to.NegativeSpans, h.NegativeSpans)
|
|
|
|
|
|
|
|
|
|
to.NegativeBuckets = resize(to.NegativeBuckets, len(h.NegativeBuckets))
|
|
|
|
|
copy(to.NegativeBuckets, h.NegativeBuckets)
|
2025-05-07 08:29:35 -04:00
|
|
|
// Custom values are interned, no need to reset.
|
|
|
|
|
to.CustomValues = nil
|
2024-02-28 08:06:43 -05:00
|
|
|
}
|
|
|
|
|
|
2024-01-23 11:02:14 -05:00
|
|
|
to.PositiveSpans = resize(to.PositiveSpans, len(h.PositiveSpans))
|
|
|
|
|
copy(to.PositiveSpans, h.PositiveSpans)
|
|
|
|
|
|
|
|
|
|
to.PositiveBuckets = resize(to.PositiveBuckets, len(h.PositiveBuckets))
|
|
|
|
|
copy(to.PositiveBuckets, h.PositiveBuckets)
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-09 15:45:04 -05:00
|
|
|
// String returns a string representation of the Histogram.
|
2021-11-23 13:40:49 -05:00
|
|
|
func (h *Histogram) String() string {
|
2021-11-09 15:45:04 -05:00
|
|
|
var sb strings.Builder
|
|
|
|
|
fmt.Fprintf(&sb, "{count:%d, sum:%g", h.Count, h.Sum)
|
|
|
|
|
|
2022-10-03 07:15:27 -04:00
|
|
|
var nBuckets []Bucket[uint64]
|
2021-11-09 15:45:04 -05:00
|
|
|
for it := h.NegativeBucketIterator(); it.Next(); {
|
|
|
|
|
bucket := it.At()
|
|
|
|
|
if bucket.Count != 0 {
|
|
|
|
|
nBuckets = append(nBuckets, it.At())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for i := len(nBuckets) - 1; i >= 0; i-- {
|
|
|
|
|
fmt.Fprintf(&sb, ", %s", nBuckets[i].String())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if h.ZeroCount != 0 {
|
|
|
|
|
fmt.Fprintf(&sb, ", %s", h.ZeroBucket().String())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for it := h.PositiveBucketIterator(); it.Next(); {
|
|
|
|
|
bucket := it.At()
|
|
|
|
|
if bucket.Count != 0 {
|
|
|
|
|
fmt.Fprintf(&sb, ", %s", bucket.String())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sb.WriteRune('}')
|
|
|
|
|
return sb.String()
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-07 06:50:59 -04:00
|
|
|
// ZeroBucket returns the zero bucket. This method panics if the schema is for custom buckets.
|
2022-10-03 07:15:27 -04:00
|
|
|
func (h *Histogram) ZeroBucket() Bucket[uint64] {
|
2024-02-28 08:06:43 -05:00
|
|
|
if h.UsesCustomBuckets() {
|
|
|
|
|
panic("histograms with custom buckets have no zero bucket")
|
|
|
|
|
}
|
2022-10-03 07:15:27 -04:00
|
|
|
return Bucket[uint64]{
|
2021-11-09 15:45:04 -05:00
|
|
|
Lower: -h.ZeroThreshold,
|
|
|
|
|
Upper: h.ZeroThreshold,
|
|
|
|
|
LowerInclusive: true,
|
|
|
|
|
UpperInclusive: true,
|
|
|
|
|
Count: h.ZeroCount,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// PositiveBucketIterator returns a BucketIterator to iterate over all positive
|
|
|
|
|
// buckets in ascending order (starting next to the zero bucket and going up).
|
2022-10-03 07:15:27 -04:00
|
|
|
func (h *Histogram) PositiveBucketIterator() BucketIterator[uint64] {
|
2024-03-22 09:36:39 -04:00
|
|
|
it := newRegularBucketIterator(h.PositiveSpans, h.PositiveBuckets, h.Schema, true, h.CustomValues)
|
2023-10-09 02:40:59 -04:00
|
|
|
return &it
|
2021-11-09 15:45:04 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NegativeBucketIterator returns a BucketIterator to iterate over all negative
|
|
|
|
|
// buckets in descending order (starting next to the zero bucket and going down).
|
2022-10-03 07:15:27 -04:00
|
|
|
func (h *Histogram) NegativeBucketIterator() BucketIterator[uint64] {
|
2024-02-28 08:06:43 -05:00
|
|
|
it := newRegularBucketIterator(h.NegativeSpans, h.NegativeBuckets, h.Schema, false, nil)
|
2023-10-09 02:40:59 -04:00
|
|
|
return &it
|
2021-06-30 06:45:43 -04:00
|
|
|
}
|
2021-07-03 09:53:56 -04:00
|
|
|
|
Style cleanup of all the changes in sparsehistogram so far
A lot of this code was hacked together, literally during a
hackathon. This commit intends not to change the code substantially,
but just make the code obey the usual style practices.
A (possibly incomplete) list of areas:
* Generally address linter warnings.
* The `pgk` directory is deprecated as per dev-summit. No new packages should
be added to it. I moved the new `pkg/histogram` package to `model`
anticipating what's proposed in #9478.
* Make the naming of the Sparse Histogram more consistent. Including
abbreviations, there were just too many names for it: SparseHistogram,
Histogram, Histo, hist, his, shs, h. The idea is to call it "Histogram" in
general. Only add "Sparse" if it is needed to avoid confusion with
conventional Histograms (which is rare because the TSDB really has no notion
of conventional Histograms). Use abbreviations only in local scope, and then
really abbreviate (not just removing three out of seven letters like in
"Histo"). This is in the spirit of
https://github.com/golang/go/wiki/CodeReviewComments#variable-names
* Several other minor name changes.
* A lot of formatting of doc comments. For one, following
https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences
, but also layout question, anticipating how things will look like
when rendered by `godoc` (even where `godoc` doesn't render them
right now because they are for unexported types or not a doc comment
at all but just a normal code comment - consistency is queen!).
* Re-enabled `TestQueryLog` and `TestEndopints` (they pass now,
leaving them disabled was presumably an oversight).
* Bucket iterator for histogram.Histogram is now created with a
method.
* HistogramChunk.iterator now allows iterator recycling. (I think
@dieterbe only commented it out because he was confused by the
question in the comment.)
* HistogramAppender.Append panics now because we decided to treat
staleness marker differently.
Signed-off-by: beorn7 <beorn@grafana.com>
2021-10-09 09:57:07 -04:00
|
|
|
// CumulativeBucketIterator returns a BucketIterator to iterate over a
|
|
|
|
|
// cumulative view of the buckets. This method currently only supports
|
|
|
|
|
// Histograms without negative buckets and panics if the Histogram has negative
|
|
|
|
|
// buckets. It is currently only used for testing.
|
2022-10-03 07:15:27 -04:00
|
|
|
func (h *Histogram) CumulativeBucketIterator() BucketIterator[uint64] {
|
Style cleanup of all the changes in sparsehistogram so far
A lot of this code was hacked together, literally during a
hackathon. This commit intends not to change the code substantially,
but just make the code obey the usual style practices.
A (possibly incomplete) list of areas:
* Generally address linter warnings.
* The `pgk` directory is deprecated as per dev-summit. No new packages should
be added to it. I moved the new `pkg/histogram` package to `model`
anticipating what's proposed in #9478.
* Make the naming of the Sparse Histogram more consistent. Including
abbreviations, there were just too many names for it: SparseHistogram,
Histogram, Histo, hist, his, shs, h. The idea is to call it "Histogram" in
general. Only add "Sparse" if it is needed to avoid confusion with
conventional Histograms (which is rare because the TSDB really has no notion
of conventional Histograms). Use abbreviations only in local scope, and then
really abbreviate (not just removing three out of seven letters like in
"Histo"). This is in the spirit of
https://github.com/golang/go/wiki/CodeReviewComments#variable-names
* Several other minor name changes.
* A lot of formatting of doc comments. For one, following
https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences
, but also layout question, anticipating how things will look like
when rendered by `godoc` (even where `godoc` doesn't render them
right now because they are for unexported types or not a doc comment
at all but just a normal code comment - consistency is queen!).
* Re-enabled `TestQueryLog` and `TestEndopints` (they pass now,
leaving them disabled was presumably an oversight).
* Bucket iterator for histogram.Histogram is now created with a
method.
* HistogramChunk.iterator now allows iterator recycling. (I think
@dieterbe only commented it out because he was confused by the
question in the comment.)
* HistogramAppender.Append panics now because we decided to treat
staleness marker differently.
Signed-off-by: beorn7 <beorn@grafana.com>
2021-10-09 09:57:07 -04:00
|
|
|
if len(h.NegativeBuckets) > 0 {
|
2021-11-23 13:40:49 -05:00
|
|
|
panic("CumulativeBucketIterator called on Histogram with negative buckets")
|
|
|
|
|
}
|
|
|
|
|
return &cumulativeBucketIterator{h: h, posSpansIdx: -1}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-19 03:40:30 -04:00
|
|
|
// Equals returns true if the given histogram matches exactly.
|
|
|
|
|
// Exact match is when there are no new buckets (even empty) and no missing buckets,
|
|
|
|
|
// and all the bucket values match. Spans can have different empty length spans in between,
|
|
|
|
|
// but they must represent the same bucket layout to match.
|
2023-10-09 09:09:46 -04:00
|
|
|
// Sum is compared based on its bit pattern because this method
|
|
|
|
|
// is about data equality rather than mathematical equality.
|
2024-02-28 08:06:43 -05:00
|
|
|
// We ignore fields that are not used based on the exponential / custom buckets schema,
|
|
|
|
|
// but check fields where differences may cause unintended behaviour even if they are not
|
|
|
|
|
// supposed to be used according to the schema.
|
2022-09-19 03:40:30 -04:00
|
|
|
func (h *Histogram) Equals(h2 *Histogram) bool {
|
|
|
|
|
if h2 == nil {
|
2025-12-22 04:38:48 -05:00
|
|
|
return h == nil
|
2022-09-19 03:40:30 -04:00
|
|
|
}
|
|
|
|
|
|
2024-02-28 08:06:43 -05:00
|
|
|
if h.Schema != h2.Schema || h.Count != h2.Count ||
|
2023-09-25 09:03:55 -04:00
|
|
|
math.Float64bits(h.Sum) != math.Float64bits(h2.Sum) {
|
2022-09-19 03:40:30 -04:00
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-28 08:06:43 -05:00
|
|
|
if h.UsesCustomBuckets() {
|
2025-10-05 15:38:07 -04:00
|
|
|
if !CustomBucketBoundsMatch(h.CustomValues, h2.CustomValues) {
|
2024-02-28 08:06:43 -05:00
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if h.ZeroThreshold != h2.ZeroThreshold || h.ZeroCount != h2.ZeroCount {
|
2022-09-19 03:40:30 -04:00
|
|
|
return false
|
|
|
|
|
}
|
2024-02-28 08:06:43 -05:00
|
|
|
|
2022-09-19 03:40:30 -04:00
|
|
|
if !spansMatch(h.NegativeSpans, h2.NegativeSpans) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
2024-02-28 08:06:43 -05:00
|
|
|
if !slices.Equal(h.NegativeBuckets, h2.NegativeBuckets) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
2022-09-19 03:40:30 -04:00
|
|
|
|
2024-02-28 08:06:43 -05:00
|
|
|
if !spansMatch(h.PositiveSpans, h2.PositiveSpans) {
|
2022-09-19 03:40:30 -04:00
|
|
|
return false
|
|
|
|
|
}
|
2024-02-28 08:06:43 -05:00
|
|
|
if !slices.Equal(h.PositiveBuckets, h2.PositiveBuckets) {
|
2022-09-19 03:40:30 -04:00
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// spansMatch returns true if both spans represent the same bucket layout
|
|
|
|
|
// after combining zero length spans with the next non-zero length span.
|
|
|
|
|
func spansMatch(s1, s2 []Span) bool {
|
|
|
|
|
if len(s1) == 0 && len(s2) == 0 {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s1idx, s2idx := 0, 0
|
|
|
|
|
for {
|
|
|
|
|
if s1idx >= len(s1) {
|
|
|
|
|
return allEmptySpans(s2[s2idx:])
|
|
|
|
|
}
|
|
|
|
|
if s2idx >= len(s2) {
|
|
|
|
|
return allEmptySpans(s1[s1idx:])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
currS1, currS2 := s1[s1idx], s2[s2idx]
|
|
|
|
|
s1idx++
|
|
|
|
|
s2idx++
|
|
|
|
|
if currS1.Length == 0 {
|
|
|
|
|
// This span is zero length, so we add consecutive such spans
|
|
|
|
|
// until we find a non-zero span.
|
|
|
|
|
for ; s1idx < len(s1) && s1[s1idx].Length == 0; s1idx++ {
|
|
|
|
|
currS1.Offset += s1[s1idx].Offset
|
|
|
|
|
}
|
|
|
|
|
if s1idx < len(s1) {
|
|
|
|
|
currS1.Offset += s1[s1idx].Offset
|
|
|
|
|
currS1.Length = s1[s1idx].Length
|
|
|
|
|
s1idx++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if currS2.Length == 0 {
|
|
|
|
|
// This span is zero length, so we add consecutive such spans
|
|
|
|
|
// until we find a non-zero span.
|
|
|
|
|
for ; s2idx < len(s2) && s2[s2idx].Length == 0; s2idx++ {
|
|
|
|
|
currS2.Offset += s2[s2idx].Offset
|
|
|
|
|
}
|
|
|
|
|
if s2idx < len(s2) {
|
|
|
|
|
currS2.Offset += s2[s2idx].Offset
|
|
|
|
|
currS2.Length = s2[s2idx].Length
|
|
|
|
|
s2idx++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if currS1.Length == 0 && currS2.Length == 0 {
|
|
|
|
|
// The last spans of both set are zero length. Previous spans match.
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if currS1.Offset != currS2.Offset || currS1.Length != currS2.Length {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func allEmptySpans(s []Span) bool {
|
|
|
|
|
for _, ss := range s {
|
|
|
|
|
if ss.Length > 0 {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
histograms: Add Compact method to the normal integer Histogram
And use the new method to call to compact Histograms during
parsing. This happens for both `Histogram` and `FloatHistogram`. In
this way, if targets decide to optimize the exposition size by merging
spans with empty buckets in between, we still get a normalized
results. It will also normalize away any valid but weird
representations like empty spans, spans with offset zero, and empty
buckets at the start or end of a span.
The implementation seemed easy at first as it just turns the
`compactBuckets` helper into a generic function (which now got its own
file). However, the integer Histograms have delta buckets instead of
absolute buckets, which had to be treated specially in the generic
`compactBuckets` function. To make sure it works, I have added plenty
of explicit tests for `Histogram` in addition to the `FloatHistogram`
tests.
I have also updated the doc comment for the `Compact` method.
Based on the insights now expressed in the doc comment, compacting
with a maxEmptyBuckets > 0 is rarely useful. Therefore, this commit
also sets the value to 0 in the two cases we were using 3 so far. We
might still want to reconsider, so I don't want to remove the
maxEmptyBuckets parameter right now.
Signed-off-by: beorn7 <beorn@grafana.com>
2022-09-27 07:04:16 -04:00
|
|
|
// Compact works like FloatHistogram.Compact. See there for detailed
|
|
|
|
|
// explanations.
|
|
|
|
|
func (h *Histogram) Compact(maxEmptyBuckets int) *Histogram {
|
|
|
|
|
h.PositiveBuckets, h.PositiveSpans = compactBuckets(
|
|
|
|
|
h.PositiveBuckets, h.PositiveSpans, maxEmptyBuckets, true,
|
|
|
|
|
)
|
|
|
|
|
h.NegativeBuckets, h.NegativeSpans = compactBuckets(
|
|
|
|
|
h.NegativeBuckets, h.NegativeSpans, maxEmptyBuckets, true,
|
|
|
|
|
)
|
|
|
|
|
return h
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-29 09:15:57 -05:00
|
|
|
// ToFloat returns a FloatHistogram representation of the Histogram. It is a deep
|
|
|
|
|
// copy (e.g. spans are not shared). The function accepts a FloatHistogram as an
|
|
|
|
|
// argument whose memory will be reused and overwritten if provided. If this
|
|
|
|
|
// argument is nil, a new FloatHistogram will be allocated.
|
|
|
|
|
func (h *Histogram) ToFloat(fh *FloatHistogram) *FloatHistogram {
|
|
|
|
|
if fh == nil {
|
|
|
|
|
fh = &FloatHistogram{}
|
2021-11-23 13:40:49 -05:00
|
|
|
}
|
2023-11-29 09:15:57 -05:00
|
|
|
fh.CounterResetHint = h.CounterResetHint
|
|
|
|
|
fh.Schema = h.Schema
|
|
|
|
|
fh.Count = float64(h.Count)
|
|
|
|
|
fh.Sum = h.Sum
|
|
|
|
|
|
2024-02-28 08:06:43 -05:00
|
|
|
if h.UsesCustomBuckets() {
|
|
|
|
|
fh.ZeroThreshold = 0
|
|
|
|
|
fh.ZeroCount = 0
|
|
|
|
|
fh.NegativeSpans = clearIfNotNil(fh.NegativeSpans)
|
|
|
|
|
fh.NegativeBuckets = clearIfNotNil(fh.NegativeBuckets)
|
2025-05-07 08:29:35 -04:00
|
|
|
// Custom values are interned, it's ok to copy by reference.
|
|
|
|
|
fh.CustomValues = h.CustomValues
|
2024-02-28 08:06:43 -05:00
|
|
|
} else {
|
|
|
|
|
fh.ZeroThreshold = h.ZeroThreshold
|
|
|
|
|
fh.ZeroCount = float64(h.ZeroCount)
|
|
|
|
|
|
|
|
|
|
fh.NegativeSpans = resize(fh.NegativeSpans, len(h.NegativeSpans))
|
|
|
|
|
copy(fh.NegativeSpans, h.NegativeSpans)
|
|
|
|
|
|
|
|
|
|
fh.NegativeBuckets = resize(fh.NegativeBuckets, len(h.NegativeBuckets))
|
|
|
|
|
var currentNegative float64
|
|
|
|
|
for i, b := range h.NegativeBuckets {
|
|
|
|
|
currentNegative += float64(b)
|
|
|
|
|
fh.NegativeBuckets[i] = currentNegative
|
|
|
|
|
}
|
2025-05-07 08:29:35 -04:00
|
|
|
// Custom values are interned, no need to reset.
|
|
|
|
|
fh.CustomValues = nil
|
2024-02-28 08:06:43 -05:00
|
|
|
}
|
|
|
|
|
|
2023-11-29 09:15:57 -05:00
|
|
|
fh.PositiveSpans = resize(fh.PositiveSpans, len(h.PositiveSpans))
|
|
|
|
|
copy(fh.PositiveSpans, h.PositiveSpans)
|
|
|
|
|
|
|
|
|
|
fh.PositiveBuckets = resize(fh.PositiveBuckets, len(h.PositiveBuckets))
|
|
|
|
|
var currentPositive float64
|
|
|
|
|
for i, b := range h.PositiveBuckets {
|
|
|
|
|
currentPositive += float64(b)
|
|
|
|
|
fh.PositiveBuckets[i] = currentPositive
|
2021-11-23 13:40:49 -05:00
|
|
|
}
|
2023-11-29 09:15:57 -05:00
|
|
|
|
|
|
|
|
return fh
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func resize[T any](items []T, n int) []T {
|
2023-12-08 04:12:50 -05:00
|
|
|
if cap(items) < n {
|
2023-11-29 09:15:57 -05:00
|
|
|
return make([]T, n)
|
Style cleanup of all the changes in sparsehistogram so far
A lot of this code was hacked together, literally during a
hackathon. This commit intends not to change the code substantially,
but just make the code obey the usual style practices.
A (possibly incomplete) list of areas:
* Generally address linter warnings.
* The `pgk` directory is deprecated as per dev-summit. No new packages should
be added to it. I moved the new `pkg/histogram` package to `model`
anticipating what's proposed in #9478.
* Make the naming of the Sparse Histogram more consistent. Including
abbreviations, there were just too many names for it: SparseHistogram,
Histogram, Histo, hist, his, shs, h. The idea is to call it "Histogram" in
general. Only add "Sparse" if it is needed to avoid confusion with
conventional Histograms (which is rare because the TSDB really has no notion
of conventional Histograms). Use abbreviations only in local scope, and then
really abbreviate (not just removing three out of seven letters like in
"Histo"). This is in the spirit of
https://github.com/golang/go/wiki/CodeReviewComments#variable-names
* Several other minor name changes.
* A lot of formatting of doc comments. For one, following
https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences
, but also layout question, anticipating how things will look like
when rendered by `godoc` (even where `godoc` doesn't render them
right now because they are for unexported types or not a doc comment
at all but just a normal code comment - consistency is queen!).
* Re-enabled `TestQueryLog` and `TestEndopints` (they pass now,
leaving them disabled was presumably an oversight).
* Bucket iterator for histogram.Histogram is now created with a
method.
* HistogramChunk.iterator now allows iterator recycling. (I think
@dieterbe only commented it out because he was confused by the
question in the comment.)
* HistogramAppender.Append panics now because we decided to treat
staleness marker differently.
Signed-off-by: beorn7 <beorn@grafana.com>
2021-10-09 09:57:07 -04:00
|
|
|
}
|
2023-11-29 09:15:57 -05:00
|
|
|
return items[:n]
|
Style cleanup of all the changes in sparsehistogram so far
A lot of this code was hacked together, literally during a
hackathon. This commit intends not to change the code substantially,
but just make the code obey the usual style practices.
A (possibly incomplete) list of areas:
* Generally address linter warnings.
* The `pgk` directory is deprecated as per dev-summit. No new packages should
be added to it. I moved the new `pkg/histogram` package to `model`
anticipating what's proposed in #9478.
* Make the naming of the Sparse Histogram more consistent. Including
abbreviations, there were just too many names for it: SparseHistogram,
Histogram, Histo, hist, his, shs, h. The idea is to call it "Histogram" in
general. Only add "Sparse" if it is needed to avoid confusion with
conventional Histograms (which is rare because the TSDB really has no notion
of conventional Histograms). Use abbreviations only in local scope, and then
really abbreviate (not just removing three out of seven letters like in
"Histo"). This is in the spirit of
https://github.com/golang/go/wiki/CodeReviewComments#variable-names
* Several other minor name changes.
* A lot of formatting of doc comments. For one, following
https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences
, but also layout question, anticipating how things will look like
when rendered by `godoc` (even where `godoc` doesn't render them
right now because they are for unexported types or not a doc comment
at all but just a normal code comment - consistency is queen!).
* Re-enabled `TestQueryLog` and `TestEndopints` (they pass now,
leaving them disabled was presumably an oversight).
* Bucket iterator for histogram.Histogram is now created with a
method.
* HistogramChunk.iterator now allows iterator recycling. (I think
@dieterbe only commented it out because he was confused by the
question in the comment.)
* HistogramAppender.Append panics now because we decided to treat
staleness marker differently.
Signed-off-by: beorn7 <beorn@grafana.com>
2021-10-09 09:57:07 -04:00
|
|
|
}
|
|
|
|
|
|
2023-11-03 10:47:59 -04:00
|
|
|
// Validate validates consistency between span and bucket slices. Also, buckets are checked
|
2024-02-28 08:06:43 -05:00
|
|
|
// against negative values. We check to make sure there are no unexpected fields or field values
|
|
|
|
|
// based on the exponential / custom buckets schema.
|
2023-11-03 10:47:59 -04:00
|
|
|
// For histograms that have not observed any NaN values (based on IsNaN(h.Sum) check), a
|
|
|
|
|
// strict h.Count = nCount + pCount + h.ZeroCount check is performed.
|
|
|
|
|
// Otherwise, only a lower bound check will be done (h.Count >= nCount + pCount + h.ZeroCount),
|
|
|
|
|
// because NaN observations do not increment the values of buckets (but they do increment
|
|
|
|
|
// the total h.Count).
|
|
|
|
|
func (h *Histogram) Validate() error {
|
|
|
|
|
var nCount, pCount uint64
|
2025-09-13 10:25:21 -04:00
|
|
|
switch {
|
|
|
|
|
case IsCustomBucketsSchema(h.Schema):
|
2024-03-22 09:36:39 -04:00
|
|
|
if err := checkHistogramCustomBounds(h.CustomValues, h.PositiveSpans, len(h.PositiveBuckets)); err != nil {
|
2024-02-28 08:06:43 -05:00
|
|
|
return fmt.Errorf("custom buckets: %w", err)
|
|
|
|
|
}
|
|
|
|
|
if h.ZeroCount != 0 {
|
2025-09-23 01:24:46 -04:00
|
|
|
return ErrHistogramCustomBucketsZeroCount
|
2024-02-28 08:06:43 -05:00
|
|
|
}
|
|
|
|
|
if h.ZeroThreshold != 0 {
|
2025-09-23 01:24:46 -04:00
|
|
|
return ErrHistogramCustomBucketsZeroThresh
|
2024-02-28 08:06:43 -05:00
|
|
|
}
|
|
|
|
|
if len(h.NegativeSpans) > 0 {
|
2025-09-23 01:24:46 -04:00
|
|
|
return ErrHistogramCustomBucketsNegSpans
|
2024-02-28 08:06:43 -05:00
|
|
|
}
|
|
|
|
|
if len(h.NegativeBuckets) > 0 {
|
2025-09-23 01:24:46 -04:00
|
|
|
return ErrHistogramCustomBucketsNegBuckets
|
2024-02-28 08:06:43 -05:00
|
|
|
}
|
2025-09-13 10:25:21 -04:00
|
|
|
case IsExponentialSchema(h.Schema):
|
2024-02-28 08:06:43 -05:00
|
|
|
if err := checkHistogramSpans(h.PositiveSpans, len(h.PositiveBuckets)); err != nil {
|
|
|
|
|
return fmt.Errorf("positive side: %w", err)
|
|
|
|
|
}
|
|
|
|
|
if err := checkHistogramSpans(h.NegativeSpans, len(h.NegativeBuckets)); err != nil {
|
|
|
|
|
return fmt.Errorf("negative side: %w", err)
|
|
|
|
|
}
|
|
|
|
|
err := checkHistogramBuckets(h.NegativeBuckets, &nCount, true)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("negative side: %w", err)
|
|
|
|
|
}
|
2024-03-22 09:36:39 -04:00
|
|
|
if h.CustomValues != nil {
|
2025-09-23 01:24:46 -04:00
|
|
|
return ErrHistogramExpSchemaCustomBounds
|
2024-02-28 08:06:43 -05:00
|
|
|
}
|
2025-09-13 10:25:21 -04:00
|
|
|
default:
|
2025-09-19 03:26:34 -04:00
|
|
|
return InvalidSchemaError(h.Schema)
|
2023-11-03 10:47:59 -04:00
|
|
|
}
|
2024-02-28 08:06:43 -05:00
|
|
|
err := checkHistogramBuckets(h.PositiveBuckets, &pCount, true)
|
2023-11-03 10:47:59 -04:00
|
|
|
if err != nil {
|
2023-11-07 22:49:39 -05:00
|
|
|
return fmt.Errorf("positive side: %w", err)
|
2023-11-03 10:47:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sumOfBuckets := nCount + pCount + h.ZeroCount
|
|
|
|
|
if math.IsNaN(h.Sum) {
|
|
|
|
|
if sumOfBuckets > h.Count {
|
2023-11-07 22:49:39 -05:00
|
|
|
return fmt.Errorf("%d observations found in buckets, but the Count field is %d: %w", sumOfBuckets, h.Count, ErrHistogramCountNotBigEnough)
|
2023-11-03 10:47:59 -04:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if sumOfBuckets != h.Count {
|
2023-11-07 22:49:39 -05:00
|
|
|
return fmt.Errorf("%d observations found in buckets, but the Count field is %d: %w", sumOfBuckets, h.Count, ErrHistogramCountMismatch)
|
2023-11-03 10:47:59 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-09 15:45:04 -05:00
|
|
|
type regularBucketIterator struct {
|
2022-10-03 07:15:27 -04:00
|
|
|
baseBucketIterator[uint64, int64]
|
2021-11-09 15:45:04 -05:00
|
|
|
}
|
|
|
|
|
|
2024-03-22 09:36:39 -04:00
|
|
|
func newRegularBucketIterator(spans []Span, buckets []int64, schema int32, positive bool, customValues []float64) regularBucketIterator {
|
2022-10-03 07:15:27 -04:00
|
|
|
i := baseBucketIterator[uint64, int64]{
|
2024-02-28 08:06:43 -05:00
|
|
|
schema: schema,
|
|
|
|
|
spans: spans,
|
|
|
|
|
buckets: buckets,
|
|
|
|
|
positive: positive,
|
2024-03-22 09:36:39 -04:00
|
|
|
customValues: customValues,
|
2021-11-09 15:45:04 -05:00
|
|
|
}
|
2023-10-09 02:40:59 -04:00
|
|
|
return regularBucketIterator{i}
|
2021-11-09 15:45:04 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *regularBucketIterator) Next() bool {
|
|
|
|
|
if r.spansIdx >= len(r.spans) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
span := r.spans[r.spansIdx]
|
|
|
|
|
// Seed currIdx for the first bucket.
|
|
|
|
|
if r.bucketsIdx == 0 {
|
|
|
|
|
r.currIdx = span.Offset
|
|
|
|
|
} else {
|
|
|
|
|
r.currIdx++
|
|
|
|
|
}
|
|
|
|
|
for r.idxInSpan >= span.Length {
|
|
|
|
|
// We have exhausted the current span and have to find a new
|
|
|
|
|
// one. We'll even handle pathologic spans of length 0.
|
|
|
|
|
r.idxInSpan = 0
|
|
|
|
|
r.spansIdx++
|
|
|
|
|
if r.spansIdx >= len(r.spans) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
span = r.spans[r.spansIdx]
|
|
|
|
|
r.currIdx += span.Offset
|
|
|
|
|
}
|
|
|
|
|
|
model/histogram: Make histogram bucket iterators more robust
Currently, iterating over histogram buckets can panic if the spans are
not consistent with the buckets. We aim for validating histograms upon
ingestion, but there might still be data corruptions on disk that
could trigger the panic. While data corruption on disk is really bad
and will lead to all kind of weirdness, we should still avoid
panic'ing.
Note, though, that chunks are secured by checksums, so the corruptions
won't realistically happen because of disk faults, but more likely
because a chunk was generated in a faulty way in the first place, by
a software bug or even maliciously.
This commit prevents panics in the situation where there are fewer
buckets than described by the spans. Note that the missing buckets
will simply not be iterated over. There is no signalling of this
problem. We might still consider this separately, but for now, I would
say that this kind of corruption is exceedingly rare and doesn't
deserve special treatment (which will add a whole lot of complexity to
the code).
Signed-off-by: beorn7 <beorn@grafana.com>
2025-11-18 16:59:46 -05:00
|
|
|
// This protects against index out of range panic, which
|
|
|
|
|
// can only happen with an invalid histogram.
|
|
|
|
|
if r.bucketsIdx >= len(r.buckets) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
2021-11-09 15:45:04 -05:00
|
|
|
r.currCount += r.buckets[r.bucketsIdx]
|
|
|
|
|
r.idxInSpan++
|
|
|
|
|
r.bucketsIdx++
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-03 09:53:56 -04:00
|
|
|
type cumulativeBucketIterator struct {
|
2021-11-09 15:45:04 -05:00
|
|
|
h *Histogram
|
2021-07-03 09:53:56 -04:00
|
|
|
|
|
|
|
|
posSpansIdx int // Index in h.PositiveSpans we are in. -1 means 0 bucket.
|
Style cleanup of all the changes in sparsehistogram so far
A lot of this code was hacked together, literally during a
hackathon. This commit intends not to change the code substantially,
but just make the code obey the usual style practices.
A (possibly incomplete) list of areas:
* Generally address linter warnings.
* The `pgk` directory is deprecated as per dev-summit. No new packages should
be added to it. I moved the new `pkg/histogram` package to `model`
anticipating what's proposed in #9478.
* Make the naming of the Sparse Histogram more consistent. Including
abbreviations, there were just too many names for it: SparseHistogram,
Histogram, Histo, hist, his, shs, h. The idea is to call it "Histogram" in
general. Only add "Sparse" if it is needed to avoid confusion with
conventional Histograms (which is rare because the TSDB really has no notion
of conventional Histograms). Use abbreviations only in local scope, and then
really abbreviate (not just removing three out of seven letters like in
"Histo"). This is in the spirit of
https://github.com/golang/go/wiki/CodeReviewComments#variable-names
* Several other minor name changes.
* A lot of formatting of doc comments. For one, following
https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences
, but also layout question, anticipating how things will look like
when rendered by `godoc` (even where `godoc` doesn't render them
right now because they are for unexported types or not a doc comment
at all but just a normal code comment - consistency is queen!).
* Re-enabled `TestQueryLog` and `TestEndopints` (they pass now,
leaving them disabled was presumably an oversight).
* Bucket iterator for histogram.Histogram is now created with a
method.
* HistogramChunk.iterator now allows iterator recycling. (I think
@dieterbe only commented it out because he was confused by the
question in the comment.)
* HistogramAppender.Append panics now because we decided to treat
staleness marker differently.
Signed-off-by: beorn7 <beorn@grafana.com>
2021-10-09 09:57:07 -04:00
|
|
|
posBucketsIdx int // Index in h.PositiveBuckets.
|
2021-07-03 09:53:56 -04:00
|
|
|
idxInSpan uint32 // Index in the current span. 0 <= idxInSpan < span.Length.
|
|
|
|
|
|
2021-11-09 15:45:04 -05:00
|
|
|
initialized bool
|
2021-07-03 09:53:56 -04:00
|
|
|
currIdx int32 // The actual bucket index after decoding from spans.
|
Style cleanup of all the changes in sparsehistogram so far
A lot of this code was hacked together, literally during a
hackathon. This commit intends not to change the code substantially,
but just make the code obey the usual style practices.
A (possibly incomplete) list of areas:
* Generally address linter warnings.
* The `pgk` directory is deprecated as per dev-summit. No new packages should
be added to it. I moved the new `pkg/histogram` package to `model`
anticipating what's proposed in #9478.
* Make the naming of the Sparse Histogram more consistent. Including
abbreviations, there were just too many names for it: SparseHistogram,
Histogram, Histo, hist, his, shs, h. The idea is to call it "Histogram" in
general. Only add "Sparse" if it is needed to avoid confusion with
conventional Histograms (which is rare because the TSDB really has no notion
of conventional Histograms). Use abbreviations only in local scope, and then
really abbreviate (not just removing three out of seven letters like in
"Histo"). This is in the spirit of
https://github.com/golang/go/wiki/CodeReviewComments#variable-names
* Several other minor name changes.
* A lot of formatting of doc comments. For one, following
https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences
, but also layout question, anticipating how things will look like
when rendered by `godoc` (even where `godoc` doesn't render them
right now because they are for unexported types or not a doc comment
at all but just a normal code comment - consistency is queen!).
* Re-enabled `TestQueryLog` and `TestEndopints` (they pass now,
leaving them disabled was presumably an oversight).
* Bucket iterator for histogram.Histogram is now created with a
method.
* HistogramChunk.iterator now allows iterator recycling. (I think
@dieterbe only commented it out because he was confused by the
question in the comment.)
* HistogramAppender.Append panics now because we decided to treat
staleness marker differently.
Signed-off-by: beorn7 <beorn@grafana.com>
2021-10-09 09:57:07 -04:00
|
|
|
currUpper float64 // The upper boundary of the current bucket.
|
2021-07-03 09:53:56 -04:00
|
|
|
currCount int64 // Current non-cumulative count for the current bucket. Does not apply for empty bucket.
|
|
|
|
|
currCumulativeCount uint64 // Current "cumulative" count for the current bucket.
|
|
|
|
|
|
|
|
|
|
// Between 2 spans there could be some empty buckets which
|
|
|
|
|
// still needs to be counted for cumulative buckets.
|
|
|
|
|
// When we hit the end of a span, we use this to iterate
|
|
|
|
|
// through the empty buckets.
|
|
|
|
|
emptyBucketCount int32
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *cumulativeBucketIterator) Next() bool {
|
|
|
|
|
if c.posSpansIdx == -1 {
|
|
|
|
|
// Zero bucket.
|
|
|
|
|
c.posSpansIdx++
|
|
|
|
|
if c.h.ZeroCount == 0 {
|
|
|
|
|
return c.Next()
|
|
|
|
|
}
|
|
|
|
|
|
Style cleanup of all the changes in sparsehistogram so far
A lot of this code was hacked together, literally during a
hackathon. This commit intends not to change the code substantially,
but just make the code obey the usual style practices.
A (possibly incomplete) list of areas:
* Generally address linter warnings.
* The `pgk` directory is deprecated as per dev-summit. No new packages should
be added to it. I moved the new `pkg/histogram` package to `model`
anticipating what's proposed in #9478.
* Make the naming of the Sparse Histogram more consistent. Including
abbreviations, there were just too many names for it: SparseHistogram,
Histogram, Histo, hist, his, shs, h. The idea is to call it "Histogram" in
general. Only add "Sparse" if it is needed to avoid confusion with
conventional Histograms (which is rare because the TSDB really has no notion
of conventional Histograms). Use abbreviations only in local scope, and then
really abbreviate (not just removing three out of seven letters like in
"Histo"). This is in the spirit of
https://github.com/golang/go/wiki/CodeReviewComments#variable-names
* Several other minor name changes.
* A lot of formatting of doc comments. For one, following
https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences
, but also layout question, anticipating how things will look like
when rendered by `godoc` (even where `godoc` doesn't render them
right now because they are for unexported types or not a doc comment
at all but just a normal code comment - consistency is queen!).
* Re-enabled `TestQueryLog` and `TestEndopints` (they pass now,
leaving them disabled was presumably an oversight).
* Bucket iterator for histogram.Histogram is now created with a
method.
* HistogramChunk.iterator now allows iterator recycling. (I think
@dieterbe only commented it out because he was confused by the
question in the comment.)
* HistogramAppender.Append panics now because we decided to treat
staleness marker differently.
Signed-off-by: beorn7 <beorn@grafana.com>
2021-10-09 09:57:07 -04:00
|
|
|
c.currUpper = c.h.ZeroThreshold
|
2021-07-03 09:53:56 -04:00
|
|
|
c.currCount = int64(c.h.ZeroCount)
|
|
|
|
|
c.currCumulativeCount = uint64(c.currCount)
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if c.posSpansIdx >= len(c.h.PositiveSpans) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if c.emptyBucketCount > 0 {
|
|
|
|
|
// We are traversing through empty buckets at the moment.
|
2024-03-22 09:36:39 -04:00
|
|
|
c.currUpper = getBound(c.currIdx, c.h.Schema, c.h.CustomValues)
|
2021-07-03 09:53:56 -04:00
|
|
|
c.currIdx++
|
|
|
|
|
c.emptyBucketCount--
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
span := c.h.PositiveSpans[c.posSpansIdx]
|
2021-11-09 15:45:04 -05:00
|
|
|
if c.posSpansIdx == 0 && !c.initialized {
|
2021-11-23 13:40:49 -05:00
|
|
|
// Initializing.
|
2021-07-03 09:53:56 -04:00
|
|
|
c.currIdx = span.Offset
|
2021-11-09 15:45:04 -05:00
|
|
|
// The first bucket is an absolute value and not a delta with Zero bucket.
|
2021-07-03 09:53:56 -04:00
|
|
|
c.currCount = 0
|
2021-11-09 15:45:04 -05:00
|
|
|
c.initialized = true
|
2021-07-03 09:53:56 -04:00
|
|
|
}
|
|
|
|
|
|
model/histogram: Make histogram bucket iterators more robust
Currently, iterating over histogram buckets can panic if the spans are
not consistent with the buckets. We aim for validating histograms upon
ingestion, but there might still be data corruptions on disk that
could trigger the panic. While data corruption on disk is really bad
and will lead to all kind of weirdness, we should still avoid
panic'ing.
Note, though, that chunks are secured by checksums, so the corruptions
won't realistically happen because of disk faults, but more likely
because a chunk was generated in a faulty way in the first place, by
a software bug or even maliciously.
This commit prevents panics in the situation where there are fewer
buckets than described by the spans. Note that the missing buckets
will simply not be iterated over. There is no signalling of this
problem. We might still consider this separately, but for now, I would
say that this kind of corruption is exceedingly rare and doesn't
deserve special treatment (which will add a whole lot of complexity to
the code).
Signed-off-by: beorn7 <beorn@grafana.com>
2025-11-18 16:59:46 -05:00
|
|
|
// This protects against index out of range panic, which
|
|
|
|
|
// can only happen with an invalid histogram.
|
|
|
|
|
if c.posBucketsIdx >= len(c.h.PositiveBuckets) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
2021-07-03 09:53:56 -04:00
|
|
|
c.currCount += c.h.PositiveBuckets[c.posBucketsIdx]
|
|
|
|
|
c.currCumulativeCount += uint64(c.currCount)
|
2024-03-22 09:36:39 -04:00
|
|
|
c.currUpper = getBound(c.currIdx, c.h.Schema, c.h.CustomValues)
|
2021-07-03 09:53:56 -04:00
|
|
|
|
|
|
|
|
c.posBucketsIdx++
|
|
|
|
|
c.idxInSpan++
|
|
|
|
|
c.currIdx++
|
|
|
|
|
if c.idxInSpan >= span.Length {
|
|
|
|
|
// Move to the next span. This one is done.
|
|
|
|
|
c.posSpansIdx++
|
|
|
|
|
c.idxInSpan = 0
|
|
|
|
|
if c.posSpansIdx < len(c.h.PositiveSpans) {
|
|
|
|
|
c.emptyBucketCount = c.h.PositiveSpans[c.posSpansIdx].Offset
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
}
|
2021-11-09 15:45:04 -05:00
|
|
|
|
2022-10-03 07:15:27 -04:00
|
|
|
func (c *cumulativeBucketIterator) At() Bucket[uint64] {
|
|
|
|
|
return Bucket[uint64]{
|
2021-11-09 15:45:04 -05:00
|
|
|
Upper: c.currUpper,
|
|
|
|
|
Lower: math.Inf(-1),
|
|
|
|
|
UpperInclusive: true,
|
|
|
|
|
LowerInclusive: true,
|
|
|
|
|
Count: c.currCumulativeCount,
|
|
|
|
|
Index: c.currIdx - 1,
|
2021-07-03 09:53:56 -04:00
|
|
|
}
|
|
|
|
|
}
|
2023-11-08 08:43:05 -05:00
|
|
|
|
|
|
|
|
// ReduceResolution reduces the histogram's spans, buckets into target schema.
|
Add histogram validation in remote-read and during reducing resolution (#17561)
ReduceResolution is currently called before validation during
ingestion. This will cause a panic if there are not enough buckets in
the histogram. If there are too many buckets, the spurious buckets are
ignored, and therefore the error in the input histogram is masked.
Furthermore, invalid negative offsets might cause problems, too.
Therefore, we need to do some minimal validation in reduceResolution.
Fortunately, it is easy and shouldn't slow things down. Sadly, it
requires to return errors, which triggers a bunch of code changes.
Even here is a bright side, we can get rud of a few panics. (Remember:
Don't panic!)
In different news, we haven't done a full validation of histograms
read via remote-read. This is not so much a security concern (as you
can throw off Prometheus easily by feeding it bogus data via
remote-read) but more that remote-read sources might be makeshift and
could accidentally create invalid histograms. We really don't want to
panic in that case. So this commit does not only add a check of the
spans and buckets as needed for resolution reduction but also a full
validation during remote-read.
Signed-off-by: beorn7 <beorn@grafana.com>
2025-11-20 18:22:24 -05:00
|
|
|
// An error is returned in the following cases:
|
|
|
|
|
// - The target schema is not smaller than the current histogram's schema.
|
|
|
|
|
// - The histogram has custom buckets.
|
|
|
|
|
// - The target schema is a custom buckets schema.
|
|
|
|
|
// - Any spans have an invalid offset.
|
|
|
|
|
// - The spans are inconsistent with the number of buckets.
|
|
|
|
|
func (h *Histogram) ReduceResolution(targetSchema int32) error {
|
|
|
|
|
// Note that the follow three returns are not returning a
|
|
|
|
|
// histogram.Error because they are programming errors.
|
2024-02-28 08:06:43 -05:00
|
|
|
if h.UsesCustomBuckets() {
|
Add histogram validation in remote-read and during reducing resolution (#17561)
ReduceResolution is currently called before validation during
ingestion. This will cause a panic if there are not enough buckets in
the histogram. If there are too many buckets, the spurious buckets are
ignored, and therefore the error in the input histogram is masked.
Furthermore, invalid negative offsets might cause problems, too.
Therefore, we need to do some minimal validation in reduceResolution.
Fortunately, it is easy and shouldn't slow things down. Sadly, it
requires to return errors, which triggers a bunch of code changes.
Even here is a bright side, we can get rud of a few panics. (Remember:
Don't panic!)
In different news, we haven't done a full validation of histograms
read via remote-read. This is not so much a security concern (as you
can throw off Prometheus easily by feeding it bogus data via
remote-read) but more that remote-read sources might be makeshift and
could accidentally create invalid histograms. We really don't want to
panic in that case. So this commit does not only add a check of the
spans and buckets as needed for resolution reduction but also a full
validation during remote-read.
Signed-off-by: beorn7 <beorn@grafana.com>
2025-11-20 18:22:24 -05:00
|
|
|
return errors.New("cannot reduce resolution when there are custom buckets")
|
2024-02-28 08:06:43 -05:00
|
|
|
}
|
|
|
|
|
if IsCustomBucketsSchema(targetSchema) {
|
Add histogram validation in remote-read and during reducing resolution (#17561)
ReduceResolution is currently called before validation during
ingestion. This will cause a panic if there are not enough buckets in
the histogram. If there are too many buckets, the spurious buckets are
ignored, and therefore the error in the input histogram is masked.
Furthermore, invalid negative offsets might cause problems, too.
Therefore, we need to do some minimal validation in reduceResolution.
Fortunately, it is easy and shouldn't slow things down. Sadly, it
requires to return errors, which triggers a bunch of code changes.
Even here is a bright side, we can get rud of a few panics. (Remember:
Don't panic!)
In different news, we haven't done a full validation of histograms
read via remote-read. This is not so much a security concern (as you
can throw off Prometheus easily by feeding it bogus data via
remote-read) but more that remote-read sources might be makeshift and
could accidentally create invalid histograms. We really don't want to
panic in that case. So this commit does not only add a check of the
spans and buckets as needed for resolution reduction but also a full
validation during remote-read.
Signed-off-by: beorn7 <beorn@grafana.com>
2025-11-20 18:22:24 -05:00
|
|
|
return errors.New("cannot reduce resolution to custom buckets schema")
|
2024-02-28 08:06:43 -05:00
|
|
|
}
|
2023-11-25 04:38:15 -05:00
|
|
|
if targetSchema >= h.Schema {
|
Add histogram validation in remote-read and during reducing resolution (#17561)
ReduceResolution is currently called before validation during
ingestion. This will cause a panic if there are not enough buckets in
the histogram. If there are too many buckets, the spurious buckets are
ignored, and therefore the error in the input histogram is masked.
Furthermore, invalid negative offsets might cause problems, too.
Therefore, we need to do some minimal validation in reduceResolution.
Fortunately, it is easy and shouldn't slow things down. Sadly, it
requires to return errors, which triggers a bunch of code changes.
Even here is a bright side, we can get rud of a few panics. (Remember:
Don't panic!)
In different news, we haven't done a full validation of histograms
read via remote-read. This is not so much a security concern (as you
can throw off Prometheus easily by feeding it bogus data via
remote-read) but more that remote-read sources might be makeshift and
could accidentally create invalid histograms. We really don't want to
panic in that case. So this commit does not only add a check of the
spans and buckets as needed for resolution reduction but also a full
validation during remote-read.
Signed-off-by: beorn7 <beorn@grafana.com>
2025-11-20 18:22:24 -05:00
|
|
|
return fmt.Errorf("cannot reduce resolution from schema %d to %d", h.Schema, targetSchema)
|
2023-11-25 04:38:15 -05:00
|
|
|
}
|
|
|
|
|
|
Add histogram validation in remote-read and during reducing resolution (#17561)
ReduceResolution is currently called before validation during
ingestion. This will cause a panic if there are not enough buckets in
the histogram. If there are too many buckets, the spurious buckets are
ignored, and therefore the error in the input histogram is masked.
Furthermore, invalid negative offsets might cause problems, too.
Therefore, we need to do some minimal validation in reduceResolution.
Fortunately, it is easy and shouldn't slow things down. Sadly, it
requires to return errors, which triggers a bunch of code changes.
Even here is a bright side, we can get rud of a few panics. (Remember:
Don't panic!)
In different news, we haven't done a full validation of histograms
read via remote-read. This is not so much a security concern (as you
can throw off Prometheus easily by feeding it bogus data via
remote-read) but more that remote-read sources might be makeshift and
could accidentally create invalid histograms. We really don't want to
panic in that case. So this commit does not only add a check of the
spans and buckets as needed for resolution reduction but also a full
validation during remote-read.
Signed-off-by: beorn7 <beorn@grafana.com>
2025-11-20 18:22:24 -05:00
|
|
|
var err error
|
|
|
|
|
|
|
|
|
|
if h.PositiveSpans, h.PositiveBuckets, err = reduceResolution(
|
2023-11-26 13:12:44 -05:00
|
|
|
h.PositiveSpans, h.PositiveBuckets, h.Schema, targetSchema, true, true,
|
Add histogram validation in remote-read and during reducing resolution (#17561)
ReduceResolution is currently called before validation during
ingestion. This will cause a panic if there are not enough buckets in
the histogram. If there are too many buckets, the spurious buckets are
ignored, and therefore the error in the input histogram is masked.
Furthermore, invalid negative offsets might cause problems, too.
Therefore, we need to do some minimal validation in reduceResolution.
Fortunately, it is easy and shouldn't slow things down. Sadly, it
requires to return errors, which triggers a bunch of code changes.
Even here is a bright side, we can get rud of a few panics. (Remember:
Don't panic!)
In different news, we haven't done a full validation of histograms
read via remote-read. This is not so much a security concern (as you
can throw off Prometheus easily by feeding it bogus data via
remote-read) but more that remote-read sources might be makeshift and
could accidentally create invalid histograms. We really don't want to
panic in that case. So this commit does not only add a check of the
spans and buckets as needed for resolution reduction but also a full
validation during remote-read.
Signed-off-by: beorn7 <beorn@grafana.com>
2025-11-20 18:22:24 -05:00
|
|
|
); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if h.NegativeSpans, h.NegativeBuckets, err = reduceResolution(
|
2023-11-26 13:12:44 -05:00
|
|
|
h.NegativeSpans, h.NegativeBuckets, h.Schema, targetSchema, true, true,
|
Add histogram validation in remote-read and during reducing resolution (#17561)
ReduceResolution is currently called before validation during
ingestion. This will cause a panic if there are not enough buckets in
the histogram. If there are too many buckets, the spurious buckets are
ignored, and therefore the error in the input histogram is masked.
Furthermore, invalid negative offsets might cause problems, too.
Therefore, we need to do some minimal validation in reduceResolution.
Fortunately, it is easy and shouldn't slow things down. Sadly, it
requires to return errors, which triggers a bunch of code changes.
Even here is a bright side, we can get rud of a few panics. (Remember:
Don't panic!)
In different news, we haven't done a full validation of histograms
read via remote-read. This is not so much a security concern (as you
can throw off Prometheus easily by feeding it bogus data via
remote-read) but more that remote-read sources might be makeshift and
could accidentally create invalid histograms. We really don't want to
panic in that case. So this commit does not only add a check of the
spans and buckets as needed for resolution reduction but also a full
validation during remote-read.
Signed-off-by: beorn7 <beorn@grafana.com>
2025-11-20 18:22:24 -05:00
|
|
|
); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2023-11-10 08:33:34 -05:00
|
|
|
h.Schema = targetSchema
|
Add histogram validation in remote-read and during reducing resolution (#17561)
ReduceResolution is currently called before validation during
ingestion. This will cause a panic if there are not enough buckets in
the histogram. If there are too many buckets, the spurious buckets are
ignored, and therefore the error in the input histogram is masked.
Furthermore, invalid negative offsets might cause problems, too.
Therefore, we need to do some minimal validation in reduceResolution.
Fortunately, it is easy and shouldn't slow things down. Sadly, it
requires to return errors, which triggers a bunch of code changes.
Even here is a bright side, we can get rud of a few panics. (Remember:
Don't panic!)
In different news, we haven't done a full validation of histograms
read via remote-read. This is not so much a security concern (as you
can throw off Prometheus easily by feeding it bogus data via
remote-read) but more that remote-read sources might be makeshift and
could accidentally create invalid histograms. We really don't want to
panic in that case. So this commit does not only add a check of the
spans and buckets as needed for resolution reduction but also a full
validation during remote-read.
Signed-off-by: beorn7 <beorn@grafana.com>
2025-11-20 18:22:24 -05:00
|
|
|
return nil
|
2023-11-08 08:43:05 -05:00
|
|
|
}
|