vec.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. // Copyright 2014 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. "sync"
  17. "github.com/prometheus/common/model"
  18. )
  19. // metricVec is a Collector to bundle metrics of the same name that differ in
  20. // their label values. metricVec is not used directly (and therefore
  21. // unexported). It is used as a building block for implementations of vectors of
  22. // a given metric type, like GaugeVec, CounterVec, SummaryVec, and HistogramVec.
  23. // It also handles label currying.
  24. type metricVec struct {
  25. *metricMap
  26. curry []curriedLabelValue
  27. // hashAdd and hashAddByte can be replaced for testing collision handling.
  28. hashAdd func(h uint64, s string) uint64
  29. hashAddByte func(h uint64, b byte) uint64
  30. }
  31. // newMetricVec returns an initialized metricVec.
  32. func newMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *metricVec {
  33. return &metricVec{
  34. metricMap: &metricMap{
  35. metrics: map[uint64][]metricWithLabelValues{},
  36. desc: desc,
  37. newMetric: newMetric,
  38. },
  39. hashAdd: hashAdd,
  40. hashAddByte: hashAddByte,
  41. }
  42. }
  43. // DeleteLabelValues removes the metric where the variable labels are the same
  44. // as those passed in as labels (same order as the VariableLabels in Desc). It
  45. // returns true if a metric was deleted.
  46. //
  47. // It is not an error if the number of label values is not the same as the
  48. // number of VariableLabels in Desc. However, such inconsistent label count can
  49. // never match an actual metric, so the method will always return false in that
  50. // case.
  51. //
  52. // Note that for more than one label value, this method is prone to mistakes
  53. // caused by an incorrect order of arguments. Consider Delete(Labels) as an
  54. // alternative to avoid that type of mistake. For higher label numbers, the
  55. // latter has a much more readable (albeit more verbose) syntax, but it comes
  56. // with a performance overhead (for creating and processing the Labels map).
  57. // See also the CounterVec example.
  58. func (m *metricVec) DeleteLabelValues(lvs ...string) bool {
  59. h, err := m.hashLabelValues(lvs)
  60. if err != nil {
  61. return false
  62. }
  63. return m.metricMap.deleteByHashWithLabelValues(h, lvs, m.curry)
  64. }
  65. // Delete deletes the metric where the variable labels are the same as those
  66. // passed in as labels. It returns true if a metric was deleted.
  67. //
  68. // It is not an error if the number and names of the Labels are inconsistent
  69. // with those of the VariableLabels in Desc. However, such inconsistent Labels
  70. // can never match an actual metric, so the method will always return false in
  71. // that case.
  72. //
  73. // This method is used for the same purpose as DeleteLabelValues(...string). See
  74. // there for pros and cons of the two methods.
  75. func (m *metricVec) Delete(labels Labels) bool {
  76. h, err := m.hashLabels(labels)
  77. if err != nil {
  78. return false
  79. }
  80. return m.metricMap.deleteByHashWithLabels(h, labels, m.curry)
  81. }
  82. // Without explicit forwarding of Describe, Collect, Reset, those methods won't
  83. // show up in GoDoc.
  84. // Describe implements Collector.
  85. func (m *metricVec) Describe(ch chan<- *Desc) { m.metricMap.Describe(ch) }
  86. // Collect implements Collector.
  87. func (m *metricVec) Collect(ch chan<- Metric) { m.metricMap.Collect(ch) }
  88. // Reset deletes all metrics in this vector.
  89. func (m *metricVec) Reset() { m.metricMap.Reset() }
  90. func (m *metricVec) curryWith(labels Labels) (*metricVec, error) {
  91. var (
  92. newCurry []curriedLabelValue
  93. oldCurry = m.curry
  94. iCurry int
  95. )
  96. for i, label := range m.desc.variableLabels {
  97. val, ok := labels[label]
  98. if iCurry < len(oldCurry) && oldCurry[iCurry].index == i {
  99. if ok {
  100. return nil, fmt.Errorf("label name %q is already curried", label)
  101. }
  102. newCurry = append(newCurry, oldCurry[iCurry])
  103. iCurry++
  104. } else {
  105. if !ok {
  106. continue // Label stays uncurried.
  107. }
  108. newCurry = append(newCurry, curriedLabelValue{i, val})
  109. }
  110. }
  111. if l := len(oldCurry) + len(labels) - len(newCurry); l > 0 {
  112. return nil, fmt.Errorf("%d unknown label(s) found during currying", l)
  113. }
  114. return &metricVec{
  115. metricMap: m.metricMap,
  116. curry: newCurry,
  117. hashAdd: m.hashAdd,
  118. hashAddByte: m.hashAddByte,
  119. }, nil
  120. }
  121. func (m *metricVec) getMetricWithLabelValues(lvs ...string) (Metric, error) {
  122. h, err := m.hashLabelValues(lvs)
  123. if err != nil {
  124. return nil, err
  125. }
  126. return m.metricMap.getOrCreateMetricWithLabelValues(h, lvs, m.curry), nil
  127. }
  128. func (m *metricVec) getMetricWith(labels Labels) (Metric, error) {
  129. h, err := m.hashLabels(labels)
  130. if err != nil {
  131. return nil, err
  132. }
  133. return m.metricMap.getOrCreateMetricWithLabels(h, labels, m.curry), nil
  134. }
  135. func (m *metricVec) hashLabelValues(vals []string) (uint64, error) {
  136. if err := validateLabelValues(vals, len(m.desc.variableLabels)-len(m.curry)); err != nil {
  137. return 0, err
  138. }
  139. var (
  140. h = hashNew()
  141. curry = m.curry
  142. iVals, iCurry int
  143. )
  144. for i := 0; i < len(m.desc.variableLabels); i++ {
  145. if iCurry < len(curry) && curry[iCurry].index == i {
  146. h = m.hashAdd(h, curry[iCurry].value)
  147. iCurry++
  148. } else {
  149. h = m.hashAdd(h, vals[iVals])
  150. iVals++
  151. }
  152. h = m.hashAddByte(h, model.SeparatorByte)
  153. }
  154. return h, nil
  155. }
  156. func (m *metricVec) hashLabels(labels Labels) (uint64, error) {
  157. if err := validateValuesInLabels(labels, len(m.desc.variableLabels)-len(m.curry)); err != nil {
  158. return 0, err
  159. }
  160. var (
  161. h = hashNew()
  162. curry = m.curry
  163. iCurry int
  164. )
  165. for i, label := range m.desc.variableLabels {
  166. val, ok := labels[label]
  167. if iCurry < len(curry) && curry[iCurry].index == i {
  168. if ok {
  169. return 0, fmt.Errorf("label name %q is already curried", label)
  170. }
  171. h = m.hashAdd(h, curry[iCurry].value)
  172. iCurry++
  173. } else {
  174. if !ok {
  175. return 0, fmt.Errorf("label name %q missing in label map", label)
  176. }
  177. h = m.hashAdd(h, val)
  178. }
  179. h = m.hashAddByte(h, model.SeparatorByte)
  180. }
  181. return h, nil
  182. }
  183. // metricWithLabelValues provides the metric and its label values for
  184. // disambiguation on hash collision.
  185. type metricWithLabelValues struct {
  186. values []string
  187. metric Metric
  188. }
  189. // curriedLabelValue sets the curried value for a label at the given index.
  190. type curriedLabelValue struct {
  191. index int
  192. value string
  193. }
  194. // metricMap is a helper for metricVec and shared between differently curried
  195. // metricVecs.
  196. type metricMap struct {
  197. mtx sync.RWMutex // Protects metrics.
  198. metrics map[uint64][]metricWithLabelValues
  199. desc *Desc
  200. newMetric func(labelValues ...string) Metric
  201. }
  202. // Describe implements Collector. It will send exactly one Desc to the provided
  203. // channel.
  204. func (m *metricMap) Describe(ch chan<- *Desc) {
  205. ch <- m.desc
  206. }
  207. // Collect implements Collector.
  208. func (m *metricMap) Collect(ch chan<- Metric) {
  209. m.mtx.RLock()
  210. defer m.mtx.RUnlock()
  211. for _, metrics := range m.metrics {
  212. for _, metric := range metrics {
  213. ch <- metric.metric
  214. }
  215. }
  216. }
  217. // Reset deletes all metrics in this vector.
  218. func (m *metricMap) Reset() {
  219. m.mtx.Lock()
  220. defer m.mtx.Unlock()
  221. for h := range m.metrics {
  222. delete(m.metrics, h)
  223. }
  224. }
  225. // deleteByHashWithLabelValues removes the metric from the hash bucket h. If
  226. // there are multiple matches in the bucket, use lvs to select a metric and
  227. // remove only that metric.
  228. func (m *metricMap) deleteByHashWithLabelValues(
  229. h uint64, lvs []string, curry []curriedLabelValue,
  230. ) bool {
  231. m.mtx.Lock()
  232. defer m.mtx.Unlock()
  233. metrics, ok := m.metrics[h]
  234. if !ok {
  235. return false
  236. }
  237. i := findMetricWithLabelValues(metrics, lvs, curry)
  238. if i >= len(metrics) {
  239. return false
  240. }
  241. if len(metrics) > 1 {
  242. m.metrics[h] = append(metrics[:i], metrics[i+1:]...)
  243. } else {
  244. delete(m.metrics, h)
  245. }
  246. return true
  247. }
  248. // deleteByHashWithLabels removes the metric from the hash bucket h. If there
  249. // are multiple matches in the bucket, use lvs to select a metric and remove
  250. // only that metric.
  251. func (m *metricMap) deleteByHashWithLabels(
  252. h uint64, labels Labels, curry []curriedLabelValue,
  253. ) bool {
  254. m.mtx.Lock()
  255. defer m.mtx.Unlock()
  256. metrics, ok := m.metrics[h]
  257. if !ok {
  258. return false
  259. }
  260. i := findMetricWithLabels(m.desc, metrics, labels, curry)
  261. if i >= len(metrics) {
  262. return false
  263. }
  264. if len(metrics) > 1 {
  265. m.metrics[h] = append(metrics[:i], metrics[i+1:]...)
  266. } else {
  267. delete(m.metrics, h)
  268. }
  269. return true
  270. }
  271. // getOrCreateMetricWithLabelValues retrieves the metric by hash and label value
  272. // or creates it and returns the new one.
  273. //
  274. // This function holds the mutex.
  275. func (m *metricMap) getOrCreateMetricWithLabelValues(
  276. hash uint64, lvs []string, curry []curriedLabelValue,
  277. ) Metric {
  278. m.mtx.RLock()
  279. metric, ok := m.getMetricWithHashAndLabelValues(hash, lvs, curry)
  280. m.mtx.RUnlock()
  281. if ok {
  282. return metric
  283. }
  284. m.mtx.Lock()
  285. defer m.mtx.Unlock()
  286. metric, ok = m.getMetricWithHashAndLabelValues(hash, lvs, curry)
  287. if !ok {
  288. inlinedLVs := inlineLabelValues(lvs, curry)
  289. metric = m.newMetric(inlinedLVs...)
  290. m.metrics[hash] = append(m.metrics[hash], metricWithLabelValues{values: inlinedLVs, metric: metric})
  291. }
  292. return metric
  293. }
  294. // getOrCreateMetricWithLabelValues retrieves the metric by hash and label value
  295. // or creates it and returns the new one.
  296. //
  297. // This function holds the mutex.
  298. func (m *metricMap) getOrCreateMetricWithLabels(
  299. hash uint64, labels Labels, curry []curriedLabelValue,
  300. ) Metric {
  301. m.mtx.RLock()
  302. metric, ok := m.getMetricWithHashAndLabels(hash, labels, curry)
  303. m.mtx.RUnlock()
  304. if ok {
  305. return metric
  306. }
  307. m.mtx.Lock()
  308. defer m.mtx.Unlock()
  309. metric, ok = m.getMetricWithHashAndLabels(hash, labels, curry)
  310. if !ok {
  311. lvs := extractLabelValues(m.desc, labels, curry)
  312. metric = m.newMetric(lvs...)
  313. m.metrics[hash] = append(m.metrics[hash], metricWithLabelValues{values: lvs, metric: metric})
  314. }
  315. return metric
  316. }
  317. // getMetricWithHashAndLabelValues gets a metric while handling possible
  318. // collisions in the hash space. Must be called while holding the read mutex.
  319. func (m *metricMap) getMetricWithHashAndLabelValues(
  320. h uint64, lvs []string, curry []curriedLabelValue,
  321. ) (Metric, bool) {
  322. metrics, ok := m.metrics[h]
  323. if ok {
  324. if i := findMetricWithLabelValues(metrics, lvs, curry); i < len(metrics) {
  325. return metrics[i].metric, true
  326. }
  327. }
  328. return nil, false
  329. }
  330. // getMetricWithHashAndLabels gets a metric while handling possible collisions in
  331. // the hash space. Must be called while holding read mutex.
  332. func (m *metricMap) getMetricWithHashAndLabels(
  333. h uint64, labels Labels, curry []curriedLabelValue,
  334. ) (Metric, bool) {
  335. metrics, ok := m.metrics[h]
  336. if ok {
  337. if i := findMetricWithLabels(m.desc, metrics, labels, curry); i < len(metrics) {
  338. return metrics[i].metric, true
  339. }
  340. }
  341. return nil, false
  342. }
  343. // findMetricWithLabelValues returns the index of the matching metric or
  344. // len(metrics) if not found.
  345. func findMetricWithLabelValues(
  346. metrics []metricWithLabelValues, lvs []string, curry []curriedLabelValue,
  347. ) int {
  348. for i, metric := range metrics {
  349. if matchLabelValues(metric.values, lvs, curry) {
  350. return i
  351. }
  352. }
  353. return len(metrics)
  354. }
  355. // findMetricWithLabels returns the index of the matching metric or len(metrics)
  356. // if not found.
  357. func findMetricWithLabels(
  358. desc *Desc, metrics []metricWithLabelValues, labels Labels, curry []curriedLabelValue,
  359. ) int {
  360. for i, metric := range metrics {
  361. if matchLabels(desc, metric.values, labels, curry) {
  362. return i
  363. }
  364. }
  365. return len(metrics)
  366. }
  367. func matchLabelValues(values []string, lvs []string, curry []curriedLabelValue) bool {
  368. if len(values) != len(lvs)+len(curry) {
  369. return false
  370. }
  371. var iLVs, iCurry int
  372. for i, v := range values {
  373. if iCurry < len(curry) && curry[iCurry].index == i {
  374. if v != curry[iCurry].value {
  375. return false
  376. }
  377. iCurry++
  378. continue
  379. }
  380. if v != lvs[iLVs] {
  381. return false
  382. }
  383. iLVs++
  384. }
  385. return true
  386. }
  387. func matchLabels(desc *Desc, values []string, labels Labels, curry []curriedLabelValue) bool {
  388. if len(values) != len(labels)+len(curry) {
  389. return false
  390. }
  391. iCurry := 0
  392. for i, k := range desc.variableLabels {
  393. if iCurry < len(curry) && curry[iCurry].index == i {
  394. if values[i] != curry[iCurry].value {
  395. return false
  396. }
  397. iCurry++
  398. continue
  399. }
  400. if values[i] != labels[k] {
  401. return false
  402. }
  403. }
  404. return true
  405. }
  406. func extractLabelValues(desc *Desc, labels Labels, curry []curriedLabelValue) []string {
  407. labelValues := make([]string, len(labels)+len(curry))
  408. iCurry := 0
  409. for i, k := range desc.variableLabels {
  410. if iCurry < len(curry) && curry[iCurry].index == i {
  411. labelValues[i] = curry[iCurry].value
  412. iCurry++
  413. continue
  414. }
  415. labelValues[i] = labels[k]
  416. }
  417. return labelValues
  418. }
  419. func inlineLabelValues(lvs []string, curry []curriedLabelValue) []string {
  420. labelValues := make([]string, len(lvs)+len(curry))
  421. var iCurry, iLVs int
  422. for i := range labelValues {
  423. if iCurry < len(curry) && curry[iCurry].index == i {
  424. labelValues[i] = curry[iCurry].value
  425. iCurry++
  426. continue
  427. }
  428. labelValues[i] = lvs[iLVs]
  429. iLVs++
  430. }
  431. return labelValues
  432. }