| 
									
										
										
										
											2024-06-06 23:17:13 +08:00
										 |  |  | // Copyright 2024 The Prometheus Authors
 | 
					
						
							|  |  |  | // 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 promql | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"github.com/prometheus/prometheus/model/histogram" | 
					
						
							|  |  |  | 	"github.com/prometheus/prometheus/model/value" | 
					
						
							|  |  |  | 	"github.com/prometheus/prometheus/tsdb/chunkenc" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-03 05:35:49 +08:00
										 |  |  | // HistogramStatsIterator is an iterator that returns histogram objects that
 | 
					
						
							|  |  |  | // have only their sum and count values populated. The iterator handles counter
 | 
					
						
							|  |  |  | // reset detection internally and sets the counter reset hint accordingly in
 | 
					
						
							|  |  |  | // each returned histogram object. The Next and Seek methods of the iterator
 | 
					
						
							|  |  |  | // will never return ValHistogram, but ValFloatHistogram instead. Effectively,
 | 
					
						
							|  |  |  | // the iterator enforces conversion of (integer) Histogram to FloatHistogram.
 | 
					
						
							|  |  |  | // The AtHistogram method must not be called (and will panic).
 | 
					
						
							| 
									
										
										
										
											2025-06-19 05:53:46 +08:00
										 |  |  | type HistogramStatsIterator struct { | 
					
						
							| 
									
										
										
										
											2024-06-06 23:17:13 +08:00
										 |  |  | 	chunkenc.Iterator | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-05 01:05:14 +08:00
										 |  |  | 	current       *histogram.FloatHistogram | 
					
						
							|  |  |  | 	last          *histogram.FloatHistogram | 
					
						
							|  |  |  | 	lastIsCurrent bool | 
					
						
							| 
									
										
										
										
											2024-06-06 23:17:13 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-19 05:53:46 +08:00
										 |  |  | // NewHistogramStatsIterator creates a new HistogramStatsIterator.
 | 
					
						
							|  |  |  | func NewHistogramStatsIterator(it chunkenc.Iterator) *HistogramStatsIterator { | 
					
						
							|  |  |  | 	return &HistogramStatsIterator{ | 
					
						
							| 
									
										
										
										
											2025-09-05 01:05:14 +08:00
										 |  |  | 		Iterator: it, | 
					
						
							|  |  |  | 		current:  &histogram.FloatHistogram{}, | 
					
						
							| 
									
										
										
										
											2024-06-06 23:17:13 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-19 05:53:46 +08:00
										 |  |  | // Reset resets this iterator for use with a new underlying iterator, reusing
 | 
					
						
							|  |  |  | // objects already allocated where possible.
 | 
					
						
							| 
									
										
										
										
											2025-08-28 21:00:25 +08:00
										 |  |  | func (hsi *HistogramStatsIterator) Reset(it chunkenc.Iterator) { | 
					
						
							|  |  |  | 	hsi.Iterator = it | 
					
						
							| 
									
										
										
										
											2025-09-05 01:05:14 +08:00
										 |  |  | 	hsi.last = nil | 
					
						
							|  |  |  | 	hsi.lastIsCurrent = false | 
					
						
							| 
									
										
										
										
											2025-06-19 05:53:46 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-03 05:35:49 +08:00
										 |  |  | // Next mostly relays to the underlying iterator, but changes a ValHistogram
 | 
					
						
							|  |  |  | // return into a ValFloatHistogram return.
 | 
					
						
							|  |  |  | func (hsi *HistogramStatsIterator) Next() chunkenc.ValueType { | 
					
						
							| 
									
										
										
										
											2025-09-05 01:05:14 +08:00
										 |  |  | 	hsi.lastIsCurrent = false | 
					
						
							| 
									
										
										
										
											2025-09-03 05:35:49 +08:00
										 |  |  | 	vt := hsi.Iterator.Next() | 
					
						
							|  |  |  | 	if vt == chunkenc.ValHistogram { | 
					
						
							|  |  |  | 		return chunkenc.ValFloatHistogram | 
					
						
							| 
									
										
										
										
											2024-06-06 23:17:13 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2025-09-03 05:35:49 +08:00
										 |  |  | 	return vt | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2024-06-06 23:17:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-03 05:35:49 +08:00
										 |  |  | // Seek mostly relays to the underlying iterator, but changes a ValHistogram
 | 
					
						
							|  |  |  | // return into a ValFloatHistogram return.
 | 
					
						
							|  |  |  | func (hsi *HistogramStatsIterator) Seek(t int64) chunkenc.ValueType { | 
					
						
							| 
									
										
										
										
											2025-09-04 19:45:24 +08:00
										 |  |  | 	// If the Seek is going to move the iterator, we have to forget the
 | 
					
						
							| 
									
										
										
										
											2025-09-05 01:05:14 +08:00
										 |  |  | 	// lastFH and mark the currentFH as not current anymore.
 | 
					
						
							| 
									
										
										
										
											2025-09-04 19:45:24 +08:00
										 |  |  | 	if t > hsi.AtT() { | 
					
						
							| 
									
										
										
										
											2025-09-05 01:05:14 +08:00
										 |  |  | 		hsi.last = nil | 
					
						
							|  |  |  | 		hsi.lastIsCurrent = false | 
					
						
							| 
									
										
										
										
											2025-09-04 19:45:24 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2025-09-03 05:35:49 +08:00
										 |  |  | 	vt := hsi.Iterator.Seek(t) | 
					
						
							|  |  |  | 	if vt == chunkenc.ValHistogram { | 
					
						
							|  |  |  | 		return chunkenc.ValFloatHistogram | 
					
						
							| 
									
										
										
										
											2024-07-31 17:17:09 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2025-09-03 05:35:49 +08:00
										 |  |  | 	return vt | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2024-07-31 17:17:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-03 05:35:49 +08:00
										 |  |  | // AtHistogram must never be called.
 | 
					
						
							|  |  |  | func (*HistogramStatsIterator) AtHistogram(*histogram.Histogram) (int64, *histogram.Histogram) { | 
					
						
							|  |  |  | 	panic("HistogramStatsIterator.AtHistogram must never be called") | 
					
						
							| 
									
										
										
										
											2024-06-06 23:17:13 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-03 05:35:49 +08:00
										 |  |  | // AtFloatHistogram returns the next timestamp/float histogram pair. The method
 | 
					
						
							|  |  |  | // performs a counter reset detection on the fly. It will return an explicit
 | 
					
						
							|  |  |  | // hint (not UnknownCounterReset) if the previous sample has been accessed with
 | 
					
						
							|  |  |  | // the same iterator.
 | 
					
						
							| 
									
										
										
										
											2025-08-28 21:00:25 +08:00
										 |  |  | func (hsi *HistogramStatsIterator) AtFloatHistogram(fh *histogram.FloatHistogram) (int64, *histogram.FloatHistogram) { | 
					
						
							| 
									
										
										
										
											2025-09-05 01:05:14 +08:00
										 |  |  | 	populateFH := func(src *histogram.FloatHistogram, detectReset bool) { | 
					
						
							|  |  |  | 		h := histogram.FloatHistogram{ | 
					
						
							|  |  |  | 			CounterResetHint: src.CounterResetHint, | 
					
						
							|  |  |  | 			Count:            src.Count, | 
					
						
							|  |  |  | 			Sum:              src.Sum, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if detectReset { | 
					
						
							|  |  |  | 			h.CounterResetHint = hsi.getResetHint(src.CounterResetHint) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if fh == nil { | 
					
						
							|  |  |  | 			// Note that we cannot simply write `fh = &h` here
 | 
					
						
							|  |  |  | 			// because that would let h escape to the heap.
 | 
					
						
							|  |  |  | 			fh = &histogram.FloatHistogram{} | 
					
						
							|  |  |  | 			*fh = h | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			h.CopyTo(fh) | 
					
						
							| 
									
										
										
										
											2024-06-06 23:17:13 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-05 01:05:14 +08:00
										 |  |  | 	if hsi.lastIsCurrent { | 
					
						
							|  |  |  | 		// Nothing changed since last AtFloatHistogram call. Return a
 | 
					
						
							|  |  |  | 		// copy of the stored last histogram rather than doing counter
 | 
					
						
							|  |  |  | 		// reset detection again (which would yield a potentially wrong
 | 
					
						
							|  |  |  | 		// result of "no counter reset").
 | 
					
						
							|  |  |  | 		populateFH(hsi.last, false) | 
					
						
							|  |  |  | 		return hsi.AtT(), fh | 
					
						
							| 
									
										
										
										
											2024-07-31 17:17:09 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-05 01:05:14 +08:00
										 |  |  | 	var t int64 | 
					
						
							|  |  |  | 	t, hsi.current = hsi.Iterator.AtFloatHistogram(hsi.current) | 
					
						
							|  |  |  | 	if value.IsStaleNaN(hsi.current.Sum) { | 
					
						
							|  |  |  | 		populateFH(hsi.current, false) | 
					
						
							|  |  |  | 		return t, fh | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	populateFH(hsi.current, true) | 
					
						
							|  |  |  | 	hsi.setLastFromCurrent(fh.CounterResetHint) | 
					
						
							| 
									
										
										
										
											2024-06-06 23:17:13 +08:00
										 |  |  | 	return t, fh | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-05 01:05:14 +08:00
										 |  |  | // setLastFromCurrent stores a copy of hsi.current as hsi.last. The
 | 
					
						
							|  |  |  | // CounterResetHint of hsi.last is set to the provided value, though. This is
 | 
					
						
							|  |  |  | // meant to store the value we have calculated on the fly so that we can return
 | 
					
						
							|  |  |  | // the same without re-calculation in case AtFloatHistogram is called multiple
 | 
					
						
							|  |  |  | // times.
 | 
					
						
							|  |  |  | func (hsi *HistogramStatsIterator) setLastFromCurrent(hint histogram.CounterResetHint) { | 
					
						
							|  |  |  | 	if hsi.last == nil { | 
					
						
							|  |  |  | 		hsi.last = hsi.current.Copy() | 
					
						
							| 
									
										
										
										
											2024-06-06 23:17:13 +08:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2025-09-05 01:05:14 +08:00
										 |  |  | 		hsi.current.CopyTo(hsi.last) | 
					
						
							| 
									
										
										
										
											2024-06-06 23:17:13 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2025-09-05 01:05:14 +08:00
										 |  |  | 	hsi.last.CounterResetHint = hint | 
					
						
							|  |  |  | 	hsi.lastIsCurrent = true | 
					
						
							| 
									
										
										
										
											2024-06-06 23:17:13 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-05 01:05:14 +08:00
										 |  |  | func (hsi *HistogramStatsIterator) getResetHint(hint histogram.CounterResetHint) histogram.CounterResetHint { | 
					
						
							| 
									
										
										
										
											2024-06-06 23:17:13 +08:00
										 |  |  | 	if hint != histogram.UnknownCounterReset { | 
					
						
							|  |  |  | 		return hint | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2025-09-05 01:05:14 +08:00
										 |  |  | 	if hsi.last == nil { | 
					
						
							| 
									
										
										
										
											2025-09-03 05:35:49 +08:00
										 |  |  | 		// We don't know if there's a counter reset. Note that this
 | 
					
						
							|  |  |  | 		// generally will trigger an explicit counter reset detection by
 | 
					
						
							|  |  |  | 		// the PromQL engine, which in turn isn't as reliable in this
 | 
					
						
							|  |  |  | 		// case because the PromQL engine will not see the buckets.
 | 
					
						
							|  |  |  | 		// However, we can assume that in cases where the counter reset
 | 
					
						
							|  |  |  | 		// detection is relevant, an iteration through the series has
 | 
					
						
							|  |  |  | 		// happened, and therefore we do not end up here in the first
 | 
					
						
							|  |  |  | 		// place.
 | 
					
						
							|  |  |  | 		return histogram.UnknownCounterReset | 
					
						
							| 
									
										
										
										
											2024-06-06 23:17:13 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2025-09-05 01:05:14 +08:00
										 |  |  | 	if hsi.current.DetectReset(hsi.last) { | 
					
						
							| 
									
										
										
										
											2024-06-06 23:17:13 +08:00
										 |  |  | 		return histogram.CounterReset | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return histogram.NotCounterReset | 
					
						
							|  |  |  | } |