@@ -286,46 +286,115 @@ func funcIncrease(vals []parser.Value, args parser.Expressions, enh *EvalNodeHel
286286
287287// === irate(node parser.ValueTypeMatrix) (Vector, Annotations) ===
288288func funcIrate (vals []parser.Value , args parser.Expressions , enh * EvalNodeHelper ) (Vector , annotations.Annotations ) {
289- return instantValue (vals , enh .Out , true )
289+ return instantValue (vals , args , enh .Out , true )
290290}
291291
292292// === idelta(node model.ValMatrix) (Vector, Annotations) ===
293293func funcIdelta (vals []parser.Value , args parser.Expressions , enh * EvalNodeHelper ) (Vector , annotations.Annotations ) {
294- return instantValue (vals , enh .Out , false )
294+ return instantValue (vals , args , enh .Out , false )
295295}
296296
297- func instantValue (vals []parser.Value , out Vector , isRate bool ) (Vector , annotations.Annotations ) {
298- samples := vals [0 ].(Matrix )[0 ]
297+ func instantValue (vals []parser.Value , args parser.Expressions , out Vector , isRate bool ) (Vector , annotations.Annotations ) {
298+ var (
299+ samples = vals [0 ].(Matrix )[0 ]
300+ metricName = samples .Metric .Get (labels .MetricName )
301+ ss = make ([]Sample , 0 , 2 )
302+ annos annotations.Annotations
303+ )
304+
299305 // No sense in trying to compute a rate without at least two points. Drop
300306 // this Vector element.
301307 // TODO: add RangeTooShortWarning
302- if len (samples .Floats ) < 2 {
308+ if len (samples .Floats )+ len ( samples . Histograms ) < 2 {
303309 return out , nil
304310 }
305311
306- lastSample := samples .Floats [len (samples .Floats )- 1 ]
307- previousSample := samples .Floats [len (samples .Floats )- 2 ]
312+ // Add the last 2 float samples if they exist.
313+ for i := max (0 , len (samples .Floats )- 2 ); i < len (samples .Floats ); i ++ {
314+ ss = append (ss , Sample {
315+ F : samples .Floats [i ].F ,
316+ T : samples .Floats [i ].T ,
317+ })
318+ }
308319
309- var resultValue float64
310- if isRate && lastSample .F < previousSample .F {
311- // Counter reset.
312- resultValue = lastSample .F
313- } else {
314- resultValue = lastSample .F - previousSample .F
320+ // Add the last 2 histogram samples into their correct position if they exist.
321+ for i := max (0 , len (samples .Histograms )- 2 ); i < len (samples .Histograms ); i ++ {
322+ s := Sample {
323+ H : samples .Histograms [i ].H ,
324+ T : samples .Histograms [i ].T ,
325+ }
326+ switch {
327+ case len (ss ) == 0 :
328+ ss = append (ss , s )
329+ case len (ss ) == 1 :
330+ if s .T < ss [0 ].T {
331+ ss = append ([]Sample {s }, ss ... )
332+ } else {
333+ ss = append (ss , s )
334+ }
335+ case s .T < ss [0 ].T :
336+ // s is older than 1st, so discard it.
337+ case s .T > ss [1 ].T :
338+ // s is newest, so add it as 2nd and make the old 2nd the new 1st.
339+ ss [0 ] = ss [1 ]
340+ ss [1 ] = s
341+ default :
342+ // In all other cases, we just make s the new 1st.
343+ // This establishes a correct order, even in the (irregular)
344+ // case of equal timestamps.
345+ ss [0 ] = s
346+ }
315347 }
316348
317- sampledInterval := lastSample .T - previousSample .T
349+ resultSample := ss [1 ]
350+ sampledInterval := ss [1 ].T - ss [0 ].T
318351 if sampledInterval == 0 {
319352 // Avoid dividing by 0.
320353 return out , nil
321354 }
355+ switch {
356+ case ss [1 ].H == nil && ss [0 ].H == nil :
357+ if ! isRate || ss [1 ].F >= ss [0 ].F {
358+ // Gauge or counter without reset.
359+ resultSample .F = ss [1 ].F - ss [0 ].F
360+ }
361+ // In case of a counter reset, we leave resultSample at
362+ // its current value, which is already ss[1].
363+ case ss [1 ].H != nil && ss [0 ].H != nil :
364+ resultSample .H = ss [1 ].H .Copy ()
365+ // irate should only be applied to counters.
366+ if isRate && (ss [1 ].H .CounterResetHint == histogram .GaugeType || ss [0 ].H .CounterResetHint == histogram .GaugeType ) {
367+ annos .Add (annotations .NewNativeHistogramNotCounterWarning (metricName , args .PositionRange ()))
368+ }
369+ // idelta should only be applied to gauges.
370+ if ! isRate && (ss [1 ].H .CounterResetHint != histogram .GaugeType || ss [0 ].H .CounterResetHint != histogram .GaugeType ) {
371+ annos .Add (annotations .NewNativeHistogramNotGaugeWarning (metricName , args .PositionRange ()))
372+ }
373+ if ! isRate || ! ss [1 ].H .DetectReset (ss [0 ].H ) {
374+ _ , err := resultSample .H .Sub (ss [0 ].H )
375+ if errors .Is (err , histogram .ErrHistogramsIncompatibleSchema ) {
376+ return out , annos .Add (annotations .NewMixedExponentialCustomHistogramsWarning (metricName , args .PositionRange ()))
377+ } else if errors .Is (err , histogram .ErrHistogramsIncompatibleBounds ) {
378+ return out , annos .Add (annotations .NewIncompatibleCustomBucketsHistogramsWarning (metricName , args .PositionRange ()))
379+ }
380+ }
381+ resultSample .H .CounterResetHint = histogram .GaugeType
382+ resultSample .H .Compact (0 )
383+ default :
384+ // Mix of a float and a histogram.
385+ return out , annos .Add (annotations .NewMixedFloatsHistogramsWarning (metricName , args .PositionRange ()))
386+ }
322387
323388 if isRate {
324389 // Convert to per-second.
325- resultValue /= float64 (sampledInterval ) / 1000
390+ if resultSample .H == nil {
391+ resultSample .F /= float64 (sampledInterval ) / 1000
392+ } else {
393+ resultSample .H .Div (float64 (sampledInterval ) / 1000 )
394+ }
326395 }
327396
328- return append (out , Sample { F : resultValue } ), nil
397+ return append (out , resultSample ), annos
329398}
330399
331400// Calculate the trend value at the given index i in raw data d.
0 commit comments