You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

histogram.go 63KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531
  1. // Copyright 2015 The Prometheus Authors
  2. // Licensed under the Apache License, Version 2.0 (the "License");
  3. // you may not use this file except in compliance with the License.
  4. // You may obtain a copy of the License at
  5. //
  6. // http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. // Unless required by applicable law or agreed to in writing, software
  9. // distributed under the License is distributed on an "AS IS" BASIS,
  10. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. // See the License for the specific language governing permissions and
  12. // limitations under the License.
  13. package prometheus
  14. import (
  15. "fmt"
  16. "math"
  17. "runtime"
  18. "sort"
  19. "sync"
  20. "sync/atomic"
  21. "time"
  22. dto "github.com/prometheus/client_model/go"
  23. "google.golang.org/protobuf/proto"
  24. "google.golang.org/protobuf/types/known/timestamppb"
  25. )
  26. // nativeHistogramBounds for the frac of observed values. Only relevant for
  27. // schema > 0. The position in the slice is the schema. (0 is never used, just
  28. // here for convenience of using the schema directly as the index.)
  29. //
  30. // TODO(beorn7): Currently, we do a binary search into these slices. There are
  31. // ways to turn it into a small number of simple array lookups. It probably only
  32. // matters for schema 5 and beyond, but should be investigated. See this comment
  33. // as a starting point:
  34. // https://github.com/open-telemetry/opentelemetry-specification/issues/1776#issuecomment-870164310
  35. var nativeHistogramBounds = [][]float64{
  36. // Schema "0":
  37. {0.5},
  38. // Schema 1:
  39. {0.5, 0.7071067811865475},
  40. // Schema 2:
  41. {0.5, 0.5946035575013605, 0.7071067811865475, 0.8408964152537144},
  42. // Schema 3:
  43. {
  44. 0.5, 0.5452538663326288, 0.5946035575013605, 0.6484197773255048,
  45. 0.7071067811865475, 0.7711054127039704, 0.8408964152537144, 0.9170040432046711,
  46. },
  47. // Schema 4:
  48. {
  49. 0.5, 0.5221368912137069, 0.5452538663326288, 0.5693943173783458,
  50. 0.5946035575013605, 0.620928906036742, 0.6484197773255048, 0.6771277734684463,
  51. 0.7071067811865475, 0.7384130729697496, 0.7711054127039704, 0.805245165974627,
  52. 0.8408964152537144, 0.8781260801866495, 0.9170040432046711, 0.9576032806985735,
  53. },
  54. // Schema 5:
  55. {
  56. 0.5, 0.5109485743270583, 0.5221368912137069, 0.5335702003384117,
  57. 0.5452538663326288, 0.5571933712979462, 0.5693943173783458, 0.5818624293887887,
  58. 0.5946035575013605, 0.6076236799902344, 0.620928906036742, 0.6345254785958666,
  59. 0.6484197773255048, 0.6626183215798706, 0.6771277734684463, 0.6919549409819159,
  60. 0.7071067811865475, 0.7225904034885232, 0.7384130729697496, 0.7545822137967112,
  61. 0.7711054127039704, 0.7879904225539431, 0.805245165974627, 0.8228777390769823,
  62. 0.8408964152537144, 0.8593096490612387, 0.8781260801866495, 0.8973545375015533,
  63. 0.9170040432046711, 0.9370838170551498, 0.9576032806985735, 0.9785720620876999,
  64. },
  65. // Schema 6:
  66. {
  67. 0.5, 0.5054446430258502, 0.5109485743270583, 0.5165124395106142,
  68. 0.5221368912137069, 0.5278225891802786, 0.5335702003384117, 0.5393803988785598,
  69. 0.5452538663326288, 0.5511912916539204, 0.5571933712979462, 0.5632608093041209,
  70. 0.5693943173783458, 0.5755946149764913, 0.5818624293887887, 0.5881984958251406,
  71. 0.5946035575013605, 0.6010783657263515, 0.6076236799902344, 0.6142402680534349,
  72. 0.620928906036742, 0.6276903785123455, 0.6345254785958666, 0.6414350080393891,
  73. 0.6484197773255048, 0.6554806057623822, 0.6626183215798706, 0.6698337620266515,
  74. 0.6771277734684463, 0.6845012114872953, 0.6919549409819159, 0.6994898362691555,
  75. 0.7071067811865475, 0.7148066691959849, 0.7225904034885232, 0.7304588970903234,
  76. 0.7384130729697496, 0.7464538641456323, 0.7545822137967112, 0.762799075372269,
  77. 0.7711054127039704, 0.7795022001189185, 0.7879904225539431, 0.7965710756711334,
  78. 0.805245165974627, 0.8140137109286738, 0.8228777390769823, 0.8318382901633681,
  79. 0.8408964152537144, 0.8500531768592616, 0.8593096490612387, 0.8686669176368529,
  80. 0.8781260801866495, 0.8876882462632604, 0.8973545375015533, 0.9071260877501991,
  81. 0.9170040432046711, 0.9269895625416926, 0.9370838170551498, 0.9472879907934827,
  82. 0.9576032806985735, 0.9680308967461471, 0.9785720620876999, 0.9892280131939752,
  83. },
  84. // Schema 7:
  85. {
  86. 0.5, 0.5027149505564014, 0.5054446430258502, 0.5081891574554764,
  87. 0.5109485743270583, 0.5137229745593818, 0.5165124395106142, 0.5193170509806894,
  88. 0.5221368912137069, 0.5249720429003435, 0.5278225891802786, 0.5306886136446309,
  89. 0.5335702003384117, 0.5364674337629877, 0.5393803988785598, 0.5423091811066545,
  90. 0.5452538663326288, 0.5482145409081883, 0.5511912916539204, 0.5541842058618393,
  91. 0.5571933712979462, 0.5602188762048033, 0.5632608093041209, 0.5663192597993595,
  92. 0.5693943173783458, 0.572486072215902, 0.5755946149764913, 0.5787200368168754,
  93. 0.5818624293887887, 0.585021884841625, 0.5881984958251406, 0.5913923554921704,
  94. 0.5946035575013605, 0.5978321960199137, 0.6010783657263515, 0.6043421618132907,
  95. 0.6076236799902344, 0.6109230164863786, 0.6142402680534349, 0.6175755319684665,
  96. 0.620928906036742, 0.6243004885946023, 0.6276903785123455, 0.6310986751971253,
  97. 0.6345254785958666, 0.637970889198196, 0.6414350080393891, 0.6449179367033329,
  98. 0.6484197773255048, 0.6519406325959679, 0.6554806057623822, 0.659039800633032,
  99. 0.6626183215798706, 0.6662162735415805, 0.6698337620266515, 0.6734708931164728,
  100. 0.6771277734684463, 0.6808045103191123, 0.6845012114872953, 0.688217985377265,
  101. 0.6919549409819159, 0.6957121878859629, 0.6994898362691555, 0.7032879969095076,
  102. 0.7071067811865475, 0.7109463010845827, 0.7148066691959849, 0.718687998724491,
  103. 0.7225904034885232, 0.7265139979245261, 0.7304588970903234, 0.7344252166684908,
  104. 0.7384130729697496, 0.7424225829363761, 0.7464538641456323, 0.7505070348132126,
  105. 0.7545822137967112, 0.7586795205991071, 0.762799075372269, 0.7669409989204777,
  106. 0.7711054127039704, 0.7752924388424999, 0.7795022001189185, 0.7837348199827764,
  107. 0.7879904225539431, 0.7922691326262467, 0.7965710756711334, 0.8008963778413465,
  108. 0.805245165974627, 0.8096175675974316, 0.8140137109286738, 0.8184337248834821,
  109. 0.8228777390769823, 0.8273458838280969, 0.8318382901633681, 0.8363550898207981,
  110. 0.8408964152537144, 0.8454623996346523, 0.8500531768592616, 0.8546688815502312,
  111. 0.8593096490612387, 0.8639756154809185, 0.8686669176368529, 0.8733836930995842,
  112. 0.8781260801866495, 0.8828942179666361, 0.8876882462632604, 0.8925083056594671,
  113. 0.8973545375015533, 0.9022270839033115, 0.9071260877501991, 0.9120516927035263,
  114. 0.9170040432046711, 0.9219832844793128, 0.9269895625416926, 0.9320230241988943,
  115. 0.9370838170551498, 0.9421720895161669, 0.9472879907934827, 0.9524316709088368,
  116. 0.9576032806985735, 0.9628029718180622, 0.9680308967461471, 0.9732872087896164,
  117. 0.9785720620876999, 0.9838856116165875, 0.9892280131939752, 0.9945994234836328,
  118. },
  119. // Schema 8:
  120. {
  121. 0.5, 0.5013556375251013, 0.5027149505564014, 0.5040779490592088,
  122. 0.5054446430258502, 0.5068150424757447, 0.5081891574554764, 0.509566998038869,
  123. 0.5109485743270583, 0.5123338964485679, 0.5137229745593818, 0.5151158188430205,
  124. 0.5165124395106142, 0.5179128468009786, 0.5193170509806894, 0.520725062344158,
  125. 0.5221368912137069, 0.5235525479396449, 0.5249720429003435, 0.526395386502313,
  126. 0.5278225891802786, 0.5292536613972564, 0.5306886136446309, 0.5321274564422321,
  127. 0.5335702003384117, 0.5350168559101208, 0.5364674337629877, 0.5379219445313954,
  128. 0.5393803988785598, 0.5408428074966075, 0.5423091811066545, 0.5437795304588847,
  129. 0.5452538663326288, 0.5467321995364429, 0.5482145409081883, 0.549700901315111,
  130. 0.5511912916539204, 0.5526857228508706, 0.5541842058618393, 0.5556867516724088,
  131. 0.5571933712979462, 0.5587040757836845, 0.5602188762048033, 0.5617377836665098,
  132. 0.5632608093041209, 0.564787964283144, 0.5663192597993595, 0.5678547070789026,
  133. 0.5693943173783458, 0.5709381019847808, 0.572486072215902, 0.5740382394200894,
  134. 0.5755946149764913, 0.5771552102951081, 0.5787200368168754, 0.5802891060137493,
  135. 0.5818624293887887, 0.5834400184762408, 0.585021884841625, 0.5866080400818185,
  136. 0.5881984958251406, 0.5897932637314379, 0.5913923554921704, 0.5929957828304968,
  137. 0.5946035575013605, 0.5962156912915756, 0.5978321960199137, 0.5994530835371903,
  138. 0.6010783657263515, 0.6027080545025619, 0.6043421618132907, 0.6059806996384005,
  139. 0.6076236799902344, 0.6092711149137041, 0.6109230164863786, 0.6125793968185725,
  140. 0.6142402680534349, 0.6159056423670379, 0.6175755319684665, 0.6192499490999082,
  141. 0.620928906036742, 0.622612415087629, 0.6243004885946023, 0.6259931389331581,
  142. 0.6276903785123455, 0.6293922197748583, 0.6310986751971253, 0.6328097572894031,
  143. 0.6345254785958666, 0.6362458516947014, 0.637970889198196, 0.6397006037528346,
  144. 0.6414350080393891, 0.6431741147730128, 0.6449179367033329, 0.6466664866145447,
  145. 0.6484197773255048, 0.6501778216898253, 0.6519406325959679, 0.6537082229673385,
  146. 0.6554806057623822, 0.6572577939746774, 0.659039800633032, 0.6608266388015788,
  147. 0.6626183215798706, 0.6644148621029772, 0.6662162735415805, 0.6680225691020727,
  148. 0.6698337620266515, 0.6716498655934177, 0.6734708931164728, 0.6752968579460171,
  149. 0.6771277734684463, 0.6789636531064505, 0.6808045103191123, 0.6826503586020058,
  150. 0.6845012114872953, 0.6863570825438342, 0.688217985377265, 0.690083933630119,
  151. 0.6919549409819159, 0.6938310211492645, 0.6957121878859629, 0.6975984549830999,
  152. 0.6994898362691555, 0.7013863456101023, 0.7032879969095076, 0.7051948041086352,
  153. 0.7071067811865475, 0.7090239421602076, 0.7109463010845827, 0.7128738720527471,
  154. 0.7148066691959849, 0.7167447066838943, 0.718687998724491, 0.7206365595643126,
  155. 0.7225904034885232, 0.7245495448210174, 0.7265139979245261, 0.7284837772007218,
  156. 0.7304588970903234, 0.7324393720732029, 0.7344252166684908, 0.7364164454346837,
  157. 0.7384130729697496, 0.7404151139112358, 0.7424225829363761, 0.7444354947621984,
  158. 0.7464538641456323, 0.7484777058836176, 0.7505070348132126, 0.7525418658117031,
  159. 0.7545822137967112, 0.7566280937263048, 0.7586795205991071, 0.7607365094544071,
  160. 0.762799075372269, 0.7648672334736434, 0.7669409989204777, 0.7690203869158282,
  161. 0.7711054127039704, 0.7731960915705107, 0.7752924388424999, 0.7773944698885442,
  162. 0.7795022001189185, 0.7816156449856788, 0.7837348199827764, 0.7858597406461707,
  163. 0.7879904225539431, 0.7901268813264122, 0.7922691326262467, 0.7944171921585818,
  164. 0.7965710756711334, 0.7987307989543135, 0.8008963778413465, 0.8030678282083853,
  165. 0.805245165974627, 0.8074284071024302, 0.8096175675974316, 0.8118126635086642,
  166. 0.8140137109286738, 0.8162207259936375, 0.8184337248834821, 0.820652723822003,
  167. 0.8228777390769823, 0.8251087869603088, 0.8273458838280969, 0.8295890460808079,
  168. 0.8318382901633681, 0.8340936325652911, 0.8363550898207981, 0.8386226785089391,
  169. 0.8408964152537144, 0.8431763167241966, 0.8454623996346523, 0.8477546807446661,
  170. 0.8500531768592616, 0.8523579048290255, 0.8546688815502312, 0.8569861239649629,
  171. 0.8593096490612387, 0.8616394738731368, 0.8639756154809185, 0.8663180910111553,
  172. 0.8686669176368529, 0.871022112577578, 0.8733836930995842, 0.8757516765159389,
  173. 0.8781260801866495, 0.8805069215187917, 0.8828942179666361, 0.8852879870317771,
  174. 0.8876882462632604, 0.890095013257712, 0.8925083056594671, 0.8949281411607002,
  175. 0.8973545375015533, 0.8997875124702672, 0.9022270839033115, 0.9046732696855155,
  176. 0.9071260877501991, 0.909585556079304, 0.9120516927035263, 0.9145245157024483,
  177. 0.9170040432046711, 0.9194902933879467, 0.9219832844793128, 0.9244830347552253,
  178. 0.9269895625416926, 0.92950288621441, 0.9320230241988943, 0.9345499949706191,
  179. 0.9370838170551498, 0.93962450902828, 0.9421720895161669, 0.9447265771954693,
  180. 0.9472879907934827, 0.9498563490882775, 0.9524316709088368, 0.9550139751351947,
  181. 0.9576032806985735, 0.9601996065815236, 0.9628029718180622, 0.9654133954938133,
  182. 0.9680308967461471, 0.9706554947643201, 0.9732872087896164, 0.9759260581154889,
  183. 0.9785720620876999, 0.9812252401044634, 0.9838856116165875, 0.9865531961276168,
  184. 0.9892280131939752, 0.9919100824251095, 0.9945994234836328, 0.9972960560854698,
  185. },
  186. }
  187. // The nativeHistogramBounds above can be generated with the code below.
  188. //
  189. // TODO(beorn7): It's tempting to actually use `go generate` to generate the
  190. // code above. However, this could lead to slightly different numbers on
  191. // different architectures. We still need to come to terms if we are fine with
  192. // that, or if we might prefer to specify precise numbers in the standard.
  193. //
  194. // var nativeHistogramBounds [][]float64 = make([][]float64, 9)
  195. //
  196. // func init() {
  197. // // Populate nativeHistogramBounds.
  198. // numBuckets := 1
  199. // for i := range nativeHistogramBounds {
  200. // bounds := []float64{0.5}
  201. // factor := math.Exp2(math.Exp2(float64(-i)))
  202. // for j := 0; j < numBuckets-1; j++ {
  203. // var bound float64
  204. // if (j+1)%2 == 0 {
  205. // // Use previously calculated value for increased precision.
  206. // bound = nativeHistogramBounds[i-1][j/2+1]
  207. // } else {
  208. // bound = bounds[j] * factor
  209. // }
  210. // bounds = append(bounds, bound)
  211. // }
  212. // numBuckets *= 2
  213. // nativeHistogramBounds[i] = bounds
  214. // }
  215. // }
  216. // A Histogram counts individual observations from an event or sample stream in
  217. // configurable static buckets (or in dynamic sparse buckets as part of the
  218. // experimental Native Histograms, see below for more details). Similar to a
  219. // Summary, it also provides a sum of observations and an observation count.
  220. //
  221. // On the Prometheus server, quantiles can be calculated from a Histogram using
  222. // the histogram_quantile PromQL function.
  223. //
  224. // Note that Histograms, in contrast to Summaries, can be aggregated in PromQL
  225. // (see the documentation for detailed procedures). However, Histograms require
  226. // the user to pre-define suitable buckets, and they are in general less
  227. // accurate. (Both problems are addressed by the experimental Native
  228. // Histograms. To use them, configure a NativeHistogramBucketFactor in the
  229. // HistogramOpts. They also require a Prometheus server v2.40+ with the
  230. // corresponding feature flag enabled.)
  231. //
  232. // The Observe method of a Histogram has a very low performance overhead in
  233. // comparison with the Observe method of a Summary.
  234. //
  235. // To create Histogram instances, use NewHistogram.
  236. type Histogram interface {
  237. Metric
  238. Collector
  239. // Observe adds a single observation to the histogram. Observations are
  240. // usually positive or zero. Negative observations are accepted but
  241. // prevent current versions of Prometheus from properly detecting
  242. // counter resets in the sum of observations. (The experimental Native
  243. // Histograms handle negative observations properly.) See
  244. // https://prometheus.io/docs/practices/histograms/#count-and-sum-of-observations
  245. // for details.
  246. Observe(float64)
  247. }
  248. // bucketLabel is used for the label that defines the upper bound of a
  249. // bucket of a histogram ("le" -> "less or equal").
  250. const bucketLabel = "le"
  251. // DefBuckets are the default Histogram buckets. The default buckets are
  252. // tailored to broadly measure the response time (in seconds) of a network
  253. // service. Most likely, however, you will be required to define buckets
  254. // customized to your use case.
  255. var DefBuckets = []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10}
  256. // DefNativeHistogramZeroThreshold is the default value for
  257. // NativeHistogramZeroThreshold in the HistogramOpts.
  258. //
  259. // The value is 2^-128 (or 0.5*2^-127 in the actual IEEE 754 representation),
  260. // which is a bucket boundary at all possible resolutions.
  261. const DefNativeHistogramZeroThreshold = 2.938735877055719e-39
  262. // NativeHistogramZeroThresholdZero can be used as NativeHistogramZeroThreshold
  263. // in the HistogramOpts to create a zero bucket of width zero, i.e. a zero
  264. // bucket that only receives observations of precisely zero.
  265. const NativeHistogramZeroThresholdZero = -1
  266. var errBucketLabelNotAllowed = fmt.Errorf(
  267. "%q is not allowed as label name in histograms", bucketLabel,
  268. )
  269. // LinearBuckets creates 'count' regular buckets, each 'width' wide, where the
  270. // lowest bucket has an upper bound of 'start'. The final +Inf bucket is not
  271. // counted and not included in the returned slice. The returned slice is meant
  272. // to be used for the Buckets field of HistogramOpts.
  273. //
  274. // The function panics if 'count' is zero or negative.
  275. func LinearBuckets(start, width float64, count int) []float64 {
  276. if count < 1 {
  277. panic("LinearBuckets needs a positive count")
  278. }
  279. buckets := make([]float64, count)
  280. for i := range buckets {
  281. buckets[i] = start
  282. start += width
  283. }
  284. return buckets
  285. }
  286. // ExponentialBuckets creates 'count' regular buckets, where the lowest bucket
  287. // has an upper bound of 'start' and each following bucket's upper bound is
  288. // 'factor' times the previous bucket's upper bound. The final +Inf bucket is
  289. // not counted and not included in the returned slice. The returned slice is
  290. // meant to be used for the Buckets field of HistogramOpts.
  291. //
  292. // The function panics if 'count' is 0 or negative, if 'start' is 0 or negative,
  293. // or if 'factor' is less than or equal 1.
  294. func ExponentialBuckets(start, factor float64, count int) []float64 {
  295. if count < 1 {
  296. panic("ExponentialBuckets needs a positive count")
  297. }
  298. if start <= 0 {
  299. panic("ExponentialBuckets needs a positive start value")
  300. }
  301. if factor <= 1 {
  302. panic("ExponentialBuckets needs a factor greater than 1")
  303. }
  304. buckets := make([]float64, count)
  305. for i := range buckets {
  306. buckets[i] = start
  307. start *= factor
  308. }
  309. return buckets
  310. }
  311. // ExponentialBucketsRange creates 'count' buckets, where the lowest bucket is
  312. // 'min' and the highest bucket is 'max'. The final +Inf bucket is not counted
  313. // and not included in the returned slice. The returned slice is meant to be
  314. // used for the Buckets field of HistogramOpts.
  315. //
  316. // The function panics if 'count' is 0 or negative, if 'min' is 0 or negative.
  317. func ExponentialBucketsRange(min, max float64, count int) []float64 {
  318. if count < 1 {
  319. panic("ExponentialBucketsRange count needs a positive count")
  320. }
  321. if min <= 0 {
  322. panic("ExponentialBucketsRange min needs to be greater than 0")
  323. }
  324. // Formula for exponential buckets.
  325. // max = min*growthFactor^(bucketCount-1)
  326. // We know max/min and highest bucket. Solve for growthFactor.
  327. growthFactor := math.Pow(max/min, 1.0/float64(count-1))
  328. // Now that we know growthFactor, solve for each bucket.
  329. buckets := make([]float64, count)
  330. for i := 1; i <= count; i++ {
  331. buckets[i-1] = min * math.Pow(growthFactor, float64(i-1))
  332. }
  333. return buckets
  334. }
  335. // HistogramOpts bundles the options for creating a Histogram metric. It is
  336. // mandatory to set Name to a non-empty string. All other fields are optional
  337. // and can safely be left at their zero value, although it is strongly
  338. // encouraged to set a Help string.
  339. type HistogramOpts struct {
  340. // Namespace, Subsystem, and Name are components of the fully-qualified
  341. // name of the Histogram (created by joining these components with
  342. // "_"). Only Name is mandatory, the others merely help structuring the
  343. // name. Note that the fully-qualified name of the Histogram must be a
  344. // valid Prometheus metric name.
  345. Namespace string
  346. Subsystem string
  347. Name string
  348. // Help provides information about this Histogram.
  349. //
  350. // Metrics with the same fully-qualified name must have the same Help
  351. // string.
  352. Help string
  353. // ConstLabels are used to attach fixed labels to this metric. Metrics
  354. // with the same fully-qualified name must have the same label names in
  355. // their ConstLabels.
  356. //
  357. // ConstLabels are only used rarely. In particular, do not use them to
  358. // attach the same labels to all your metrics. Those use cases are
  359. // better covered by target labels set by the scraping Prometheus
  360. // server, or by one specific metric (e.g. a build_info or a
  361. // machine_role metric). See also
  362. // https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels-not-static-scraped-labels
  363. ConstLabels Labels
  364. // Buckets defines the buckets into which observations are counted. Each
  365. // element in the slice is the upper inclusive bound of a bucket. The
  366. // values must be sorted in strictly increasing order. There is no need
  367. // to add a highest bucket with +Inf bound, it will be added
  368. // implicitly. If Buckets is left as nil or set to a slice of length
  369. // zero, it is replaced by default buckets. The default buckets are
  370. // DefBuckets if no buckets for a native histogram (see below) are used,
  371. // otherwise the default is no buckets. (In other words, if you want to
  372. // use both regular buckets and buckets for a native histogram, you have
  373. // to define the regular buckets here explicitly.)
  374. Buckets []float64
  375. // If NativeHistogramBucketFactor is greater than one, so-called sparse
  376. // buckets are used (in addition to the regular buckets, if defined
  377. // above). A Histogram with sparse buckets will be ingested as a Native
  378. // Histogram by a Prometheus server with that feature enabled (requires
  379. // Prometheus v2.40+). Sparse buckets are exponential buckets covering
  380. // the whole float64 range (with the exception of the “zero” bucket, see
  381. // NativeHistogramZeroThreshold below). From any one bucket to the next,
  382. // the width of the bucket grows by a constant
  383. // factor. NativeHistogramBucketFactor provides an upper bound for this
  384. // factor (exception see below). The smaller
  385. // NativeHistogramBucketFactor, the more buckets will be used and thus
  386. // the more costly the histogram will become. A generally good trade-off
  387. // between cost and accuracy is a value of 1.1 (each bucket is at most
  388. // 10% wider than the previous one), which will result in each power of
  389. // two divided into 8 buckets (e.g. there will be 8 buckets between 1
  390. // and 2, same as between 2 and 4, and 4 and 8, etc.).
  391. //
  392. // Details about the actually used factor: The factor is calculated as
  393. // 2^(2^-n), where n is an integer number between (and including) -4 and
  394. // 8. n is chosen so that the resulting factor is the largest that is
  395. // still smaller or equal to NativeHistogramBucketFactor. Note that the
  396. // smallest possible factor is therefore approx. 1.00271 (i.e. 2^(2^-8)
  397. // ). If NativeHistogramBucketFactor is greater than 1 but smaller than
  398. // 2^(2^-8), then the actually used factor is still 2^(2^-8) even though
  399. // it is larger than the provided NativeHistogramBucketFactor.
  400. //
  401. // NOTE: Native Histograms are still an experimental feature. Their
  402. // behavior might still change without a major version
  403. // bump. Subsequently, all NativeHistogram... options here might still
  404. // change their behavior or name (or might completely disappear) without
  405. // a major version bump.
  406. NativeHistogramBucketFactor float64
  407. // All observations with an absolute value of less or equal
  408. // NativeHistogramZeroThreshold are accumulated into a “zero” bucket.
  409. // For best results, this should be close to a bucket boundary. This is
  410. // usually the case if picking a power of two. If
  411. // NativeHistogramZeroThreshold is left at zero,
  412. // DefNativeHistogramZeroThreshold is used as the threshold. To
  413. // configure a zero bucket with an actual threshold of zero (i.e. only
  414. // observations of precisely zero will go into the zero bucket), set
  415. // NativeHistogramZeroThreshold to the NativeHistogramZeroThresholdZero
  416. // constant (or any negative float value).
  417. NativeHistogramZeroThreshold float64
  418. // The remaining fields define a strategy to limit the number of
  419. // populated sparse buckets. If NativeHistogramMaxBucketNumber is left
  420. // at zero, the number of buckets is not limited. (Note that this might
  421. // lead to unbounded memory consumption if the values observed by the
  422. // Histogram are sufficiently wide-spread. In particular, this could be
  423. // used as a DoS attack vector. Where the observed values depend on
  424. // external inputs, it is highly recommended to set a
  425. // NativeHistogramMaxBucketNumber.) Once the set
  426. // NativeHistogramMaxBucketNumber is exceeded, the following strategy is
  427. // enacted:
  428. // - First, if the last reset (or the creation) of the histogram is at
  429. // least NativeHistogramMinResetDuration ago, then the whole
  430. // histogram is reset to its initial state (including regular
  431. // buckets).
  432. // - If less time has passed, or if NativeHistogramMinResetDuration is
  433. // zero, no reset is performed. Instead, the zero threshold is
  434. // increased sufficiently to reduce the number of buckets to or below
  435. // NativeHistogramMaxBucketNumber, but not to more than
  436. // NativeHistogramMaxZeroThreshold. Thus, if
  437. // NativeHistogramMaxZeroThreshold is already at or below the current
  438. // zero threshold, nothing happens at this step.
  439. // - After that, if the number of buckets still exceeds
  440. // NativeHistogramMaxBucketNumber, the resolution of the histogram is
  441. // reduced by doubling the width of the sparse buckets (up to a
  442. // growth factor between one bucket to the next of 2^(2^4) = 65536,
  443. // see above).
  444. // - Any increased zero threshold or reduced resolution is reset back
  445. // to their original values once NativeHistogramMinResetDuration has
  446. // passed (since the last reset or the creation of the histogram).
  447. NativeHistogramMaxBucketNumber uint32
  448. NativeHistogramMinResetDuration time.Duration
  449. NativeHistogramMaxZeroThreshold float64
  450. // now is for testing purposes, by default it's time.Now.
  451. now func() time.Time
  452. }
  453. // HistogramVecOpts bundles the options to create a HistogramVec metric.
  454. // It is mandatory to set HistogramOpts, see there for mandatory fields. VariableLabels
  455. // is optional and can safely be left to its default value.
  456. type HistogramVecOpts struct {
  457. HistogramOpts
  458. // VariableLabels are used to partition the metric vector by the given set
  459. // of labels. Each label value will be constrained with the optional Constraint
  460. // function, if provided.
  461. VariableLabels ConstrainableLabels
  462. }
  463. // NewHistogram creates a new Histogram based on the provided HistogramOpts. It
  464. // panics if the buckets in HistogramOpts are not in strictly increasing order.
  465. //
  466. // The returned implementation also implements ExemplarObserver. It is safe to
  467. // perform the corresponding type assertion. Exemplars are tracked separately
  468. // for each bucket.
  469. func NewHistogram(opts HistogramOpts) Histogram {
  470. return newHistogram(
  471. NewDesc(
  472. BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
  473. opts.Help,
  474. nil,
  475. opts.ConstLabels,
  476. ),
  477. opts,
  478. )
  479. }
  480. func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogram {
  481. if len(desc.variableLabels.names) != len(labelValues) {
  482. panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.names, labelValues))
  483. }
  484. for _, n := range desc.variableLabels.names {
  485. if n == bucketLabel {
  486. panic(errBucketLabelNotAllowed)
  487. }
  488. }
  489. for _, lp := range desc.constLabelPairs {
  490. if lp.GetName() == bucketLabel {
  491. panic(errBucketLabelNotAllowed)
  492. }
  493. }
  494. if opts.now == nil {
  495. opts.now = time.Now
  496. }
  497. h := &histogram{
  498. desc: desc,
  499. upperBounds: opts.Buckets,
  500. labelPairs: MakeLabelPairs(desc, labelValues),
  501. nativeHistogramMaxBuckets: opts.NativeHistogramMaxBucketNumber,
  502. nativeHistogramMaxZeroThreshold: opts.NativeHistogramMaxZeroThreshold,
  503. nativeHistogramMinResetDuration: opts.NativeHistogramMinResetDuration,
  504. lastResetTime: opts.now(),
  505. now: opts.now,
  506. }
  507. if len(h.upperBounds) == 0 && opts.NativeHistogramBucketFactor <= 1 {
  508. h.upperBounds = DefBuckets
  509. }
  510. if opts.NativeHistogramBucketFactor <= 1 {
  511. h.nativeHistogramSchema = math.MinInt32 // To mark that there are no sparse buckets.
  512. } else {
  513. switch {
  514. case opts.NativeHistogramZeroThreshold > 0:
  515. h.nativeHistogramZeroThreshold = opts.NativeHistogramZeroThreshold
  516. case opts.NativeHistogramZeroThreshold == 0:
  517. h.nativeHistogramZeroThreshold = DefNativeHistogramZeroThreshold
  518. } // Leave h.nativeHistogramZeroThreshold at 0 otherwise.
  519. h.nativeHistogramSchema = pickSchema(opts.NativeHistogramBucketFactor)
  520. }
  521. for i, upperBound := range h.upperBounds {
  522. if i < len(h.upperBounds)-1 {
  523. if upperBound >= h.upperBounds[i+1] {
  524. panic(fmt.Errorf(
  525. "histogram buckets must be in increasing order: %f >= %f",
  526. upperBound, h.upperBounds[i+1],
  527. ))
  528. }
  529. } else {
  530. if math.IsInf(upperBound, +1) {
  531. // The +Inf bucket is implicit. Remove it here.
  532. h.upperBounds = h.upperBounds[:i]
  533. }
  534. }
  535. }
  536. // Finally we know the final length of h.upperBounds and can make buckets
  537. // for both counts as well as exemplars:
  538. h.counts[0] = &histogramCounts{buckets: make([]uint64, len(h.upperBounds))}
  539. atomic.StoreUint64(&h.counts[0].nativeHistogramZeroThresholdBits, math.Float64bits(h.nativeHistogramZeroThreshold))
  540. atomic.StoreInt32(&h.counts[0].nativeHistogramSchema, h.nativeHistogramSchema)
  541. h.counts[1] = &histogramCounts{buckets: make([]uint64, len(h.upperBounds))}
  542. atomic.StoreUint64(&h.counts[1].nativeHistogramZeroThresholdBits, math.Float64bits(h.nativeHistogramZeroThreshold))
  543. atomic.StoreInt32(&h.counts[1].nativeHistogramSchema, h.nativeHistogramSchema)
  544. h.exemplars = make([]atomic.Value, len(h.upperBounds)+1)
  545. h.init(h) // Init self-collection.
  546. return h
  547. }
  548. type histogramCounts struct {
  549. // Order in this struct matters for the alignment required by atomic
  550. // operations, see http://golang.org/pkg/sync/atomic/#pkg-note-BUG
  551. // sumBits contains the bits of the float64 representing the sum of all
  552. // observations.
  553. sumBits uint64
  554. count uint64
  555. // nativeHistogramZeroBucket counts all (positive and negative)
  556. // observations in the zero bucket (with an absolute value less or equal
  557. // the current threshold, see next field.
  558. nativeHistogramZeroBucket uint64
  559. // nativeHistogramZeroThresholdBits is the bit pattern of the current
  560. // threshold for the zero bucket. It's initially equal to
  561. // nativeHistogramZeroThreshold but may change according to the bucket
  562. // count limitation strategy.
  563. nativeHistogramZeroThresholdBits uint64
  564. // nativeHistogramSchema may change over time according to the bucket
  565. // count limitation strategy and therefore has to be saved here.
  566. nativeHistogramSchema int32
  567. // Number of (positive and negative) sparse buckets.
  568. nativeHistogramBucketsNumber uint32
  569. // Regular buckets.
  570. buckets []uint64
  571. // The sparse buckets for native histograms are implemented with a
  572. // sync.Map for now. A dedicated data structure will likely be more
  573. // efficient. There are separate maps for negative and positive
  574. // observations. The map's value is an *int64, counting observations in
  575. // that bucket. (Note that we don't use uint64 as an int64 won't
  576. // overflow in practice, and working with signed numbers from the
  577. // beginning simplifies the handling of deltas.) The map's key is the
  578. // index of the bucket according to the used
  579. // nativeHistogramSchema. Index 0 is for an upper bound of 1.
  580. nativeHistogramBucketsPositive, nativeHistogramBucketsNegative sync.Map
  581. }
  582. // observe manages the parts of observe that only affects
  583. // histogramCounts. doSparse is true if sparse buckets should be done,
  584. // too.
  585. func (hc *histogramCounts) observe(v float64, bucket int, doSparse bool) {
  586. if bucket < len(hc.buckets) {
  587. atomic.AddUint64(&hc.buckets[bucket], 1)
  588. }
  589. atomicAddFloat(&hc.sumBits, v)
  590. if doSparse && !math.IsNaN(v) {
  591. var (
  592. key int
  593. schema = atomic.LoadInt32(&hc.nativeHistogramSchema)
  594. zeroThreshold = math.Float64frombits(atomic.LoadUint64(&hc.nativeHistogramZeroThresholdBits))
  595. bucketCreated, isInf bool
  596. )
  597. if math.IsInf(v, 0) {
  598. // Pretend v is MaxFloat64 but later increment key by one.
  599. if math.IsInf(v, +1) {
  600. v = math.MaxFloat64
  601. } else {
  602. v = -math.MaxFloat64
  603. }
  604. isInf = true
  605. }
  606. frac, exp := math.Frexp(math.Abs(v))
  607. if schema > 0 {
  608. bounds := nativeHistogramBounds[schema]
  609. key = sort.SearchFloat64s(bounds, frac) + (exp-1)*len(bounds)
  610. } else {
  611. key = exp
  612. if frac == 0.5 {
  613. key--
  614. }
  615. offset := (1 << -schema) - 1
  616. key = (key + offset) >> -schema
  617. }
  618. if isInf {
  619. key++
  620. }
  621. switch {
  622. case v > zeroThreshold:
  623. bucketCreated = addToBucket(&hc.nativeHistogramBucketsPositive, key, 1)
  624. case v < -zeroThreshold:
  625. bucketCreated = addToBucket(&hc.nativeHistogramBucketsNegative, key, 1)
  626. default:
  627. atomic.AddUint64(&hc.nativeHistogramZeroBucket, 1)
  628. }
  629. if bucketCreated {
  630. atomic.AddUint32(&hc.nativeHistogramBucketsNumber, 1)
  631. }
  632. }
  633. // Increment count last as we take it as a signal that the observation
  634. // is complete.
  635. atomic.AddUint64(&hc.count, 1)
  636. }
  637. type histogram struct {
  638. // countAndHotIdx enables lock-free writes with use of atomic updates.
  639. // The most significant bit is the hot index [0 or 1] of the count field
  640. // below. Observe calls update the hot one. All remaining bits count the
  641. // number of Observe calls. Observe starts by incrementing this counter,
  642. // and finish by incrementing the count field in the respective
  643. // histogramCounts, as a marker for completion.
  644. //
  645. // Calls of the Write method (which are non-mutating reads from the
  646. // perspective of the histogram) swap the hot–cold under the writeMtx
  647. // lock. A cooldown is awaited (while locked) by comparing the number of
  648. // observations with the initiation count. Once they match, then the
  649. // last observation on the now cool one has completed. All cold fields must
  650. // be merged into the new hot before releasing writeMtx.
  651. //
  652. // Fields with atomic access first! See alignment constraint:
  653. // http://golang.org/pkg/sync/atomic/#pkg-note-BUG
  654. countAndHotIdx uint64
  655. selfCollector
  656. desc *Desc
  657. // Only used in the Write method and for sparse bucket management.
  658. mtx sync.Mutex
  659. // Two counts, one is "hot" for lock-free observations, the other is
  660. // "cold" for writing out a dto.Metric. It has to be an array of
  661. // pointers to guarantee 64bit alignment of the histogramCounts, see
  662. // http://golang.org/pkg/sync/atomic/#pkg-note-BUG.
  663. counts [2]*histogramCounts
  664. upperBounds []float64
  665. labelPairs []*dto.LabelPair
  666. exemplars []atomic.Value // One more than buckets (to include +Inf), each a *dto.Exemplar.
  667. nativeHistogramSchema int32 // The initial schema. Set to math.MinInt32 if no sparse buckets are used.
  668. nativeHistogramZeroThreshold float64 // The initial zero threshold.
  669. nativeHistogramMaxZeroThreshold float64
  670. nativeHistogramMaxBuckets uint32
  671. nativeHistogramMinResetDuration time.Duration
  672. // lastResetTime is protected by mtx. It is also used as created timestamp.
  673. lastResetTime time.Time
  674. // now is for testing purposes, by default it's time.Now.
  675. now func() time.Time
  676. }
  677. func (h *histogram) Desc() *Desc {
  678. return h.desc
  679. }
  680. func (h *histogram) Observe(v float64) {
  681. h.observe(v, h.findBucket(v))
  682. }
  683. func (h *histogram) ObserveWithExemplar(v float64, e Labels) {
  684. i := h.findBucket(v)
  685. h.observe(v, i)
  686. h.updateExemplar(v, i, e)
  687. }
  688. func (h *histogram) Write(out *dto.Metric) error {
  689. // For simplicity, we protect this whole method by a mutex. It is not in
  690. // the hot path, i.e. Observe is called much more often than Write. The
  691. // complication of making Write lock-free isn't worth it, if possible at
  692. // all.
  693. h.mtx.Lock()
  694. defer h.mtx.Unlock()
  695. // Adding 1<<63 switches the hot index (from 0 to 1 or from 1 to 0)
  696. // without touching the count bits. See the struct comments for a full
  697. // description of the algorithm.
  698. n := atomic.AddUint64(&h.countAndHotIdx, 1<<63)
  699. // count is contained unchanged in the lower 63 bits.
  700. count := n & ((1 << 63) - 1)
  701. // The most significant bit tells us which counts is hot. The complement
  702. // is thus the cold one.
  703. hotCounts := h.counts[n>>63]
  704. coldCounts := h.counts[(^n)>>63]
  705. waitForCooldown(count, coldCounts)
  706. his := &dto.Histogram{
  707. Bucket: make([]*dto.Bucket, len(h.upperBounds)),
  708. SampleCount: proto.Uint64(count),
  709. SampleSum: proto.Float64(math.Float64frombits(atomic.LoadUint64(&coldCounts.sumBits))),
  710. CreatedTimestamp: timestamppb.New(h.lastResetTime),
  711. }
  712. out.Histogram = his
  713. out.Label = h.labelPairs
  714. var cumCount uint64
  715. for i, upperBound := range h.upperBounds {
  716. cumCount += atomic.LoadUint64(&coldCounts.buckets[i])
  717. his.Bucket[i] = &dto.Bucket{
  718. CumulativeCount: proto.Uint64(cumCount),
  719. UpperBound: proto.Float64(upperBound),
  720. }
  721. if e := h.exemplars[i].Load(); e != nil {
  722. his.Bucket[i].Exemplar = e.(*dto.Exemplar)
  723. }
  724. }
  725. // If there is an exemplar for the +Inf bucket, we have to add that bucket explicitly.
  726. if e := h.exemplars[len(h.upperBounds)].Load(); e != nil {
  727. b := &dto.Bucket{
  728. CumulativeCount: proto.Uint64(count),
  729. UpperBound: proto.Float64(math.Inf(1)),
  730. Exemplar: e.(*dto.Exemplar),
  731. }
  732. his.Bucket = append(his.Bucket, b)
  733. }
  734. if h.nativeHistogramSchema > math.MinInt32 {
  735. his.ZeroThreshold = proto.Float64(math.Float64frombits(atomic.LoadUint64(&coldCounts.nativeHistogramZeroThresholdBits)))
  736. his.Schema = proto.Int32(atomic.LoadInt32(&coldCounts.nativeHistogramSchema))
  737. zeroBucket := atomic.LoadUint64(&coldCounts.nativeHistogramZeroBucket)
  738. defer func() {
  739. coldCounts.nativeHistogramBucketsPositive.Range(addAndReset(&hotCounts.nativeHistogramBucketsPositive, &hotCounts.nativeHistogramBucketsNumber))
  740. coldCounts.nativeHistogramBucketsNegative.Range(addAndReset(&hotCounts.nativeHistogramBucketsNegative, &hotCounts.nativeHistogramBucketsNumber))
  741. }()
  742. his.ZeroCount = proto.Uint64(zeroBucket)
  743. his.NegativeSpan, his.NegativeDelta = makeBuckets(&coldCounts.nativeHistogramBucketsNegative)
  744. his.PositiveSpan, his.PositiveDelta = makeBuckets(&coldCounts.nativeHistogramBucketsPositive)
  745. // Add a no-op span to a histogram without observations and with
  746. // a zero threshold of zero. Otherwise, a native histogram would
  747. // look like a classic histogram to scrapers.
  748. if *his.ZeroThreshold == 0 && *his.ZeroCount == 0 && len(his.PositiveSpan) == 0 && len(his.NegativeSpan) == 0 {
  749. his.PositiveSpan = []*dto.BucketSpan{{
  750. Offset: proto.Int32(0),
  751. Length: proto.Uint32(0),
  752. }}
  753. }
  754. }
  755. addAndResetCounts(hotCounts, coldCounts)
  756. return nil
  757. }
  758. // findBucket returns the index of the bucket for the provided value, or
  759. // len(h.upperBounds) for the +Inf bucket.
  760. func (h *histogram) findBucket(v float64) int {
  761. // TODO(beorn7): For small numbers of buckets (<30), a linear search is
  762. // slightly faster than the binary search. If we really care, we could
  763. // switch from one search strategy to the other depending on the number
  764. // of buckets.
  765. //
  766. // Microbenchmarks (BenchmarkHistogramNoLabels):
  767. // 11 buckets: 38.3 ns/op linear - binary 48.7 ns/op
  768. // 100 buckets: 78.1 ns/op linear - binary 54.9 ns/op
  769. // 300 buckets: 154 ns/op linear - binary 61.6 ns/op
  770. return sort.SearchFloat64s(h.upperBounds, v)
  771. }
  772. // observe is the implementation for Observe without the findBucket part.
  773. func (h *histogram) observe(v float64, bucket int) {
  774. // Do not add to sparse buckets for NaN observations.
  775. doSparse := h.nativeHistogramSchema > math.MinInt32 && !math.IsNaN(v)
  776. // We increment h.countAndHotIdx so that the counter in the lower
  777. // 63 bits gets incremented. At the same time, we get the new value
  778. // back, which we can use to find the currently-hot counts.
  779. n := atomic.AddUint64(&h.countAndHotIdx, 1)
  780. hotCounts := h.counts[n>>63]
  781. hotCounts.observe(v, bucket, doSparse)
  782. if doSparse {
  783. h.limitBuckets(hotCounts, v, bucket)
  784. }
  785. }
  786. // limitBuckets applies a strategy to limit the number of populated sparse
  787. // buckets. It's generally best effort, and there are situations where the
  788. // number can go higher (if even the lowest resolution isn't enough to reduce
  789. // the number sufficiently, or if the provided counts aren't fully updated yet
  790. // by a concurrently happening Write call).
  791. func (h *histogram) limitBuckets(counts *histogramCounts, value float64, bucket int) {
  792. if h.nativeHistogramMaxBuckets == 0 {
  793. return // No limit configured.
  794. }
  795. if h.nativeHistogramMaxBuckets >= atomic.LoadUint32(&counts.nativeHistogramBucketsNumber) {
  796. return // Bucket limit not exceeded yet.
  797. }
  798. h.mtx.Lock()
  799. defer h.mtx.Unlock()
  800. // The hot counts might have been swapped just before we acquired the
  801. // lock. Re-fetch the hot counts first...
  802. n := atomic.LoadUint64(&h.countAndHotIdx)
  803. hotIdx := n >> 63
  804. coldIdx := (^n) >> 63
  805. hotCounts := h.counts[hotIdx]
  806. coldCounts := h.counts[coldIdx]
  807. // ...and then check again if we really have to reduce the bucket count.
  808. if h.nativeHistogramMaxBuckets >= atomic.LoadUint32(&hotCounts.nativeHistogramBucketsNumber) {
  809. return // Bucket limit not exceeded after all.
  810. }
  811. // Try the various strategies in order.
  812. if h.maybeReset(hotCounts, coldCounts, coldIdx, value, bucket) {
  813. return
  814. }
  815. if h.maybeWidenZeroBucket(hotCounts, coldCounts) {
  816. return
  817. }
  818. h.doubleBucketWidth(hotCounts, coldCounts)
  819. }
  820. // maybeReset resets the whole histogram if at least h.nativeHistogramMinResetDuration
  821. // has been passed. It returns true if the histogram has been reset. The caller
  822. // must have locked h.mtx.
  823. func (h *histogram) maybeReset(
  824. hot, cold *histogramCounts, coldIdx uint64, value float64, bucket int,
  825. ) bool {
  826. // We are using the possibly mocked h.now() rather than
  827. // time.Since(h.lastResetTime) to enable testing.
  828. if h.nativeHistogramMinResetDuration == 0 ||
  829. h.now().Sub(h.lastResetTime) < h.nativeHistogramMinResetDuration {
  830. return false
  831. }
  832. // Completely reset coldCounts.
  833. h.resetCounts(cold)
  834. // Repeat the latest observation to not lose it completely.
  835. cold.observe(value, bucket, true)
  836. // Make coldCounts the new hot counts while resetting countAndHotIdx.
  837. n := atomic.SwapUint64(&h.countAndHotIdx, (coldIdx<<63)+1)
  838. count := n & ((1 << 63) - 1)
  839. waitForCooldown(count, hot)
  840. // Finally, reset the formerly hot counts, too.
  841. h.resetCounts(hot)
  842. h.lastResetTime = h.now()
  843. return true
  844. }
  845. // maybeWidenZeroBucket widens the zero bucket until it includes the existing
  846. // buckets closest to the zero bucket (which could be two, if an equidistant
  847. // negative and a positive bucket exists, but usually it's only one bucket to be
  848. // merged into the new wider zero bucket). h.nativeHistogramMaxZeroThreshold
  849. // limits how far the zero bucket can be extended, and if that's not enough to
  850. // include an existing bucket, the method returns false. The caller must have
  851. // locked h.mtx.
  852. func (h *histogram) maybeWidenZeroBucket(hot, cold *histogramCounts) bool {
  853. currentZeroThreshold := math.Float64frombits(atomic.LoadUint64(&hot.nativeHistogramZeroThresholdBits))
  854. if currentZeroThreshold >= h.nativeHistogramMaxZeroThreshold {
  855. return false
  856. }
  857. // Find the key of the bucket closest to zero.
  858. smallestKey := findSmallestKey(&hot.nativeHistogramBucketsPositive)
  859. smallestNegativeKey := findSmallestKey(&hot.nativeHistogramBucketsNegative)
  860. if smallestNegativeKey < smallestKey {
  861. smallestKey = smallestNegativeKey
  862. }
  863. if smallestKey == math.MaxInt32 {
  864. return false
  865. }
  866. newZeroThreshold := getLe(smallestKey, atomic.LoadInt32(&hot.nativeHistogramSchema))
  867. if newZeroThreshold > h.nativeHistogramMaxZeroThreshold {
  868. return false // New threshold would exceed the max threshold.
  869. }
  870. atomic.StoreUint64(&cold.nativeHistogramZeroThresholdBits, math.Float64bits(newZeroThreshold))
  871. // Remove applicable buckets.
  872. if _, loaded := cold.nativeHistogramBucketsNegative.LoadAndDelete(smallestKey); loaded {
  873. atomicDecUint32(&cold.nativeHistogramBucketsNumber)
  874. }
  875. if _, loaded := cold.nativeHistogramBucketsPositive.LoadAndDelete(smallestKey); loaded {
  876. atomicDecUint32(&cold.nativeHistogramBucketsNumber)
  877. }
  878. // Make cold counts the new hot counts.
  879. n := atomic.AddUint64(&h.countAndHotIdx, 1<<63)
  880. count := n & ((1 << 63) - 1)
  881. // Swap the pointer names to represent the new roles and make
  882. // the rest less confusing.
  883. hot, cold = cold, hot
  884. waitForCooldown(count, cold)
  885. // Add all the now cold counts to the new hot counts...
  886. addAndResetCounts(hot, cold)
  887. // ...adjust the new zero threshold in the cold counts, too...
  888. atomic.StoreUint64(&cold.nativeHistogramZeroThresholdBits, math.Float64bits(newZeroThreshold))
  889. // ...and then merge the newly deleted buckets into the wider zero
  890. // bucket.
  891. mergeAndDeleteOrAddAndReset := func(hotBuckets, coldBuckets *sync.Map) func(k, v interface{}) bool {
  892. return func(k, v interface{}) bool {
  893. key := k.(int)
  894. bucket := v.(*int64)
  895. if key == smallestKey {
  896. // Merge into hot zero bucket...
  897. atomic.AddUint64(&hot.nativeHistogramZeroBucket, uint64(atomic.LoadInt64(bucket)))
  898. // ...and delete from cold counts.
  899. coldBuckets.Delete(key)
  900. atomicDecUint32(&cold.nativeHistogramBucketsNumber)
  901. } else {
  902. // Add to corresponding hot bucket...
  903. if addToBucket(hotBuckets, key, atomic.LoadInt64(bucket)) {
  904. atomic.AddUint32(&hot.nativeHistogramBucketsNumber, 1)
  905. }
  906. // ...and reset cold bucket.
  907. atomic.StoreInt64(bucket, 0)
  908. }
  909. return true
  910. }
  911. }
  912. cold.nativeHistogramBucketsPositive.Range(mergeAndDeleteOrAddAndReset(&hot.nativeHistogramBucketsPositive, &cold.nativeHistogramBucketsPositive))
  913. cold.nativeHistogramBucketsNegative.Range(mergeAndDeleteOrAddAndReset(&hot.nativeHistogramBucketsNegative, &cold.nativeHistogramBucketsNegative))
  914. return true
  915. }
  916. // doubleBucketWidth doubles the bucket width (by decrementing the schema
  917. // number). Note that very sparse buckets could lead to a low reduction of the
  918. // bucket count (or even no reduction at all). The method does nothing if the
  919. // schema is already -4.
  920. func (h *histogram) doubleBucketWidth(hot, cold *histogramCounts) {
  921. coldSchema := atomic.LoadInt32(&cold.nativeHistogramSchema)
  922. if coldSchema == -4 {
  923. return // Already at lowest resolution.
  924. }
  925. coldSchema--
  926. atomic.StoreInt32(&cold.nativeHistogramSchema, coldSchema)
  927. // Play it simple and just delete all cold buckets.
  928. atomic.StoreUint32(&cold.nativeHistogramBucketsNumber, 0)
  929. deleteSyncMap(&cold.nativeHistogramBucketsNegative)
  930. deleteSyncMap(&cold.nativeHistogramBucketsPositive)
  931. // Make coldCounts the new hot counts.
  932. n := atomic.AddUint64(&h.countAndHotIdx, 1<<63)
  933. count := n & ((1 << 63) - 1)
  934. // Swap the pointer names to represent the new roles and make
  935. // the rest less confusing.
  936. hot, cold = cold, hot
  937. waitForCooldown(count, cold)
  938. // Add all the now cold counts to the new hot counts...
  939. addAndResetCounts(hot, cold)
  940. // ...adjust the schema in the cold counts, too...
  941. atomic.StoreInt32(&cold.nativeHistogramSchema, coldSchema)
  942. // ...and then merge the cold buckets into the wider hot buckets.
  943. merge := func(hotBuckets *sync.Map) func(k, v interface{}) bool {
  944. return func(k, v interface{}) bool {
  945. key := k.(int)
  946. bucket := v.(*int64)
  947. // Adjust key to match the bucket to merge into.
  948. if key > 0 {
  949. key++
  950. }
  951. key /= 2
  952. // Add to corresponding hot bucket.
  953. if addToBucket(hotBuckets, key, atomic.LoadInt64(bucket)) {
  954. atomic.AddUint32(&hot.nativeHistogramBucketsNumber, 1)
  955. }
  956. return true
  957. }
  958. }
  959. cold.nativeHistogramBucketsPositive.Range(merge(&hot.nativeHistogramBucketsPositive))
  960. cold.nativeHistogramBucketsNegative.Range(merge(&hot.nativeHistogramBucketsNegative))
  961. // Play it simple again and just delete all cold buckets.
  962. atomic.StoreUint32(&cold.nativeHistogramBucketsNumber, 0)
  963. deleteSyncMap(&cold.nativeHistogramBucketsNegative)
  964. deleteSyncMap(&cold.nativeHistogramBucketsPositive)
  965. }
  966. func (h *histogram) resetCounts(counts *histogramCounts) {
  967. atomic.StoreUint64(&counts.sumBits, 0)
  968. atomic.StoreUint64(&counts.count, 0)
  969. atomic.StoreUint64(&counts.nativeHistogramZeroBucket, 0)
  970. atomic.StoreUint64(&counts.nativeHistogramZeroThresholdBits, math.Float64bits(h.nativeHistogramZeroThreshold))
  971. atomic.StoreInt32(&counts.nativeHistogramSchema, h.nativeHistogramSchema)
  972. atomic.StoreUint32(&counts.nativeHistogramBucketsNumber, 0)
  973. for i := range h.upperBounds {
  974. atomic.StoreUint64(&counts.buckets[i], 0)
  975. }
  976. deleteSyncMap(&counts.nativeHistogramBucketsNegative)
  977. deleteSyncMap(&counts.nativeHistogramBucketsPositive)
  978. }
  979. // updateExemplar replaces the exemplar for the provided bucket. With empty
  980. // labels, it's a no-op. It panics if any of the labels is invalid.
  981. func (h *histogram) updateExemplar(v float64, bucket int, l Labels) {
  982. if l == nil {
  983. return
  984. }
  985. e, err := newExemplar(v, h.now(), l)
  986. if err != nil {
  987. panic(err)
  988. }
  989. h.exemplars[bucket].Store(e)
  990. }
  991. // HistogramVec is a Collector that bundles a set of Histograms that all share the
  992. // same Desc, but have different values for their variable labels. This is used
  993. // if you want to count the same thing partitioned by various dimensions
  994. // (e.g. HTTP request latencies, partitioned by status code and method). Create
  995. // instances with NewHistogramVec.
  996. type HistogramVec struct {
  997. *MetricVec
  998. }
  999. // NewHistogramVec creates a new HistogramVec based on the provided HistogramOpts and
  1000. // partitioned by the given label names.
  1001. func NewHistogramVec(opts HistogramOpts, labelNames []string) *HistogramVec {
  1002. return V2.NewHistogramVec(HistogramVecOpts{
  1003. HistogramOpts: opts,
  1004. VariableLabels: UnconstrainedLabels(labelNames),
  1005. })
  1006. }
  1007. // NewHistogramVec creates a new HistogramVec based on the provided HistogramVecOpts.
  1008. func (v2) NewHistogramVec(opts HistogramVecOpts) *HistogramVec {
  1009. desc := V2.NewDesc(
  1010. BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
  1011. opts.Help,
  1012. opts.VariableLabels,
  1013. opts.ConstLabels,
  1014. )
  1015. return &HistogramVec{
  1016. MetricVec: NewMetricVec(desc, func(lvs ...string) Metric {
  1017. return newHistogram(desc, opts.HistogramOpts, lvs...)
  1018. }),
  1019. }
  1020. }
  1021. // GetMetricWithLabelValues returns the Histogram for the given slice of label
  1022. // values (same order as the variable labels in Desc). If that combination of
  1023. // label values is accessed for the first time, a new Histogram is created.
  1024. //
  1025. // It is possible to call this method without using the returned Histogram to only
  1026. // create the new Histogram but leave it at its starting value, a Histogram without
  1027. // any observations.
  1028. //
  1029. // Keeping the Histogram for later use is possible (and should be considered if
  1030. // performance is critical), but keep in mind that Reset, DeleteLabelValues and
  1031. // Delete can be used to delete the Histogram from the HistogramVec. In that case, the
  1032. // Histogram will still exist, but it will not be exported anymore, even if a
  1033. // Histogram with the same label values is created later. See also the CounterVec
  1034. // example.
  1035. //
  1036. // An error is returned if the number of label values is not the same as the
  1037. // number of variable labels in Desc (minus any curried labels).
  1038. //
  1039. // Note that for more than one label value, this method is prone to mistakes
  1040. // caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
  1041. // an alternative to avoid that type of mistake. For higher label numbers, the
  1042. // latter has a much more readable (albeit more verbose) syntax, but it comes
  1043. // with a performance overhead (for creating and processing the Labels map).
  1044. // See also the GaugeVec example.
  1045. func (v *HistogramVec) GetMetricWithLabelValues(lvs ...string) (Observer, error) {
  1046. metric, err := v.MetricVec.GetMetricWithLabelValues(lvs...)
  1047. if metric != nil {
  1048. return metric.(Observer), err
  1049. }
  1050. return nil, err
  1051. }
  1052. // GetMetricWith returns the Histogram for the given Labels map (the label names
  1053. // must match those of the variable labels in Desc). If that label map is
  1054. // accessed for the first time, a new Histogram is created. Implications of
  1055. // creating a Histogram without using it and keeping the Histogram for later use
  1056. // are the same as for GetMetricWithLabelValues.
  1057. //
  1058. // An error is returned if the number and names of the Labels are inconsistent
  1059. // with those of the variable labels in Desc (minus any curried labels).
  1060. //
  1061. // This method is used for the same purpose as
  1062. // GetMetricWithLabelValues(...string). See there for pros and cons of the two
  1063. // methods.
  1064. func (v *HistogramVec) GetMetricWith(labels Labels) (Observer, error) {
  1065. metric, err := v.MetricVec.GetMetricWith(labels)
  1066. if metric != nil {
  1067. return metric.(Observer), err
  1068. }
  1069. return nil, err
  1070. }
  1071. // WithLabelValues works as GetMetricWithLabelValues, but panics where
  1072. // GetMetricWithLabelValues would have returned an error. Not returning an
  1073. // error allows shortcuts like
  1074. //
  1075. // myVec.WithLabelValues("404", "GET").Observe(42.21)
  1076. func (v *HistogramVec) WithLabelValues(lvs ...string) Observer {
  1077. h, err := v.GetMetricWithLabelValues(lvs...)
  1078. if err != nil {
  1079. panic(err)
  1080. }
  1081. return h
  1082. }
  1083. // With works as GetMetricWith but panics where GetMetricWithLabels would have
  1084. // returned an error. Not returning an error allows shortcuts like
  1085. //
  1086. // myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Observe(42.21)
  1087. func (v *HistogramVec) With(labels Labels) Observer {
  1088. h, err := v.GetMetricWith(labels)
  1089. if err != nil {
  1090. panic(err)
  1091. }
  1092. return h
  1093. }
  1094. // CurryWith returns a vector curried with the provided labels, i.e. the
  1095. // returned vector has those labels pre-set for all labeled operations performed
  1096. // on it. The cardinality of the curried vector is reduced accordingly. The
  1097. // order of the remaining labels stays the same (just with the curried labels
  1098. // taken out of the sequence – which is relevant for the
  1099. // (GetMetric)WithLabelValues methods). It is possible to curry a curried
  1100. // vector, but only with labels not yet used for currying before.
  1101. //
  1102. // The metrics contained in the HistogramVec are shared between the curried and
  1103. // uncurried vectors. They are just accessed differently. Curried and uncurried
  1104. // vectors behave identically in terms of collection. Only one must be
  1105. // registered with a given registry (usually the uncurried version). The Reset
  1106. // method deletes all metrics, even if called on a curried vector.
  1107. func (v *HistogramVec) CurryWith(labels Labels) (ObserverVec, error) {
  1108. vec, err := v.MetricVec.CurryWith(labels)
  1109. if vec != nil {
  1110. return &HistogramVec{vec}, err
  1111. }
  1112. return nil, err
  1113. }
  1114. // MustCurryWith works as CurryWith but panics where CurryWith would have
  1115. // returned an error.
  1116. func (v *HistogramVec) MustCurryWith(labels Labels) ObserverVec {
  1117. vec, err := v.CurryWith(labels)
  1118. if err != nil {
  1119. panic(err)
  1120. }
  1121. return vec
  1122. }
  1123. type constHistogram struct {
  1124. desc *Desc
  1125. count uint64
  1126. sum float64
  1127. buckets map[float64]uint64
  1128. labelPairs []*dto.LabelPair
  1129. createdTs *timestamppb.Timestamp
  1130. }
  1131. func (h *constHistogram) Desc() *Desc {
  1132. return h.desc
  1133. }
  1134. func (h *constHistogram) Write(out *dto.Metric) error {
  1135. his := &dto.Histogram{
  1136. CreatedTimestamp: h.createdTs,
  1137. }
  1138. buckets := make([]*dto.Bucket, 0, len(h.buckets))
  1139. his.SampleCount = proto.Uint64(h.count)
  1140. his.SampleSum = proto.Float64(h.sum)
  1141. for upperBound, count := range h.buckets {
  1142. buckets = append(buckets, &dto.Bucket{
  1143. CumulativeCount: proto.Uint64(count),
  1144. UpperBound: proto.Float64(upperBound),
  1145. })
  1146. }
  1147. if len(buckets) > 0 {
  1148. sort.Sort(buckSort(buckets))
  1149. }
  1150. his.Bucket = buckets
  1151. out.Histogram = his
  1152. out.Label = h.labelPairs
  1153. return nil
  1154. }
  1155. // NewConstHistogram returns a metric representing a Prometheus histogram with
  1156. // fixed values for the count, sum, and bucket counts. As those parameters
  1157. // cannot be changed, the returned value does not implement the Histogram
  1158. // interface (but only the Metric interface). Users of this package will not
  1159. // have much use for it in regular operations. However, when implementing custom
  1160. // Collectors, it is useful as a throw-away metric that is generated on the fly
  1161. // to send it to Prometheus in the Collect method.
  1162. //
  1163. // buckets is a map of upper bounds to cumulative counts, excluding the +Inf
  1164. // bucket. The +Inf bucket is implicit, and its value is equal to the provided count.
  1165. //
  1166. // NewConstHistogram returns an error if the length of labelValues is not
  1167. // consistent with the variable labels in Desc or if Desc is invalid.
  1168. func NewConstHistogram(
  1169. desc *Desc,
  1170. count uint64,
  1171. sum float64,
  1172. buckets map[float64]uint64,
  1173. labelValues ...string,
  1174. ) (Metric, error) {
  1175. if desc.err != nil {
  1176. return nil, desc.err
  1177. }
  1178. if err := validateLabelValues(labelValues, len(desc.variableLabels.names)); err != nil {
  1179. return nil, err
  1180. }
  1181. return &constHistogram{
  1182. desc: desc,
  1183. count: count,
  1184. sum: sum,
  1185. buckets: buckets,
  1186. labelPairs: MakeLabelPairs(desc, labelValues),
  1187. }, nil
  1188. }
  1189. // MustNewConstHistogram is a version of NewConstHistogram that panics where
  1190. // NewConstHistogram would have returned an error.
  1191. func MustNewConstHistogram(
  1192. desc *Desc,
  1193. count uint64,
  1194. sum float64,
  1195. buckets map[float64]uint64,
  1196. labelValues ...string,
  1197. ) Metric {
  1198. m, err := NewConstHistogram(desc, count, sum, buckets, labelValues...)
  1199. if err != nil {
  1200. panic(err)
  1201. }
  1202. return m
  1203. }
  1204. type buckSort []*dto.Bucket
  1205. func (s buckSort) Len() int {
  1206. return len(s)
  1207. }
  1208. func (s buckSort) Swap(i, j int) {
  1209. s[i], s[j] = s[j], s[i]
  1210. }
  1211. func (s buckSort) Less(i, j int) bool {
  1212. return s[i].GetUpperBound() < s[j].GetUpperBound()
  1213. }
  1214. // pickSchema returns the largest number n between -4 and 8 such that
  1215. // 2^(2^-n) is less or equal the provided bucketFactor.
  1216. //
  1217. // Special cases:
  1218. // - bucketFactor <= 1: panics.
  1219. // - bucketFactor < 2^(2^-8) (but > 1): still returns 8.
  1220. func pickSchema(bucketFactor float64) int32 {
  1221. if bucketFactor <= 1 {
  1222. panic(fmt.Errorf("bucketFactor %f is <=1", bucketFactor))
  1223. }
  1224. floor := math.Floor(math.Log2(math.Log2(bucketFactor)))
  1225. switch {
  1226. case floor <= -8:
  1227. return 8
  1228. case floor >= 4:
  1229. return -4
  1230. default:
  1231. return -int32(floor)
  1232. }
  1233. }
  1234. func makeBuckets(buckets *sync.Map) ([]*dto.BucketSpan, []int64) {
  1235. var ii []int
  1236. buckets.Range(func(k, v interface{}) bool {
  1237. ii = append(ii, k.(int))
  1238. return true
  1239. })
  1240. sort.Ints(ii)
  1241. if len(ii) == 0 {
  1242. return nil, nil
  1243. }
  1244. var (
  1245. spans []*dto.BucketSpan
  1246. deltas []int64
  1247. prevCount int64
  1248. nextI int
  1249. )
  1250. appendDelta := func(count int64) {
  1251. *spans[len(spans)-1].Length++
  1252. deltas = append(deltas, count-prevCount)
  1253. prevCount = count
  1254. }
  1255. for n, i := range ii {
  1256. v, _ := buckets.Load(i)
  1257. count := atomic.LoadInt64(v.(*int64))
  1258. // Multiple spans with only small gaps in between are probably
  1259. // encoded more efficiently as one larger span with a few empty
  1260. // buckets. Needs some research to find the sweet spot. For now,
  1261. // we assume that gaps of one or two buckets should not create
  1262. // a new span.
  1263. iDelta := int32(i - nextI)
  1264. if n == 0 || iDelta > 2 {
  1265. // We have to create a new span, either because we are
  1266. // at the very beginning, or because we have found a gap
  1267. // of more than two buckets.
  1268. spans = append(spans, &dto.BucketSpan{
  1269. Offset: proto.Int32(iDelta),
  1270. Length: proto.Uint32(0),
  1271. })
  1272. } else {
  1273. // We have found a small gap (or no gap at all).
  1274. // Insert empty buckets as needed.
  1275. for j := int32(0); j < iDelta; j++ {
  1276. appendDelta(0)
  1277. }
  1278. }
  1279. appendDelta(count)
  1280. nextI = i + 1
  1281. }
  1282. return spans, deltas
  1283. }
  1284. // addToBucket increments the sparse bucket at key by the provided amount. It
  1285. // returns true if a new sparse bucket had to be created for that.
  1286. func addToBucket(buckets *sync.Map, key int, increment int64) bool {
  1287. if existingBucket, ok := buckets.Load(key); ok {
  1288. // Fast path without allocation.
  1289. atomic.AddInt64(existingBucket.(*int64), increment)
  1290. return false
  1291. }
  1292. // Bucket doesn't exist yet. Slow path allocating new counter.
  1293. newBucket := increment // TODO(beorn7): Check if this is sufficient to not let increment escape.
  1294. if actualBucket, loaded := buckets.LoadOrStore(key, &newBucket); loaded {
  1295. // The bucket was created concurrently in another goroutine.
  1296. // Have to increment after all.
  1297. atomic.AddInt64(actualBucket.(*int64), increment)
  1298. return false
  1299. }
  1300. return true
  1301. }
  1302. // addAndReset returns a function to be used with sync.Map.Range of spare
  1303. // buckets in coldCounts. It increments the buckets in the provided hotBuckets
  1304. // according to the buckets ranged through. It then resets all buckets ranged
  1305. // through to 0 (but leaves them in place so that they don't need to get
  1306. // recreated on the next scrape).
  1307. func addAndReset(hotBuckets *sync.Map, bucketNumber *uint32) func(k, v interface{}) bool {
  1308. return func(k, v interface{}) bool {
  1309. bucket := v.(*int64)
  1310. if addToBucket(hotBuckets, k.(int), atomic.LoadInt64(bucket)) {
  1311. atomic.AddUint32(bucketNumber, 1)
  1312. }
  1313. atomic.StoreInt64(bucket, 0)
  1314. return true
  1315. }
  1316. }
  1317. func deleteSyncMap(m *sync.Map) {
  1318. m.Range(func(k, v interface{}) bool {
  1319. m.Delete(k)
  1320. return true
  1321. })
  1322. }
  1323. func findSmallestKey(m *sync.Map) int {
  1324. result := math.MaxInt32
  1325. m.Range(func(k, v interface{}) bool {
  1326. key := k.(int)
  1327. if key < result {
  1328. result = key
  1329. }
  1330. return true
  1331. })
  1332. return result
  1333. }
  1334. func getLe(key int, schema int32) float64 {
  1335. // Here a bit of context about the behavior for the last bucket counting
  1336. // regular numbers (called simply "last bucket" below) and the bucket
  1337. // counting observations of ±Inf (called "inf bucket" below, with a key
  1338. // one higher than that of the "last bucket"):
  1339. //
  1340. // If we apply the usual formula to the last bucket, its upper bound
  1341. // would be calculated as +Inf. The reason is that the max possible
  1342. // regular float64 number (math.MaxFloat64) doesn't coincide with one of
  1343. // the calculated bucket boundaries. So the calculated boundary has to
  1344. // be larger than math.MaxFloat64, and the only float64 larger than
  1345. // math.MaxFloat64 is +Inf. However, we want to count actual
  1346. // observations of ±Inf in the inf bucket. Therefore, we have to treat
  1347. // the upper bound of the last bucket specially and set it to
  1348. // math.MaxFloat64. (The upper bound of the inf bucket, with its key
  1349. // being one higher than that of the last bucket, naturally comes out as
  1350. // +Inf by the usual formula. So that's fine.)
  1351. //
  1352. // math.MaxFloat64 has a frac of 0.9999999999999999 and an exp of
  1353. // 1024. If there were a float64 number following math.MaxFloat64, it
  1354. // would have a frac of 1.0 and an exp of 1024, or equivalently a frac
  1355. // of 0.5 and an exp of 1025. However, since frac must be smaller than
  1356. // 1, and exp must be smaller than 1025, either representation overflows
  1357. // a float64. (Which, in turn, is the reason that math.MaxFloat64 is the
  1358. // largest possible float64. Q.E.D.) However, the formula for
  1359. // calculating the upper bound from the idx and schema of the last
  1360. // bucket results in precisely that. It is either frac=1.0 & exp=1024
  1361. // (for schema < 0) or frac=0.5 & exp=1025 (for schema >=0). (This is,
  1362. // by the way, a power of two where the exponent itself is a power of
  1363. // two, 2¹⁰ in fact, which coinicides with a bucket boundary in all
  1364. // schemas.) So these are the special cases we have to catch below.
  1365. if schema < 0 {
  1366. exp := key << -schema
  1367. if exp == 1024 {
  1368. // This is the last bucket before the overflow bucket
  1369. // (for ±Inf observations). Return math.MaxFloat64 as
  1370. // explained above.
  1371. return math.MaxFloat64
  1372. }
  1373. return math.Ldexp(1, exp)
  1374. }
  1375. fracIdx := key & ((1 << schema) - 1)
  1376. frac := nativeHistogramBounds[schema][fracIdx]
  1377. exp := (key >> schema) + 1
  1378. if frac == 0.5 && exp == 1025 {
  1379. // This is the last bucket before the overflow bucket (for ±Inf
  1380. // observations). Return math.MaxFloat64 as explained above.
  1381. return math.MaxFloat64
  1382. }
  1383. return math.Ldexp(frac, exp)
  1384. }
  1385. // waitForCooldown returns after the count field in the provided histogramCounts
  1386. // has reached the provided count value.
  1387. func waitForCooldown(count uint64, counts *histogramCounts) {
  1388. for count != atomic.LoadUint64(&counts.count) {
  1389. runtime.Gosched() // Let observations get work done.
  1390. }
  1391. }
  1392. // atomicAddFloat adds the provided float atomically to another float
  1393. // represented by the bit pattern the bits pointer is pointing to.
  1394. func atomicAddFloat(bits *uint64, v float64) {
  1395. for {
  1396. loadedBits := atomic.LoadUint64(bits)
  1397. newBits := math.Float64bits(math.Float64frombits(loadedBits) + v)
  1398. if atomic.CompareAndSwapUint64(bits, loadedBits, newBits) {
  1399. break
  1400. }
  1401. }
  1402. }
  1403. // atomicDecUint32 atomically decrements the uint32 p points to. See
  1404. // https://pkg.go.dev/sync/atomic#AddUint32 to understand how this is done.
  1405. func atomicDecUint32(p *uint32) {
  1406. atomic.AddUint32(p, ^uint32(0))
  1407. }
  1408. // addAndResetCounts adds certain fields (count, sum, conventional buckets, zero
  1409. // bucket) from the cold counts to the corresponding fields in the hot
  1410. // counts. Those fields are then reset to 0 in the cold counts.
  1411. func addAndResetCounts(hot, cold *histogramCounts) {
  1412. atomic.AddUint64(&hot.count, atomic.LoadUint64(&cold.count))
  1413. atomic.StoreUint64(&cold.count, 0)
  1414. coldSum := math.Float64frombits(atomic.LoadUint64(&cold.sumBits))
  1415. atomicAddFloat(&hot.sumBits, coldSum)
  1416. atomic.StoreUint64(&cold.sumBits, 0)
  1417. for i := range hot.buckets {
  1418. atomic.AddUint64(&hot.buckets[i], atomic.LoadUint64(&cold.buckets[i]))
  1419. atomic.StoreUint64(&cold.buckets[i], 0)
  1420. }
  1421. atomic.AddUint64(&hot.nativeHistogramZeroBucket, atomic.LoadUint64(&cold.nativeHistogramZeroBucket))
  1422. atomic.StoreUint64(&cold.nativeHistogramZeroBucket, 0)
  1423. }