mirror of
https://github.com/prometheus/prometheus.git
synced 2026-05-28 04:02:21 -04:00
Merge pull request #17904 from linasm/trim_histogram
Some checks are pending
buf.build / lint and publish (push) Waiting to run
CI / Go tests (push) Waiting to run
CI / More Go tests (push) Waiting to run
CI / Go tests with previous Go version (push) Waiting to run
CI / UI tests (push) Waiting to run
CI / Go tests on Windows (push) Waiting to run
CI / Mixins tests (push) Waiting to run
CI / Compliance testing (push) Waiting to run
CI / Build Prometheus for common architectures (push) Waiting to run
CI / Build Prometheus for all architectures (push) Waiting to run
CI / Report status of build Prometheus for all architectures (push) Blocked by required conditions
CI / Check generated parser (push) Waiting to run
CI / golangci-lint (push) Waiting to run
CI / fuzzing (push) Waiting to run
CI / codeql (push) Waiting to run
CI / Publish main branch artifacts (push) Blocked by required conditions
CI / Publish release artefacts (push) Blocked by required conditions
CI / Publish UI on npm Registry (push) Blocked by required conditions
Scorecards supply-chain security / Scorecards analysis (push) Waiting to run
Some checks are pending
buf.build / lint and publish (push) Waiting to run
CI / Go tests (push) Waiting to run
CI / More Go tests (push) Waiting to run
CI / Go tests with previous Go version (push) Waiting to run
CI / UI tests (push) Waiting to run
CI / Go tests on Windows (push) Waiting to run
CI / Mixins tests (push) Waiting to run
CI / Compliance testing (push) Waiting to run
CI / Build Prometheus for common architectures (push) Waiting to run
CI / Build Prometheus for all architectures (push) Waiting to run
CI / Report status of build Prometheus for all architectures (push) Blocked by required conditions
CI / Check generated parser (push) Waiting to run
CI / golangci-lint (push) Waiting to run
CI / fuzzing (push) Waiting to run
CI / codeql (push) Waiting to run
CI / Publish main branch artifacts (push) Blocked by required conditions
CI / Publish release artefacts (push) Blocked by required conditions
CI / Publish UI on npm Registry (push) Blocked by required conditions
Scorecards supply-chain security / Scorecards analysis (push) Waiting to run
promql: Implement </ and >/ operators for trimming native histograms
This commit is contained in:
commit
5d3f9ee39b
14 changed files with 1508 additions and 708 deletions
2
cmd/prometheus/testdata/features.json
vendored
2
cmd/prometheus/testdata/features.json
vendored
|
|
@ -145,6 +145,8 @@
|
|||
"=~": true,
|
||||
">": true,
|
||||
">=": true,
|
||||
">/": true,
|
||||
"</": true,
|
||||
"@": true,
|
||||
"^": true,
|
||||
"and": true,
|
||||
|
|
|
|||
|
|
@ -126,6 +126,25 @@ samples. Operations involving histogram samples result in the removal of the
|
|||
corresponding vector elements from the output vector, flagged by an
|
||||
info-level annotation.
|
||||
|
||||
### Histogram trim operators
|
||||
|
||||
The following binary histogram trim operators exist in Prometheus:
|
||||
|
||||
* `</` (trim upper): removes all observations above a threshold value
|
||||
* `>/` (trim lower): removes all observations below a threshold value
|
||||
|
||||
Histogram trim operators are defined between vector/scalar and vector/vector value pairs,
|
||||
where the left hand side is a native histogram (either exponential or NHCB),
|
||||
and the right hand side is a float threshold value.
|
||||
|
||||
In case the threshold value is not aligned to one of the bucket boundaries of the histogram,
|
||||
either linear (for NHCB and zero buckets of exponential histogram) or exponential (for non zero
|
||||
bucket of exponential histogram) interpolation is applied to compute the estimated count
|
||||
of observations that remain in the bucket containing the threshold.
|
||||
|
||||
In case when some observations get trimmed, the new sum of observation values is recomputed
|
||||
(approximately) based on the remaining observations.
|
||||
|
||||
### Comparison binary operators
|
||||
|
||||
The following binary comparison operators exist in Prometheus:
|
||||
|
|
|
|||
345
promql/engine.go
345
promql/engine.go
|
|
@ -3184,6 +3184,337 @@ func scalarBinop(op parser.ItemType, lhs, rhs float64) float64 {
|
|||
panic(fmt.Errorf("operator %q not allowed for Scalar operations", op))
|
||||
}
|
||||
|
||||
func handleInfinityBuckets(isUpperTrim bool, b histogram.Bucket[float64], rhs float64) (underCount, bucketMidpoint float64) {
|
||||
zeroIfInf := func(x float64) float64 {
|
||||
if math.IsInf(x, 0) {
|
||||
return 0
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// Case 1: Bucket with lower bound -Inf.
|
||||
if math.IsInf(b.Lower, -1) {
|
||||
// TRIM_UPPER (</) - remove values greater than rhs
|
||||
if isUpperTrim {
|
||||
if rhs >= b.Upper {
|
||||
// As the rhs is greater than the upper bound, we keep the entire current bucket.
|
||||
return b.Count, 0
|
||||
}
|
||||
if rhs > 0 && b.Upper > 0 && !math.IsInf(b.Upper, 1) {
|
||||
// If upper is finite and positive, we treat lower as 0 (despite it de facto being -Inf).
|
||||
// This is only possible with NHCB, so we can always use linear interpolation.
|
||||
return b.Count * rhs / b.Upper, rhs / 2
|
||||
}
|
||||
if b.Upper <= 0 {
|
||||
return b.Count, rhs
|
||||
}
|
||||
// Otherwise, we are targeting a valid trim, but as we don't know the exact distribution of values that belongs to an infinite bucket, we need to remove the entire bucket.
|
||||
return 0, zeroIfInf(b.Upper)
|
||||
}
|
||||
// TRIM_LOWER (>/) - remove values less than rhs
|
||||
if rhs <= b.Lower {
|
||||
// Impossible to happen because the lower bound is -Inf. Returning the entire current bucket.
|
||||
return b.Count, 0
|
||||
}
|
||||
if rhs >= 0 && b.Upper > rhs && !math.IsInf(b.Upper, 1) {
|
||||
// If upper is finite and positive, we treat lower as 0 (despite it de facto being -Inf).
|
||||
// This is only possible with NHCB, so we can always use linear interpolation.
|
||||
return b.Count * (1 - rhs/b.Upper), (rhs + b.Upper) / 2
|
||||
}
|
||||
// Otherwise, we are targeting a valid trim, but as we don't know the exact distribution of values that belongs to an infinite bucket, we need to remove the entire bucket.
|
||||
return 0, zeroIfInf(b.Upper)
|
||||
}
|
||||
|
||||
// Case 2: Bucket with upper bound +Inf.
|
||||
if math.IsInf(b.Upper, 1) {
|
||||
if isUpperTrim {
|
||||
// TRIM_UPPER (</) - remove values greater than rhs.
|
||||
// We don't care about lower here, because:
|
||||
// when rhs >= lower and the bucket extends to +Inf, some values in this bucket could be > rhs, so we conservatively remove the entire bucket;
|
||||
// when rhs < lower, all values in this bucket are >= lower > rhs, so all values should be removed.
|
||||
return 0, zeroIfInf(b.Lower)
|
||||
}
|
||||
// TRIM_LOWER (>/) - remove values less than rhs.
|
||||
if rhs >= b.Lower {
|
||||
return b.Count, rhs
|
||||
}
|
||||
// lower < rhs: we are inside the infinity bucket, but as we don't know the exact distribution of values, we conservatively remove the entire bucket.
|
||||
return 0, zeroIfInf(b.Lower)
|
||||
}
|
||||
|
||||
panic(fmt.Errorf("one of the bounds must be infinite for handleInfinityBuckets, got %v", b))
|
||||
}
|
||||
|
||||
// computeSplit calculates the portion of the bucket's count <= rhs (trim point).
|
||||
func computeSplit(b histogram.Bucket[float64], rhs float64, isPositive, isLinear bool) float64 {
|
||||
if rhs <= b.Lower {
|
||||
return 0
|
||||
}
|
||||
if rhs >= b.Upper {
|
||||
return b.Count
|
||||
}
|
||||
|
||||
var fraction float64
|
||||
switch {
|
||||
case isLinear:
|
||||
fraction = (rhs - b.Lower) / (b.Upper - b.Lower)
|
||||
default:
|
||||
// Exponential interpolation.
|
||||
logLower := math.Log2(math.Abs(b.Lower))
|
||||
logUpper := math.Log2(math.Abs(b.Upper))
|
||||
logV := math.Log2(math.Abs(rhs))
|
||||
|
||||
if isPositive {
|
||||
fraction = (logV - logLower) / (logUpper - logLower)
|
||||
} else {
|
||||
fraction = 1 - ((logV - logUpper) / (logLower - logUpper))
|
||||
}
|
||||
}
|
||||
|
||||
return b.Count * fraction
|
||||
}
|
||||
|
||||
func computeZeroBucketTrim(zeroBucket histogram.Bucket[float64], rhs float64, hasNegative, hasPositive, isUpperTrim bool) (float64, float64) {
|
||||
var (
|
||||
lower = zeroBucket.Lower
|
||||
upper = zeroBucket.Upper
|
||||
)
|
||||
if hasNegative && !hasPositive {
|
||||
upper = 0
|
||||
}
|
||||
if hasPositive && !hasNegative {
|
||||
lower = 0
|
||||
}
|
||||
|
||||
var fraction, midpoint float64
|
||||
|
||||
if isUpperTrim {
|
||||
if rhs <= lower {
|
||||
return 0, 0
|
||||
}
|
||||
if rhs >= upper {
|
||||
return zeroBucket.Count, (lower + upper) / 2
|
||||
}
|
||||
|
||||
fraction = (rhs - lower) / (upper - lower)
|
||||
midpoint = (lower + rhs) / 2
|
||||
} else { // lower trim
|
||||
if rhs <= lower {
|
||||
return zeroBucket.Count, (lower + upper) / 2
|
||||
}
|
||||
if rhs >= upper {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
fraction = (upper - rhs) / (upper - lower)
|
||||
midpoint = (rhs + upper) / 2
|
||||
}
|
||||
|
||||
return zeroBucket.Count * fraction, midpoint
|
||||
}
|
||||
|
||||
func computeBucketTrim(b histogram.Bucket[float64], rhs float64, isUpperTrim, isPositive, isCustomBucket bool) (float64, float64) {
|
||||
if math.IsInf(b.Lower, -1) || math.IsInf(b.Upper, 1) {
|
||||
return handleInfinityBuckets(isUpperTrim, b, rhs)
|
||||
}
|
||||
|
||||
underCount := computeSplit(b, rhs, isPositive, isCustomBucket)
|
||||
|
||||
if isUpperTrim {
|
||||
return underCount, computeMidpoint(b.Lower, rhs, isPositive, isCustomBucket)
|
||||
}
|
||||
|
||||
return b.Count - underCount, computeMidpoint(rhs, b.Upper, isPositive, isCustomBucket)
|
||||
}
|
||||
|
||||
// Helper function to trim native histogram buckets.
|
||||
// TODO: move trimHistogram to model/histogram/float_histogram.go (making it a method of FloatHistogram).
|
||||
func trimHistogram(trimmedHist *histogram.FloatHistogram, rhs float64, isUpperTrim bool) {
|
||||
var (
|
||||
updatedCount, updatedSum float64
|
||||
trimmedBuckets bool
|
||||
isCustomBucket = trimmedHist.UsesCustomBuckets()
|
||||
hasPositive, hasNegative bool
|
||||
)
|
||||
|
||||
if isUpperTrim {
|
||||
// Calculate the fraction to keep for buckets that contain the trim value.
|
||||
// For TRIM_UPPER, we keep observations below the trim point (rhs).
|
||||
// Example: histogram </ float.
|
||||
for i, iter := 0, trimmedHist.PositiveBucketIterator(); iter.Next(); i++ {
|
||||
bucket := iter.At()
|
||||
if bucket.Count == 0 {
|
||||
continue
|
||||
}
|
||||
hasPositive = true
|
||||
|
||||
switch {
|
||||
case bucket.Upper <= rhs:
|
||||
// Bucket is entirely below the trim point - keep all.
|
||||
updatedCount += bucket.Count
|
||||
bucketMidpoint := computeMidpoint(bucket.Lower, bucket.Upper, true, isCustomBucket)
|
||||
updatedSum += bucketMidpoint * bucket.Count
|
||||
|
||||
case bucket.Lower < rhs:
|
||||
// Bucket contains the trim point - interpolate.
|
||||
keepCount, bucketMidpoint := computeBucketTrim(bucket, rhs, isUpperTrim, true, isCustomBucket)
|
||||
|
||||
updatedCount += keepCount
|
||||
updatedSum += bucketMidpoint * keepCount
|
||||
if trimmedHist.PositiveBuckets[i] != keepCount {
|
||||
trimmedHist.PositiveBuckets[i] = keepCount
|
||||
trimmedBuckets = true
|
||||
}
|
||||
|
||||
default:
|
||||
// Bucket is entirely above the trim point - discard.
|
||||
trimmedHist.PositiveBuckets[i] = 0
|
||||
trimmedBuckets = true
|
||||
}
|
||||
}
|
||||
|
||||
for i, iter := 0, trimmedHist.NegativeBucketIterator(); iter.Next(); i++ {
|
||||
bucket := iter.At()
|
||||
if bucket.Count == 0 {
|
||||
continue
|
||||
}
|
||||
hasNegative = true
|
||||
|
||||
switch {
|
||||
case bucket.Upper <= rhs:
|
||||
// Bucket is entirely below the trim point - keep all.
|
||||
updatedCount += bucket.Count
|
||||
bucketMidpoint := computeMidpoint(bucket.Lower, bucket.Upper, false, isCustomBucket)
|
||||
updatedSum += bucketMidpoint * bucket.Count
|
||||
|
||||
case bucket.Lower < rhs:
|
||||
// Bucket contains the trim point - interpolate.
|
||||
keepCount, bucketMidpoint := computeBucketTrim(bucket, rhs, isUpperTrim, false, isCustomBucket)
|
||||
|
||||
updatedCount += keepCount
|
||||
updatedSum += bucketMidpoint * keepCount
|
||||
if trimmedHist.NegativeBuckets[i] != keepCount {
|
||||
trimmedHist.NegativeBuckets[i] = keepCount
|
||||
trimmedBuckets = true
|
||||
}
|
||||
|
||||
default:
|
||||
trimmedHist.NegativeBuckets[i] = 0
|
||||
trimmedBuckets = true
|
||||
}
|
||||
}
|
||||
} else { // !isUpperTrim
|
||||
// For TRIM_LOWER, we keep observations above the trim point (rhs).
|
||||
// Example: histogram >/ float.
|
||||
for i, iter := 0, trimmedHist.PositiveBucketIterator(); iter.Next(); i++ {
|
||||
bucket := iter.At()
|
||||
if bucket.Count == 0 {
|
||||
continue
|
||||
}
|
||||
hasPositive = true
|
||||
|
||||
switch {
|
||||
case bucket.Lower >= rhs:
|
||||
// Bucket is entirely below the trim point - keep all.
|
||||
updatedCount += bucket.Count
|
||||
bucketMidpoint := computeMidpoint(bucket.Lower, bucket.Upper, true, isCustomBucket)
|
||||
updatedSum += bucketMidpoint * bucket.Count
|
||||
|
||||
case bucket.Upper > rhs:
|
||||
// Bucket contains the trim point - interpolate.
|
||||
keepCount, bucketMidpoint := computeBucketTrim(bucket, rhs, isUpperTrim, true, isCustomBucket)
|
||||
|
||||
updatedCount += keepCount
|
||||
updatedSum += bucketMidpoint * keepCount
|
||||
if trimmedHist.PositiveBuckets[i] != keepCount {
|
||||
trimmedHist.PositiveBuckets[i] = keepCount
|
||||
trimmedBuckets = true
|
||||
}
|
||||
|
||||
default:
|
||||
trimmedHist.PositiveBuckets[i] = 0
|
||||
trimmedBuckets = true
|
||||
}
|
||||
}
|
||||
|
||||
for i, iter := 0, trimmedHist.NegativeBucketIterator(); iter.Next(); i++ {
|
||||
bucket := iter.At()
|
||||
if bucket.Count == 0 {
|
||||
continue
|
||||
}
|
||||
hasNegative = true
|
||||
|
||||
switch {
|
||||
case bucket.Lower >= rhs:
|
||||
// Bucket is entirely below the trim point - keep all.
|
||||
updatedCount += bucket.Count
|
||||
bucketMidpoint := computeMidpoint(bucket.Lower, bucket.Upper, false, isCustomBucket)
|
||||
updatedSum += bucketMidpoint * bucket.Count
|
||||
|
||||
case bucket.Upper > rhs:
|
||||
// Bucket contains the trim point - interpolate.
|
||||
keepCount, bucketMidpoint := computeBucketTrim(bucket, rhs, isUpperTrim, false, isCustomBucket)
|
||||
|
||||
updatedCount += keepCount
|
||||
updatedSum += bucketMidpoint * keepCount
|
||||
if trimmedHist.NegativeBuckets[i] != keepCount {
|
||||
trimmedHist.NegativeBuckets[i] = keepCount
|
||||
trimmedBuckets = true
|
||||
}
|
||||
|
||||
default:
|
||||
trimmedHist.NegativeBuckets[i] = 0
|
||||
trimmedBuckets = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the zero count bucket.
|
||||
if trimmedHist.ZeroCount > 0 {
|
||||
keepCount, bucketMidpoint := computeZeroBucketTrim(trimmedHist.ZeroBucket(), rhs, hasNegative, hasPositive, isUpperTrim)
|
||||
|
||||
if trimmedHist.ZeroCount != keepCount {
|
||||
trimmedHist.ZeroCount = keepCount
|
||||
trimmedBuckets = true
|
||||
}
|
||||
updatedSum += bucketMidpoint * keepCount
|
||||
updatedCount += keepCount
|
||||
}
|
||||
|
||||
if trimmedBuckets {
|
||||
// Only update the totals in case some bucket(s) were fully (or partially) trimmed.
|
||||
trimmedHist.Count = updatedCount
|
||||
trimmedHist.Sum = updatedSum
|
||||
|
||||
trimmedHist.Compact(0)
|
||||
}
|
||||
}
|
||||
|
||||
func computeMidpoint(survivingIntervalLowerBound, survivingIntervalUpperBound float64, isPositive, isLinear bool) float64 {
|
||||
if math.IsInf(survivingIntervalLowerBound, 0) {
|
||||
if math.IsInf(survivingIntervalUpperBound, 0) {
|
||||
return 0
|
||||
}
|
||||
if survivingIntervalUpperBound > 0 {
|
||||
return survivingIntervalUpperBound / 2
|
||||
}
|
||||
return survivingIntervalUpperBound
|
||||
} else if math.IsInf(survivingIntervalUpperBound, 0) {
|
||||
return survivingIntervalLowerBound
|
||||
}
|
||||
|
||||
if isLinear {
|
||||
return (survivingIntervalLowerBound + survivingIntervalUpperBound) / 2
|
||||
}
|
||||
|
||||
geoMean := math.Sqrt(math.Abs(survivingIntervalLowerBound * survivingIntervalUpperBound))
|
||||
|
||||
if isPositive {
|
||||
return geoMean
|
||||
}
|
||||
return -geoMean
|
||||
}
|
||||
|
||||
// vectorElemBinop evaluates a binary operation between two Vector elements.
|
||||
func vectorElemBinop(op parser.ItemType, lhs, rhs float64, hlhs, hrhs *histogram.FloatHistogram, pos posrange.PositionRange) (res float64, resH *histogram.FloatHistogram, keep bool, info, err error) {
|
||||
switch {
|
||||
|
|
@ -3216,6 +3547,8 @@ func vectorElemBinop(op parser.ItemType, lhs, rhs float64, hlhs, hrhs *histogram
|
|||
return lhs, nil, lhs <= rhs, nil, nil
|
||||
case parser.ATAN2:
|
||||
return math.Atan2(lhs, rhs), nil, true, nil, nil
|
||||
case parser.TRIM_LOWER, parser.TRIM_UPPER:
|
||||
return 0, nil, false, nil, annotations.NewIncompatibleTypesInBinOpInfo("float", parser.ItemTypeStr[op], "float", pos)
|
||||
}
|
||||
}
|
||||
case hlhs == nil && hrhs != nil:
|
||||
|
|
@ -3223,7 +3556,7 @@ func vectorElemBinop(op parser.ItemType, lhs, rhs float64, hlhs, hrhs *histogram
|
|||
switch op {
|
||||
case parser.MUL:
|
||||
return 0, hrhs.Copy().Mul(lhs).Compact(0), true, nil, nil
|
||||
case parser.ADD, parser.SUB, parser.DIV, parser.POW, parser.MOD, parser.EQLC, parser.NEQ, parser.GTR, parser.LSS, parser.GTE, parser.LTE, parser.ATAN2:
|
||||
case parser.ADD, parser.SUB, parser.DIV, parser.POW, parser.MOD, parser.EQLC, parser.NEQ, parser.GTR, parser.TRIM_LOWER, parser.TRIM_UPPER, parser.LSS, parser.GTE, parser.LTE, parser.ATAN2:
|
||||
return 0, nil, false, nil, annotations.NewIncompatibleTypesInBinOpInfo("float", parser.ItemTypeStr[op], "histogram", pos)
|
||||
}
|
||||
}
|
||||
|
|
@ -3234,6 +3567,14 @@ func vectorElemBinop(op parser.ItemType, lhs, rhs float64, hlhs, hrhs *histogram
|
|||
return 0, hlhs.Copy().Mul(rhs).Compact(0), true, nil, nil
|
||||
case parser.DIV:
|
||||
return 0, hlhs.Copy().Div(rhs).Compact(0), true, nil, nil
|
||||
case parser.TRIM_UPPER:
|
||||
trimmedHist := hlhs.Copy()
|
||||
trimHistogram(trimmedHist, rhs, true)
|
||||
return 0, trimmedHist, true, nil, nil
|
||||
case parser.TRIM_LOWER:
|
||||
trimmedHist := hlhs.Copy()
|
||||
trimHistogram(trimmedHist, rhs, false)
|
||||
return 0, trimmedHist, true, nil, nil
|
||||
case parser.ADD, parser.SUB, parser.POW, parser.MOD, parser.EQLC, parser.NEQ, parser.GTR, parser.LSS, parser.GTE, parser.LTE, parser.ATAN2:
|
||||
return 0, nil, false, nil, annotations.NewIncompatibleTypesInBinOpInfo("histogram", parser.ItemTypeStr[op], "float", pos)
|
||||
}
|
||||
|
|
@ -3274,7 +3615,7 @@ func vectorElemBinop(op parser.ItemType, lhs, rhs float64, hlhs, hrhs *histogram
|
|||
case parser.NEQ:
|
||||
// This operation expects that both histograms are compacted.
|
||||
return 0, hlhs, !hlhs.Equals(hrhs), nil, nil
|
||||
case parser.MUL, parser.DIV, parser.POW, parser.MOD, parser.GTR, parser.LSS, parser.GTE, parser.LTE, parser.ATAN2:
|
||||
case parser.MUL, parser.DIV, parser.POW, parser.MOD, parser.GTR, parser.LSS, parser.GTE, parser.LTE, parser.ATAN2, parser.TRIM_LOWER, parser.TRIM_UPPER:
|
||||
return 0, nil, false, nil, annotations.NewIncompatibleTypesInBinOpInfo("histogram", parser.ItemTypeStr[op], "histogram", pos)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,6 +98,8 @@ EQLC
|
|||
EQL_REGEX
|
||||
GTE
|
||||
GTR
|
||||
TRIM_UPPER
|
||||
TRIM_LOWER
|
||||
LAND
|
||||
LOR
|
||||
LSS
|
||||
|
|
@ -200,7 +202,7 @@ START_METRIC_SELECTOR
|
|||
// Operators are listed with increasing precedence.
|
||||
%left LOR
|
||||
%left LAND LUNLESS
|
||||
%left EQLC GTE GTR LSS LTE NEQ
|
||||
%left EQLC GTE GTR LSS LTE NEQ TRIM_UPPER TRIM_LOWER
|
||||
%left ADD SUB
|
||||
%left MUL DIV MOD ATAN2
|
||||
%right POW
|
||||
|
|
@ -291,6 +293,8 @@ binary_expr : expr ADD bin_modifier expr { $$ = yylex.(*parser).newBinar
|
|||
| expr EQLC bin_modifier expr { $$ = yylex.(*parser).newBinaryExpression($1, $2, $3, $4) }
|
||||
| expr GTE bin_modifier expr { $$ = yylex.(*parser).newBinaryExpression($1, $2, $3, $4) }
|
||||
| expr GTR bin_modifier expr { $$ = yylex.(*parser).newBinaryExpression($1, $2, $3, $4) }
|
||||
| expr TRIM_UPPER bin_modifier expr { $$ = yylex.(*parser).newBinaryExpression($1, $2, $3, $4) }
|
||||
| expr TRIM_LOWER bin_modifier expr { $$ = yylex.(*parser).newBinaryExpression($1, $2, $3, $4) }
|
||||
| expr LAND bin_modifier expr { $$ = yylex.(*parser).newBinaryExpression($1, $2, $3, $4) }
|
||||
| expr LOR bin_modifier expr { $$ = yylex.(*parser).newBinaryExpression($1, $2, $3, $4) }
|
||||
| expr LSS bin_modifier expr { $$ = yylex.(*parser).newBinaryExpression($1, $2, $3, $4) }
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -189,21 +189,23 @@ var ItemTypeStr = map[ItemType]string{
|
|||
TIMES: "x",
|
||||
SPACE: "<space>",
|
||||
|
||||
SUB: "-",
|
||||
ADD: "+",
|
||||
MUL: "*",
|
||||
MOD: "%",
|
||||
DIV: "/",
|
||||
EQLC: "==",
|
||||
NEQ: "!=",
|
||||
LTE: "<=",
|
||||
LSS: "<",
|
||||
GTE: ">=",
|
||||
GTR: ">",
|
||||
EQL_REGEX: "=~",
|
||||
NEQ_REGEX: "!~",
|
||||
POW: "^",
|
||||
AT: "@",
|
||||
SUB: "-",
|
||||
ADD: "+",
|
||||
MUL: "*",
|
||||
MOD: "%",
|
||||
DIV: "/",
|
||||
EQLC: "==",
|
||||
NEQ: "!=",
|
||||
LTE: "<=",
|
||||
LSS: "<",
|
||||
GTE: ">=",
|
||||
GTR: ">",
|
||||
TRIM_UPPER: "</",
|
||||
TRIM_LOWER: ">/",
|
||||
EQL_REGEX: "=~",
|
||||
NEQ_REGEX: "!~",
|
||||
POW: "^",
|
||||
AT: "@",
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
|
@ -446,6 +448,9 @@ func lexStatements(l *Lexer) stateFn {
|
|||
if t := l.peek(); t == '=' {
|
||||
l.next()
|
||||
l.emit(LTE)
|
||||
} else if t := l.peek(); t == '/' {
|
||||
l.next()
|
||||
l.emit(TRIM_UPPER)
|
||||
} else {
|
||||
l.emit(LSS)
|
||||
}
|
||||
|
|
@ -453,6 +458,9 @@ func lexStatements(l *Lexer) stateFn {
|
|||
if t := l.peek(); t == '=' {
|
||||
l.next()
|
||||
l.emit(GTE)
|
||||
} else if t := l.peek(); t == '/' {
|
||||
l.next()
|
||||
l.emit(TRIM_LOWER)
|
||||
} else {
|
||||
l.emit(GTR)
|
||||
}
|
||||
|
|
|
|||
353
promql/promqltest/testdata/native_histograms.test
vendored
353
promql/promqltest/testdata/native_histograms.test
vendored
|
|
@ -2020,3 +2020,356 @@ eval instant at 1m irate(nhcb_add_bucket[2m]) * 60
|
|||
expect no_warn
|
||||
expect no_info
|
||||
{} {{schema:-53 sum:56 count:15 custom_values:[2 3 4 6] buckets:[1 0 1 5 8] counter_reset_hint:gauge}}
|
||||
|
||||
|
||||
# Test native histogram with trim operators ("</": TRIM_UPPER, ">/": TRIM_LOWER)
|
||||
load 1m
|
||||
h_test {{schema:0 sum:123.75 count:34 z_bucket:1 z_bucket_w:0.001 buckets:[2 4 8 16] n_buckets:[1 2]}}
|
||||
|
||||
eval instant at 1m h_test >/ -Inf
|
||||
h_test {{schema:0 sum:123.75 count:34 z_bucket:1 z_bucket_w:0.001 buckets:[2 4 8 16] n_buckets:[1 2]}}
|
||||
|
||||
eval instant at 1m h_test </ +Inf
|
||||
h_test {{schema:0 sum:123.75 count:34 z_bucket:1 z_bucket_w:0.001 buckets:[2 4 8 16] n_buckets:[1 2]}}
|
||||
|
||||
eval instant at 1m h_test >/ +Inf
|
||||
h_test {{schema:0 z_bucket_w:0.001}}
|
||||
|
||||
eval instant at 1m h_test </ -Inf
|
||||
h_test {{schema:0 z_bucket_w:0.001}}
|
||||
|
||||
eval instant at 1m h_test >/ 0
|
||||
h_test {{schema:0 sum:120.20840280171308 count:30.5 z_bucket:0.5 z_bucket_w:0.001 buckets:[2 4 8 16]}}
|
||||
|
||||
eval instant at 1m h_test </ 0
|
||||
h_test {{schema:0 sum:-3.53578390593273768 count:3.5 z_bucket:0.5 z_bucket_w:0.001 n_buckets:[1 2]}}
|
||||
|
||||
# Exponential buckets: trim uses exponential interpolation if cutoff is inside a bucket
|
||||
# Trim at sqrt(2) yields half the area between 1 and 2 boundaries.
|
||||
eval instant at 1m h_test </ 1.4142135624
|
||||
h_test {{count:8 sum:0.2570938865989847 z_bucket:1 z_bucket_w:0.001 buckets:[2 2] n_buckets:[1 2]}}
|
||||
|
||||
eval instant at 1m h_test >/ 1.4142135624
|
||||
h_test {{count:26 sum:116.50067065070982 z_bucket_w:0.001 buckets:[0 2 8 16]}}
|
||||
|
||||
|
||||
load 1m
|
||||
h_test_2 {{schema:2 sum:12.8286080906 count:28 z_bucket:1 z_bucket_w:0.001 buckets:[1 2 4 7 3] n_buckets:[1 5 3 1]}}
|
||||
|
||||
eval instant at 1m h_test_2 </ 1.13
|
||||
h_test_2 {{schema:2 count:13.410582181123704 sum:-9.385798726068233 z_bucket:1 z_bucket_w:0.001 buckets:[1 1.410582181123704] n_buckets:[1 5 3 1]}}
|
||||
|
||||
eval instant at 1m h_test_2 >/ 1.13
|
||||
h_test_2 {{schema:2 count:14.589417818876296 sum:22.168126492693734 z_bucket_w:0.001 offset:1 buckets:[0.589417818876296 4 7 3]}}
|
||||
|
||||
eval instant at 1m h_test_2 >/ -1.3
|
||||
h_test_2 {{schema:2 count:25.54213947904476 sum:16.29588491217537 z_bucket:1 z_bucket_w:0.001 buckets:[1 2 4 7 3] n_buckets:[1 5 1.54213947904476]}}
|
||||
|
||||
eval instant at 1m h_test_2 </ -1.3
|
||||
h_test_2 {{schema:2 count:2.45786052095524 sum:-3.5189307983595066 z_bucket_w:0.001 n_offset:2 n_buckets:[1.45786052095524 1]}}
|
||||
|
||||
# Exponential buckets: trim on bucket boundary uses no interpolation.
|
||||
eval instant at 1m h_test </ 2
|
||||
h_test{} {{count:10 sum:3.5355339059327373 z_bucket:1 z_bucket_w:0.001 buckets:[2 4] n_buckets:[1 2]}}
|
||||
|
||||
eval instant at 1m h_test >/ 2
|
||||
h_test{} {{count:24 sum:113.13708498984761 z_bucket_w:0.001 offset:2 buckets:[8 16]}}
|
||||
|
||||
eval instant at 1m h_test >/ -1
|
||||
h_test{} {{count:32 sum:119.50104602052653 z_bucket:1 z_bucket_w:0.001 buckets:[2 4 8 16] n_buckets:[1]}}
|
||||
|
||||
eval instant at 1m h_test </ -1
|
||||
h_test{} {{count:2 sum:-2.8284271247461903 z_bucket_w:0.001 n_offset:1 n_buckets:[2]}}
|
||||
|
||||
|
||||
# Exponential buckets: trim zero bucket that is positive-biased (because of the presence of positive buckets).
|
||||
load 1m
|
||||
h_positive_buckets {{schema:0 sum:8.0210678118654755 count:12 z_bucket:2 z_bucket_w:0.5 buckets:[10]}}
|
||||
|
||||
eval instant at 1m h_positive_buckets >/ 0.5
|
||||
h_positive_buckets {{schema:0 count:10 sum:7.0710678118654755 z_bucket:0 z_bucket_w:0.5 buckets:[10]}}
|
||||
|
||||
eval instant at 1m h_positive_buckets >/ 0.1
|
||||
h_positive_buckets {{schema:0 count:11.6 sum:7.551067811865476 z_bucket:1.6 z_bucket_w:0.5 buckets:[10]}}
|
||||
|
||||
eval instant at 1m h_positive_buckets >/ 0
|
||||
h_positive_buckets {{schema:0 sum:8.0210678118654755 count:12 z_bucket:2 z_bucket_w:0.5 buckets:[10]}}
|
||||
|
||||
eval instant at 1m h_positive_buckets </ 0.5
|
||||
h_positive_buckets {{schema:0 count:2 sum:0.5 z_bucket:2 z_bucket_w:0.5}}
|
||||
|
||||
eval instant at 1m h_positive_buckets </ 0.1
|
||||
h_positive_buckets {{schema:0 count:0.4 sum:0.02 z_bucket:0.4 z_bucket_w:0.5}}
|
||||
|
||||
eval instant at 1m h_positive_buckets </ 0
|
||||
h_positive_buckets {{schema:0 z_bucket_w:0.5}}
|
||||
|
||||
|
||||
# Exponential buckets: trim zero bucket that is negative-biased (because of the presence of negative buckets).
|
||||
load 1m
|
||||
h_negative_buckets {{schema:0 sum:-8.0210678118654755 count:12 z_bucket:2 z_bucket_w:0.5 n_buckets:[10]}}
|
||||
|
||||
eval instant at 1m h_negative_buckets </ -0.5
|
||||
h_negative_buckets {{schema:0 count:10 sum:-7.0710678118654755 z_bucket:0 z_bucket_w:0.5 n_buckets:[10]}}
|
||||
|
||||
eval instant at 1m h_negative_buckets </ -0.1
|
||||
h_negative_buckets {{schema:0 count:11.6 sum:-7.551067811865476 z_bucket:1.6 z_bucket_w:0.5 n_buckets:[10]}}
|
||||
|
||||
eval instant at 1m h_negative_buckets </ 0
|
||||
h_negative_buckets {{schema:0 sum:-8.0210678118654755 count:12 z_bucket:2 z_bucket_w:0.5 n_buckets:[10]}}
|
||||
|
||||
eval instant at 1m h_negative_buckets >/ -0.5
|
||||
h_negative_buckets {{schema:0 count:2 sum:-0.5 z_bucket:2 z_bucket_w:0.5}}
|
||||
|
||||
eval instant at 1m h_negative_buckets >/ -0.1
|
||||
h_negative_buckets {{schema:0 count:0.4 sum:-0.02 z_bucket:0.4 z_bucket_w:0.5}}
|
||||
|
||||
eval instant at 1m h_negative_buckets >/ 0
|
||||
h_negative_buckets {{schema:0 z_bucket_w:0.5}}
|
||||
|
||||
|
||||
# Exponential buckets: trim zero bucket when there are no other buckets.
|
||||
load 1m
|
||||
zero_bucket_only {{schema:0 count:5 sum:0 z_bucket:5 z_bucket_w:0.1 }}
|
||||
|
||||
eval instant at 1m zero_bucket_only >/ 0.1
|
||||
zero_bucket_only {{schema:0 count:0 sum:0 z_bucket:0 z_bucket_w:0.1 }}
|
||||
|
||||
eval instant at 1m zero_bucket_only </ 0.1
|
||||
zero_bucket_only {{schema:0 count:5 sum:0 z_bucket:5 z_bucket_w:0.1 }}
|
||||
|
||||
eval instant at 1m zero_bucket_only >/ 0.05
|
||||
zero_bucket_only {{schema:0 count:1.25 sum:0.09375 z_bucket:1.25 z_bucket_w:0.1 }}
|
||||
|
||||
eval instant at 1m zero_bucket_only </ 0.05
|
||||
zero_bucket_only {{schema:0 count:3.75 sum:-0.09375 z_bucket:3.75 z_bucket_w:0.1 }}
|
||||
|
||||
eval instant at 1m zero_bucket_only >/ 0
|
||||
zero_bucket_only {{schema:0 count:2.5 sum:0.125 z_bucket:2.5 z_bucket_w:0.1 }}
|
||||
|
||||
eval instant at 1m zero_bucket_only </ 0
|
||||
zero_bucket_only {{schema:0 count:2.5 sum:-0.125 z_bucket:2.5 z_bucket_w:0.1 }}
|
||||
|
||||
eval instant at 1m zero_bucket_only >/ -0.05
|
||||
zero_bucket_only {{schema:0 count:3.75 sum:0.09375 z_bucket:3.75 z_bucket_w:0.1 }}
|
||||
|
||||
eval instant at 1m zero_bucket_only </ -0.05
|
||||
zero_bucket_only {{schema:0 count:1.25 sum:-0.09375 z_bucket:1.25 z_bucket_w:0.1 }}
|
||||
|
||||
eval instant at 1m zero_bucket_only </ -0.1
|
||||
zero_bucket_only {{schema:0 count:0 sum:0 z_bucket:0 z_bucket_w:0.1 }}
|
||||
|
||||
eval instant at 1m zero_bucket_only >/ -0.1
|
||||
zero_bucket_only {{schema:0 count:5 sum:0 z_bucket:5 z_bucket_w:0.1 }}
|
||||
|
||||
|
||||
load 1m
|
||||
cbh {{schema:-53 sum:172.5 count:15 custom_values:[5 10 15 20] buckets:[1 6 4 3 1]}}
|
||||
|
||||
# Custom buckets: trim on bucket boundary without interpolation
|
||||
eval instant at 1m cbh </ 15
|
||||
cbh{} {{schema:-53 count:11 sum:97.5 custom_values:[5 10 15 20] buckets:[1 6 4]}}
|
||||
|
||||
eval instant at 1m cbh >/ 15
|
||||
cbh{} {{schema:-53 count:4 sum:72.5 custom_values:[5 10 15 20] offset:3 buckets:[3 1]}}
|
||||
|
||||
# Custom buckets: trim uses linear interpolation if cutoff is inside a bucket
|
||||
eval instant at 1m cbh </ 13
|
||||
cbh{} {{schema:-53 count:9.4 sum:75.1 custom_values:[5 10 15 20] buckets:[1 6 2.4]}}
|
||||
|
||||
eval instant at 1m cbh >/ 13
|
||||
cbh{} {{schema:-53 count:5.6 sum:94.9 custom_values:[5 10 15 20] offset:2 buckets:[1.6 3 1]}}
|
||||
|
||||
eval instant at 1m cbh </ 7.5
|
||||
cbh{} {{schema:-53 count:4 sum:21.25 custom_values:[5 10 15 20] buckets:[1 3]}}
|
||||
|
||||
# Custom buckets: trim drops +Inf bucket entirely even if cutoff is above its lower bound
|
||||
eval instant at 1m cbh </ 50
|
||||
cbh{} {{schema:-53 count:14 sum:150.0 custom_values:[5 10 15 20] buckets:[1 6 4 3]}}
|
||||
|
||||
eval instant at 1m cbh </ -Inf
|
||||
cbh{} {{schema:-53 custom_values:[5 10 15 20]}}
|
||||
|
||||
eval instant at 1m cbh >/ +Inf
|
||||
cbh{} {{schema:-53 custom_values:[5 10 15 20]}}
|
||||
|
||||
eval instant at 1m cbh </ +Inf
|
||||
cbh {{schema:-53 sum:172.5 count:15 custom_values:[5 10 15 20] buckets:[1 6 4 3 1]}}
|
||||
|
||||
eval instant at 1m cbh >/ -Inf
|
||||
cbh {{schema:-53 sum:172.5 count:15 custom_values:[5 10 15 20] buckets:[1 6 4 3 1]}}
|
||||
|
||||
# Noop
|
||||
eval instant at 1m cbh >/ 0
|
||||
cbh {{schema:-53 sum:172.5 count:15 custom_values:[5 10 15 20] buckets:[1 6 4 3 1]}}
|
||||
|
||||
eval instant at 1m cbh </ 0
|
||||
cbh {{schema:-53 custom_values:[5 10 15 20]}}
|
||||
|
||||
|
||||
# Custom buckets: negative values
|
||||
load 1m
|
||||
cbh_has_neg {{schema:-53 sum:172.5 count:15 custom_values:[-10 5 10 15 20] buckets:[2 1 6 4 3 1]}}
|
||||
|
||||
eval instant at 1m cbh_has_neg </ 2
|
||||
cbh_has_neg{} {{schema:-53 count:2.8 sum:-23.2 custom_values:[-10 5 10 15 20] buckets:[2 0.8]}}
|
||||
|
||||
eval instant at 1m cbh_has_neg </ -4
|
||||
cbh_has_neg{} {{schema:-53 count:2.4 sum:-22.8 custom_values:[-10 5 10 15 20] buckets:[2 0.4]}}
|
||||
|
||||
eval instant at 1m cbh_has_neg </ -15
|
||||
cbh_has_neg{} {{schema:-53 count:2 sum:-30 custom_values:[-10 5 10 15 20] buckets:[2]}}
|
||||
|
||||
|
||||
load 1m
|
||||
zero_bucket {{schema:0 sum:-6.75 z_bucket:5 z_bucket_w:0.01 buckets:[2 3] n_buckets:[1 2 3]}}
|
||||
|
||||
# Zero Bucket Edge Case: Interpolation Around Zero
|
||||
eval instant at 1m zero_bucket </ -0.005
|
||||
zero_bucket{} {{count:7.25 sum:-12.03019028017131 z_bucket:1.25 z_bucket_w:0.01 n_buckets:[1 2 3]}}
|
||||
|
||||
eval instant at 1m zero_bucket >/ 0
|
||||
zero_bucket{} {{count:7.5 sum:5.669354249492381 z_bucket:2.5 z_bucket_w:0.01 buckets:[2 3]}}
|
||||
|
||||
|
||||
load 1m
|
||||
cbh_one_bucket {{schema:-53 sum:100.0 count:100 buckets:[100]}}
|
||||
|
||||
# Skip [-Inf; +Inf] bucket (100).
|
||||
eval instant at 1m cbh_one_bucket </ 10.0
|
||||
cbh_one_bucket {{schema:-53 sum:0.0 count:0 buckets:[0]}}
|
||||
|
||||
# Skip [-Inf; +Inf] bucket (100).
|
||||
eval instant at 1m cbh_one_bucket >/ 10.0
|
||||
cbh_one_bucket {{schema:-53 sum:0.0 count:0 buckets:[0]}}
|
||||
|
||||
# Keep [-Inf; +Inf] bucket (100).
|
||||
eval instant at 1m cbh_one_bucket </ +Inf
|
||||
cbh_one_bucket {{schema:-53 sum:100 count:100 buckets:[100]}}
|
||||
|
||||
# Skip [-Inf; +Inf] bucket (100).
|
||||
eval instant at 1m cbh_one_bucket >/ +Inf
|
||||
cbh_one_bucket {{schema:-53 sum:0 count:0 buckets:[0]}}
|
||||
|
||||
# Keep [-Inf; +Inf] bucket (100).
|
||||
eval instant at 1m cbh_one_bucket >/ -Inf
|
||||
cbh_one_bucket {{schema:-53 sum:100 count:100 buckets:[100]}}
|
||||
|
||||
# Skip [-Inf; +Inf] bucket (100).
|
||||
eval instant at 1m cbh_one_bucket </ -Inf
|
||||
cbh_one_bucket {{schema:-53 sum:0 count:0 buckets:[0]}}
|
||||
|
||||
|
||||
load 1m
|
||||
cbh_two_buckets_split_at_zero {{schema:-53 sum:33.0 count:100 custom_values:[0] buckets:[1 100]}}
|
||||
|
||||
# Skip (0; +Inf] bucket (100).
|
||||
eval instant at 1m cbh_two_buckets_split_at_zero </ 10.0
|
||||
cbh_two_buckets_split_at_zero {{schema:-53 sum:0.0 count:1 custom_values:[0] buckets:[1 0]}}
|
||||
|
||||
# Skip (0; +Inf] bucket (100).
|
||||
eval instant at 1m cbh_two_buckets_split_at_zero </ 0.0
|
||||
cbh_two_buckets_split_at_zero {{schema:-53 sum:0.0 count:1 custom_values:[0] buckets:[1 0]}}
|
||||
|
||||
# Skip both buckets (1, 100).
|
||||
eval instant at 1m cbh_two_buckets_split_at_zero </ -10.0
|
||||
cbh_two_buckets_split_at_zero {{schema:-53 sum:-10.0 count:1 custom_values:[0] buckets:[1 0]}}
|
||||
|
||||
# Skip [-Inf, 0] bucket (1).
|
||||
eval instant at 1m cbh_two_buckets_split_at_zero >/ -10.0
|
||||
cbh_two_buckets_split_at_zero {{schema:-53 sum:0.0 count:100 custom_values:[0] buckets:[0 100]}}
|
||||
|
||||
# Skip [-Inf, 0] bucket (1).
|
||||
eval instant at 1m cbh_two_buckets_split_at_zero >/ 0.0
|
||||
cbh_two_buckets_split_at_zero {{schema:-53 sum:0.0 count:100 custom_values:[0] buckets:[0 100]}}
|
||||
|
||||
# Skip first bucket.
|
||||
eval instant at 1m cbh_two_buckets_split_at_zero >/ 10.0
|
||||
cbh_two_buckets_split_at_zero {{schema:-53 sum:1000.0 count:100 custom_values:[0] buckets:[0 100]}}
|
||||
|
||||
|
||||
load 1m
|
||||
cbh_two_buckets_split_at_positive {{schema:-53 sum:33 count:101 custom_values:[5] buckets:[1 100]}}
|
||||
|
||||
# Skip (5, +Inf] bucket (100).
|
||||
eval instant at 1m cbh_two_buckets_split_at_positive </ 10.0
|
||||
cbh_two_buckets_split_at_positive {{schema:-53 sum:2.5 count:1 custom_values:[5] buckets:[1 0]}}
|
||||
|
||||
# Skip (5, +Inf] bucket (100) and 3/5 of [0, 5] bucket (0.6 * 3.5).
|
||||
eval instant at 1m cbh_two_buckets_split_at_positive </ 2.0
|
||||
cbh_two_buckets_split_at_positive {{schema:-53 sum:0.4 count:0.4 custom_values:[5] buckets:[0.4 0]}}
|
||||
|
||||
# Skip both buckets (1 and 100).
|
||||
eval instant at 1m cbh_two_buckets_split_at_positive </ 0.0
|
||||
cbh_two_buckets_split_at_positive {{schema:-53 custom_values:[5]}}
|
||||
|
||||
# Skip both buckets (1 and 100).
|
||||
eval instant at 1m cbh_two_buckets_split_at_positive </ -10.0
|
||||
cbh_two_buckets_split_at_positive {{schema:-53 sum:0.0 count:0 custom_values:[5] buckets:[0 0]}}
|
||||
|
||||
# Skip [0, 5] bucket (1).
|
||||
eval instant at 1m cbh_two_buckets_split_at_positive >/ -10.0
|
||||
cbh_two_buckets_split_at_positive {{schema:-53 sum:500.0 count:100 custom_values:[5] buckets:[0 100]}}
|
||||
|
||||
# Noop.
|
||||
eval instant at 1m cbh_two_buckets_split_at_positive >/ 0.0
|
||||
cbh_two_buckets_split_at_positive {{schema:-53 sum:33.0 count:101 custom_values:[5] buckets:[1 100]}}
|
||||
|
||||
# Keep (5, +Inf] bucket (100) and 3/5 of [0, 5] bucket (0.6 * 3.5).
|
||||
eval instant at 1m cbh_two_buckets_split_at_positive >/ 2.0
|
||||
cbh_two_buckets_split_at_positive {{schema:-53 sum:502.1 count:100.6 custom_values:[5] buckets:[0.6 100]}}
|
||||
|
||||
# Skip first bucket.
|
||||
eval instant at 1m cbh_two_buckets_split_at_positive >/ 10.0
|
||||
cbh_two_buckets_split_at_positive {{schema:-53 sum:1000.0 count:100 custom_values:[5] buckets:[0 100]}}
|
||||
|
||||
|
||||
load 1m
|
||||
cbh_two_buckets_split_at_negative {{schema:-53 sum:33 count:101 custom_values:[-5] buckets:[1 100]}}
|
||||
|
||||
# Skip (-5, +Inf] bucket (100).
|
||||
eval instant at 1m cbh_two_buckets_split_at_negative </ 10.0
|
||||
cbh_two_buckets_split_at_negative {{schema:-53 sum:-5.0 count:1 custom_values:[-5] buckets:[1 0]}}
|
||||
|
||||
# Skip (-5, +Inf] bucket (100).
|
||||
eval instant at 1m cbh_two_buckets_split_at_negative </ 0.0
|
||||
cbh_two_buckets_split_at_negative {{schema:-53 sum:-5 count:1 custom_values:[-5] buckets:[1 0]}}
|
||||
|
||||
# Skip (-5; +Inf] bucket (100).
|
||||
eval instant at 1m cbh_two_buckets_split_at_negative </ -2.0
|
||||
cbh_two_buckets_split_at_negative {{schema:-53 sum:-5.0 count:1 custom_values:[-5] buckets:[1 0]}}
|
||||
|
||||
# Skip (-5, +Inf] bucket (100).
|
||||
eval instant at 1m cbh_two_buckets_split_at_negative </ -10.0
|
||||
cbh_two_buckets_split_at_negative {{schema:-53 sum:-10.0 count:1 custom_values:[-5] buckets:[1 0]}}
|
||||
|
||||
# Skip [-Inf, -5] bucket (1).
|
||||
eval instant at 1m cbh_two_buckets_split_at_negative >/ -10.0
|
||||
cbh_two_buckets_split_at_negative {{schema:-53 sum:-500 count:100 custom_values:[-5] buckets:[0 100]}}
|
||||
|
||||
# Skip [-Inf, -5] bucket (1).
|
||||
eval instant at 1m cbh_two_buckets_split_at_negative >/ -2.0
|
||||
cbh_two_buckets_split_at_negative {{schema:-53 sum:-200 count:100 custom_values:[-5] buckets:[0 100]}}
|
||||
|
||||
# Skip [-Inf, -5] bucket (1).
|
||||
eval instant at 1m cbh_two_buckets_split_at_negative >/ 0.0
|
||||
cbh_two_buckets_split_at_negative {{schema:-53 sum:0.0 count:100 custom_values:[-5] buckets:[0 100]}}
|
||||
|
||||
# Skip [-Inf, -5] bucket (1).
|
||||
eval instant at 1m cbh_two_buckets_split_at_negative >/ 10.0
|
||||
cbh_two_buckets_split_at_negative {{schema:-53 sum:1000.0 count:100 custom_values:[-5] buckets:[0 100]}}
|
||||
|
||||
|
||||
load 1m
|
||||
cbh_for_join{label="a"} {{schema:-53 sum:33 count:101 custom_values:[5] buckets:[1 100]}}
|
||||
cbh_for_join{label="b"} {{schema:-53 sum:66 count:202 custom_values:[5] buckets:[2 200]}}
|
||||
cbh_for_join{label="c"} {{schema:-53 sum:99 count:303 custom_values:[5] buckets:[3 300]}}
|
||||
float_for_join{label="a"} 1
|
||||
float_for_join{label="b"} 4
|
||||
|
||||
eval instant at 1m cbh_for_join >/ on (label) float_for_join
|
||||
{label="a"} {{schema:-53 count:100.8 sum:502.4 custom_values:[5] buckets:[0.8 100]}}
|
||||
{label="b"} {{schema:-53 count:200.4 sum:1001.8 custom_values:[5] buckets:[0.4 200]}}
|
||||
|
||||
|
||||
clear
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ export enum binaryOperatorType {
|
|||
neq = "!=",
|
||||
gtr = ">",
|
||||
lss = "<",
|
||||
trimUpper = "</",
|
||||
trimLower = ">/",
|
||||
gte = ">=",
|
||||
lte = "<=",
|
||||
and = "and",
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ const binOpPrecedence = {
|
|||
[binaryOperatorType.lss]: 4,
|
||||
[binaryOperatorType.gte]: 4,
|
||||
[binaryOperatorType.lte]: 4,
|
||||
[binaryOperatorType.trimLower]: 4,
|
||||
[binaryOperatorType.trimUpper]: 4,
|
||||
[binaryOperatorType.and]: 5,
|
||||
[binaryOperatorType.or]: 6,
|
||||
[binaryOperatorType.unless]: 5,
|
||||
|
|
|
|||
|
|
@ -60,6 +60,8 @@ import {
|
|||
LimitK,
|
||||
LimitRatio,
|
||||
CountValues,
|
||||
TrimLower,
|
||||
TrimUpper,
|
||||
} from '@prometheus-io/lezer-promql';
|
||||
import { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete';
|
||||
import { EditorState } from '@codemirror/state';
|
||||
|
|
@ -579,6 +581,8 @@ export function analyzeCompletion(state: EditorState, node: SyntaxNode, pos: num
|
|||
case Eql:
|
||||
case Gte:
|
||||
case Gtr:
|
||||
case TrimLower:
|
||||
case TrimUpper:
|
||||
case Lte:
|
||||
case Lss:
|
||||
case And:
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ export const binOpTerms = [
|
|||
{ label: '>=' },
|
||||
{ label: '>' },
|
||||
{ label: '<' },
|
||||
{ label: '</' },
|
||||
{ label: '>/' },
|
||||
{ label: '<=' },
|
||||
{ label: '!=' },
|
||||
{ label: 'atan2' },
|
||||
|
|
|
|||
|
|
@ -49,12 +49,14 @@ import {
|
|||
StepInvariantExpr,
|
||||
SubqueryExpr,
|
||||
Topk,
|
||||
TrimLower,
|
||||
TrimUpper,
|
||||
UnaryExpr,
|
||||
Unless,
|
||||
UnquotedLabelMatcher,
|
||||
VectorSelector,
|
||||
} from '@prometheus-io/lezer-promql';
|
||||
import { containsAtLeastOneChild } from './path-finder';
|
||||
import { containsAtLeastOneChild, containsChild } from './path-finder';
|
||||
import { getType } from './type';
|
||||
import { buildLabelMatchers } from './matcher';
|
||||
import { EditorState } from '@codemirror/state';
|
||||
|
|
@ -215,6 +217,8 @@ export class Parser {
|
|||
const rt = this.checkAST(rExpr);
|
||||
const boolModifierUsed = node.getChild(BoolModifier);
|
||||
const isComparisonOperator = containsAtLeastOneChild(node, Eql, Neq, Lte, Lss, Gte, Gtr);
|
||||
const isTrimLowerOperator = containsChild(node, TrimLower);
|
||||
const isTrimUpperOperator = containsChild(node, TrimUpper);
|
||||
const isSetOperator = containsAtLeastOneChild(node, And, Or, Unless);
|
||||
|
||||
// BOOL modifier check
|
||||
|
|
@ -223,8 +227,14 @@ export class Parser {
|
|||
this.addDiagnostic(node, 'bool modifier can only be used on comparison operators');
|
||||
}
|
||||
} else {
|
||||
if (isComparisonOperator && lt === ValueType.scalar && rt === ValueType.scalar) {
|
||||
this.addDiagnostic(node, 'comparisons between scalars must use BOOL modifier');
|
||||
if (lt === ValueType.scalar && rt === ValueType.scalar) {
|
||||
if (isComparisonOperator) {
|
||||
this.addDiagnostic(node, 'comparisons between scalars must use BOOL modifier');
|
||||
} else if (isTrimLowerOperator) {
|
||||
this.addDiagnostic(node, 'operator ">/" not allowed for Scalar operations');
|
||||
} else if (isTrimUpperOperator) {
|
||||
this.addDiagnostic(node, 'operator "</" not allowed for Scalar operations');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -88,6 +88,8 @@ BinaryExpr {
|
|||
expr !eql Eql binModifiers expr |
|
||||
expr !eql Gte binModifiers expr |
|
||||
expr !eql Gtr binModifiers expr |
|
||||
expr !eql TrimUpper binModifiers expr |
|
||||
expr !eql TrimLower binModifiers expr |
|
||||
expr !eql Lte binModifiers expr |
|
||||
expr !eql Lss binModifiers expr |
|
||||
expr !eql Neq binModifiers expr |
|
||||
|
|
@ -338,6 +340,8 @@ NumberDurationLiteralInDurationContext {
|
|||
Lss { "<" }
|
||||
Gte { ">=" }
|
||||
Gtr { ">" }
|
||||
TrimUpper { "</" }
|
||||
TrimLower { ">/" }
|
||||
EqlRegex { "=~" }
|
||||
EqlSingle { "=" }
|
||||
NeqRegex { "!~" }
|
||||
|
|
|
|||
|
|
@ -716,3 +716,31 @@ rate(caddy_http_requests_total[5m] smoothed)
|
|||
|
||||
==>
|
||||
PromQL(FunctionCall(FunctionIdentifier(Rate),FunctionCallBody(SmoothedExpr(MatrixSelector(VectorSelector(Identifier),NumberDurationLiteralInDurationContext),Smoothed))))
|
||||
|
||||
# TrimUpper binary operator
|
||||
|
||||
metric1 </ metric2
|
||||
|
||||
==>
|
||||
|
||||
PromQL(
|
||||
BinaryExpr(
|
||||
VectorSelector(Identifier),
|
||||
TrimUpper,
|
||||
VectorSelector(Identifier)
|
||||
)
|
||||
)
|
||||
|
||||
# TrimLower binary operator
|
||||
|
||||
metric1 >/ metric2
|
||||
|
||||
==>
|
||||
|
||||
PromQL(
|
||||
BinaryExpr(
|
||||
VectorSelector(Identifier),
|
||||
TrimLower,
|
||||
VectorSelector(Identifier)
|
||||
)
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue