Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

promql: Fix limiting of extrapolation to negative values #13725

Merged
merged 1 commit into from Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
33 changes: 16 additions & 17 deletions promql/functions.go
Expand Up @@ -128,37 +128,36 @@ func extrapolatedRate(vals []parser.Value, args parser.Expressions, enh *EvalNod
sampledInterval := float64(lastT-firstT) / 1000
averageDurationBetweenSamples := sampledInterval / float64(numSamplesMinusOne)

// TODO(beorn7): Do this for histograms, too.
// If the first/last samples are close to the boundaries of the range,
// extrapolate the result. This is as we expect that another sample
// will exist given the spacing between samples we've seen thus far,
// with an allowance for noise.
extrapolationThreshold := averageDurationBetweenSamples * 1.1
extrapolateToInterval := sampledInterval

if durationToStart >= extrapolationThreshold {
durationToStart = averageDurationBetweenSamples / 2
}
if isCounter && resultFloat > 0 && len(samples.Floats) > 0 && samples.Floats[0].F >= 0 {
// Counters cannot be negative. If we have any slope at all
// (i.e. resultFloat went up), we can extrapolate the zero point
// of the counter. If the duration to the zero point is shorter
// than the durationToStart, we take the zero point as the start
// of the series, thereby avoiding extrapolation to negative
// counter values.
// TODO(beorn7): Do this for histograms, too.
durationToZero := sampledInterval * (samples.Floats[0].F / resultFloat)
if durationToZero < durationToStart {
durationToStart = durationToZero
}
}
extrapolateToInterval += durationToStart

// If the first/last samples are close to the boundaries of the range,
// extrapolate the result. This is as we expect that another sample
// will exist given the spacing between samples we've seen thus far,
// with an allowance for noise.
extrapolationThreshold := averageDurationBetweenSamples * 1.1
extrapolateToInterval := sampledInterval

if durationToStart < extrapolationThreshold {
extrapolateToInterval += durationToStart
} else {
extrapolateToInterval += averageDurationBetweenSamples / 2
}
if durationToEnd < extrapolationThreshold {
extrapolateToInterval += durationToEnd
} else {
extrapolateToInterval += averageDurationBetweenSamples / 2
if durationToEnd >= extrapolationThreshold {
durationToEnd = averageDurationBetweenSamples / 2
}
extrapolateToInterval += durationToEnd

factor := extrapolateToInterval / sampledInterval
if isRate {
factor /= ms.Range.Seconds()
Expand Down
31 changes: 23 additions & 8 deletions promql/testdata/functions.test
Expand Up @@ -71,15 +71,28 @@ clear
load 5m
http_requests{path="/foo"} 0+10x10
http_requests{path="/bar"} 0+10x5 0+10x5
http_requests{path="/dings"} 10+10x10
http_requests{path="/bumms"} 1+10x10

# Tests for increase().
eval instant at 50m increase(http_requests[50m])
{path="/foo"} 100
{path="/bar"} 90

{path="/foo"} 100
{path="/bar"} 90
{path="/dings"} 100
{path="/bumms"} 100

# "foo" and "bar" are already at value 0 at t=0, so no extrapolation
# happens. "dings" has value 10 at t=0 and would reach 0 at t=-5m. The
# normal extrapolation by half a sample interval only goes to
# t=-2m30s, so that's not yet reaching a negative value and therefore
# chosen. However, "bumms" has value 1 at t=0 and would reach 0 at
# t=-30s. Here the extrapolation to t=-2m30s would reach a negative
# value, and therefore the extrapolation happens only by 30s.
eval instant at 50m increase(http_requests[100m])
{path="/foo"} 100
{path="/bar"} 90
{path="/foo"} 100
{path="/bar"} 90
{path="/dings"} 105
{path="/bumms"} 101

clear

Expand Down Expand Up @@ -133,13 +146,15 @@ load 4m
testcounter_zero_cutoff{start="4m"} 240+240x10
testcounter_zero_cutoff{start="5m"} 300+240x10

# Zero cutoff for left-side extrapolation.
# Zero cutoff for left-side extrapolation happens until we
# reach half a sampling interval (2m). Beyond that, we only
# extrapolate by half a sampling interval.
eval instant at 10m rate(testcounter_zero_cutoff[20m])
{start="0m"} 0.5
{start="1m"} 0.55
{start="2m"} 0.6
{start="3m"} 0.65
{start="4m"} 0.7
{start="3m"} 0.6
{start="4m"} 0.6
{start="5m"} 0.6

# Normal half-interval cutoff for left-side extrapolation.
Expand Down