@@ -1658,7 +1658,10 @@ func addAndResetCounts(hot, cold *histogramCounts) {
16581658type nativeExemplars struct {
16591659 sync.Mutex
16601660
1661- ttl time.Duration
1661+ // Time-to-live for exemplars, it is set to -1 if exemplars are disabled, that is NativeHistogramMaxExemplars is below 0.
1662+ // The ttl is used on insertion to remove an exemplar that is older than ttl, if present.
1663+ ttl time.Duration
1664+
16621665 exemplars []* dto.Exemplar
16631666}
16641667
@@ -1690,13 +1693,11 @@ func (n *nativeExemplars) addExemplar(e *dto.Exemplar) {
16901693 n .Lock ()
16911694 defer n .Unlock ()
16921695
1693- // The index where to insert the new exemplar.
1694- var nIdx int = - 1
1695-
16961696 // When the number of exemplars has not yet exceeded or
16971697 // is equal to cap(n.exemplars), then
16981698 // insert the new exemplar directly.
16991699 if len (n .exemplars ) < cap (n .exemplars ) {
1700+ var nIdx int
17001701 for nIdx = 0 ; nIdx < len (n .exemplars ); nIdx ++ {
17011702 if * e .Value < * n .exemplars [nIdx ].Value {
17021703 break
@@ -1716,11 +1717,34 @@ func (n *nativeExemplars) addExemplar(e *dto.Exemplar) {
17161717
17171718 // When the number of exemplars exceeds the limit, remove one exemplar.
17181719 var (
1719- ot = time .Now () // Oldest timestamp seen.
1720- otIdx = - 1 // Index of the exemplar with the oldest timestamp.
1721-
1722- md = - 1.0 // Logarithm of the delta of the closest pair of exemplars.
1723- rIdx = - 1 // Index of the older exemplar within the closest pair and where we need to insert the new exemplar.
1720+ ot = time.Time {} // Oldest timestamp seen. Initial value doesn't matter as we replace it due to otIdx == -1 in the loop.
1721+ otIdx = - 1 // Index of the exemplar with the oldest timestamp.
1722+
1723+ md = - 1.0 // Logarithm of the delta of the closest pair of exemplars.
1724+
1725+ // The insertion point of the new exemplar in the exemplars slice after insertion.
1726+ // This is calculated purely based on the order of the exemplars by value.
1727+ // nIdx == len(n.exemplars) means the new exemplar is to be inserted after the end.
1728+ nIdx = - 1
1729+
1730+ // rIdx is ultimately the index for the exemplar that we are replacing with the new exemplar.
1731+ // The aim is to keep a good spread of exemplars by value and not let them bunch up too much.
1732+ // It is calculated in 3 steps:
1733+ // 1. First we set rIdx to the index of the older exemplar within the closest pair by value.
1734+ // That is the following will be true (on log scale):
1735+ // either the exemplar pair on index (rIdx-1, rIdx) or (rIdx, rIdx+1) will have
1736+ // the closest values to each other from all pairs.
1737+ // For example, suppose the values are distributed like this:
1738+ // |-----------x-------------x----------------x----x-----|
1739+ // ^--rIdx as this is older.
1740+ // Or like this:
1741+ // |-----------x-------------x----------------x----x-----|
1742+ // ^--rIdx as this is older.
1743+ // 2. If there is an exemplar that expired, then we simple reset rIdx to that index.
1744+ // 3. We check if by inserting the new exemplar we would create a closer pair at
1745+ // (nIdx-1, nIdx) or (nIdx, nIdx+1) and set rIdx to nIdx-1 or nIdx accordingly to
1746+ // keep the spread of exemplars by value; otherwise we keep rIdx as it is.
1747+ rIdx = - 1
17241748 cLog float64 // Logarithm of the current exemplar.
17251749 pLog float64 // Logarithm of the previous exemplar.
17261750 )
@@ -1745,7 +1769,7 @@ func (n *nativeExemplars) addExemplar(e *dto.Exemplar) {
17451769 }
17461770 diff := math .Abs (cLog - pLog )
17471771 if md == - 1 || diff < md {
1748- // The closest exemplar pair is this: |exemplar.[i] - n.exemplars[i-1].Value| is minimal .
1772+ // The closest exemplar pair is at index: i-1, i .
17491773 // Choose the exemplar with the older timestamp for replacement.
17501774 md = diff
17511775 if n .exemplars [i ].Timestamp .AsTime ().Before (n .exemplars [i - 1 ].Timestamp .AsTime ()) {
@@ -1763,7 +1787,8 @@ func (n *nativeExemplars) addExemplar(e *dto.Exemplar) {
17631787 nIdx = len (n .exemplars )
17641788 }
17651789 // Here, we have the following relationships:
1766- // n.exemplars[nIdx-1].Value < e.Value <= n.exemplars[nIdx].Value
1790+ // n.exemplars[nIdx-1].Value < e.Value (if nIdx > 0)
1791+ // e.Value <= n.exemplars[nIdx].Value (if nIdx < len(n.exemplars))
17671792
17681793 if otIdx != - 1 && e .Timestamp .AsTime ().Sub (ot ) > n .ttl {
17691794 // If the oldest exemplar has expired, then replace it with the new exemplar.
@@ -1776,19 +1801,23 @@ func (n *nativeExemplars) addExemplar(e *dto.Exemplar) {
17761801 if nIdx > 0 {
17771802 diff := math .Abs (elog - math .Log (n .exemplars [nIdx - 1 ].GetValue ()))
17781803 if diff < md {
1779- // The closest exemplar pair is this: |e.Value - n.exemplars[nIdx-1].Value| is minimal.
1780- // Assume that the exemplar we are inserting has a newer timestamp. This is not always
1781- // true, due to concurrency, but it's a good enough approximation.
1804+ // The value we are about to insert is closer to the previous exemplar at the insertion point than what we calculated before in rIdx.
1805+ // v--rIdx
1806+ // |-----------x-n-----------x----------------x----x-----|
1807+ // nIdx-1--^ ^--new exemplar value
1808+ // Do not make the spread worse, replace nIdx-1 and not rIdx.
17821809 md = diff
17831810 rIdx = nIdx - 1
17841811 }
17851812 }
17861813 if nIdx < len (n .exemplars ) {
17871814 diff := math .Abs (math .Log (n .exemplars [nIdx ].GetValue ()) - elog )
17881815 if diff < md {
1789- // The closest exemplar pair is this: |n.exemplars[nIdx].Value - e.Value| is minimal.
1790- // Assume that the exemplar we are inserting has a newer timestamp. This is not always
1791- // true, due to concurrency, but it's a good enough approximation.
1816+ // The value we are about to insert is closer to the next exemplar at the insertion point than what we calculated before in rIdx.
1817+ // v--rIdx
1818+ // |-----------x-----------n-x----------------x----x-----|
1819+ // new exemplar value--^ ^--nIdx
1820+ // Do not make the spread worse, replace nIdx-1 and not rIdx.
17921821 rIdx = nIdx
17931822 }
17941823 }
0 commit comments