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.

vec.go 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  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 but as a building block
  21. // for implementations of vectors of a given metric type, like GaugeVec,
  22. // CounterVec, SummaryVec, and HistogramVec. It is exported so that it can be
  23. // used for custom Metric implementations.
  24. //
  25. // To create a FooVec for custom Metric Foo, embed a pointer to MetricVec in
  26. // FooVec and initialize it with NewMetricVec. Implement wrappers for
  27. // GetMetricWithLabelValues and GetMetricWith that return (Foo, error) rather
  28. // than (Metric, error). Similarly, create a wrapper for CurryWith that returns
  29. // (*FooVec, error) rather than (*MetricVec, error). It is recommended to also
  30. // add the convenience methods WithLabelValues, With, and MustCurryWith, which
  31. // panic instead of returning errors. See also the MetricVec example.
  32. type MetricVec struct {
  33. *metricMap
  34. curry []curriedLabelValue
  35. // hashAdd and hashAddByte can be replaced for testing collision handling.
  36. hashAdd func(h uint64, s string) uint64
  37. hashAddByte func(h uint64, b byte) uint64
  38. }
  39. // NewMetricVec returns an initialized metricVec.
  40. func NewMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *MetricVec {
  41. return &MetricVec{
  42. metricMap: &metricMap{
  43. metrics: map[uint64][]metricWithLabelValues{},
  44. desc: desc,
  45. newMetric: newMetric,
  46. },
  47. hashAdd: hashAdd,
  48. hashAddByte: hashAddByte,
  49. }
  50. }
  51. // DeleteLabelValues removes the metric where the variable labels are the same
  52. // as those passed in as labels (same order as the VariableLabels in Desc). It
  53. // returns true if a metric was deleted.
  54. //
  55. // It is not an error if the number of label values is not the same as the
  56. // number of VariableLabels in Desc. However, such inconsistent label count can
  57. // never match an actual metric, so the method will always return false in that
  58. // case.
  59. //
  60. // Note that for more than one label value, this method is prone to mistakes
  61. // caused by an incorrect order of arguments. Consider Delete(Labels) as an
  62. // alternative to avoid that type of mistake. For higher label numbers, the
  63. // latter has a much more readable (albeit more verbose) syntax, but it comes
  64. // with a performance overhead (for creating and processing the Labels map).
  65. // See also the CounterVec example.
  66. func (m *MetricVec) DeleteLabelValues(lvs ...string) bool {
  67. lvs = constrainLabelValues(m.desc, lvs, m.curry)
  68. h, err := m.hashLabelValues(lvs)
  69. if err != nil {
  70. return false
  71. }
  72. return m.metricMap.deleteByHashWithLabelValues(h, lvs, m.curry)
  73. }
  74. // Delete deletes the metric where the variable labels are the same as those
  75. // passed in as labels. It returns true if a metric was deleted.
  76. //
  77. // It is not an error if the number and names of the Labels are inconsistent
  78. // with those of the VariableLabels in Desc. However, such inconsistent Labels
  79. // can never match an actual metric, so the method will always return false in
  80. // that case.
  81. //
  82. // This method is used for the same purpose as DeleteLabelValues(...string). See
  83. // there for pros and cons of the two methods.
  84. func (m *MetricVec) Delete(labels Labels) bool {
  85. labels, closer := constrainLabels(m.desc, labels)
  86. defer closer()
  87. h, err := m.hashLabels(labels)
  88. if err != nil {
  89. return false
  90. }
  91. return m.metricMap.deleteByHashWithLabels(h, labels, m.curry)
  92. }
  93. // DeletePartialMatch deletes all metrics where the variable labels contain all of those
  94. // passed in as labels. The order of the labels does not matter.
  95. // It returns the number of metrics deleted.
  96. //
  97. // Note that curried labels will never be matched if deleting from the curried vector.
  98. // To match curried labels with DeletePartialMatch, it must be called on the base vector.
  99. func (m *MetricVec) DeletePartialMatch(labels Labels) int {
  100. labels, closer := constrainLabels(m.desc, labels)
  101. defer closer()
  102. return m.metricMap.deleteByLabels(labels, m.curry)
  103. }
  104. // Without explicit forwarding of Describe, Collect, Reset, those methods won't
  105. // show up in GoDoc.
  106. // Describe implements Collector.
  107. func (m *MetricVec) Describe(ch chan<- *Desc) { m.metricMap.Describe(ch) }
  108. // Collect implements Collector.
  109. func (m *MetricVec) Collect(ch chan<- Metric) { m.metricMap.Collect(ch) }
  110. // Reset deletes all metrics in this vector.
  111. func (m *MetricVec) Reset() { m.metricMap.Reset() }
  112. // CurryWith returns a vector curried with the provided labels, i.e. the
  113. // returned vector has those labels pre-set for all labeled operations performed
  114. // on it. The cardinality of the curried vector is reduced accordingly. The
  115. // order of the remaining labels stays the same (just with the curried labels
  116. // taken out of the sequence – which is relevant for the
  117. // (GetMetric)WithLabelValues methods). It is possible to curry a curried
  118. // vector, but only with labels not yet used for currying before.
  119. //
  120. // The metrics contained in the MetricVec are shared between the curried and
  121. // uncurried vectors. They are just accessed differently. Curried and uncurried
  122. // vectors behave identically in terms of collection. Only one must be
  123. // registered with a given registry (usually the uncurried version). The Reset
  124. // method deletes all metrics, even if called on a curried vector.
  125. //
  126. // Note that CurryWith is usually not called directly but through a wrapper
  127. // around MetricVec, implementing a vector for a specific Metric
  128. // implementation, for example GaugeVec.
  129. func (m *MetricVec) CurryWith(labels Labels) (*MetricVec, error) {
  130. var (
  131. newCurry []curriedLabelValue
  132. oldCurry = m.curry
  133. iCurry int
  134. )
  135. for i, labelName := range m.desc.variableLabels.names {
  136. val, ok := labels[labelName]
  137. if iCurry < len(oldCurry) && oldCurry[iCurry].index == i {
  138. if ok {
  139. return nil, fmt.Errorf("label name %q is already curried", labelName)
  140. }
  141. newCurry = append(newCurry, oldCurry[iCurry])
  142. iCurry++
  143. } else {
  144. if !ok {
  145. continue // Label stays uncurried.
  146. }
  147. newCurry = append(newCurry, curriedLabelValue{
  148. i,
  149. m.desc.variableLabels.constrain(labelName, val),
  150. })
  151. }
  152. }
  153. if l := len(oldCurry) + len(labels) - len(newCurry); l > 0 {
  154. return nil, fmt.Errorf("%d unknown label(s) found during currying", l)
  155. }
  156. return &MetricVec{
  157. metricMap: m.metricMap,
  158. curry: newCurry,
  159. hashAdd: m.hashAdd,
  160. hashAddByte: m.hashAddByte,
  161. }, nil
  162. }
  163. // GetMetricWithLabelValues returns the Metric for the given slice of label
  164. // values (same order as the variable labels in Desc). If that combination of
  165. // label values is accessed for the first time, a new Metric is created (by
  166. // calling the newMetric function provided during construction of the
  167. // MetricVec).
  168. //
  169. // It is possible to call this method without using the returned Metric to only
  170. // create the new Metric but leave it in its initial state.
  171. //
  172. // Keeping the Metric for later use is possible (and should be considered if
  173. // performance is critical), but keep in mind that Reset, DeleteLabelValues and
  174. // Delete can be used to delete the Metric from the MetricVec. In that case, the
  175. // Metric will still exist, but it will not be exported anymore, even if a
  176. // Metric with the same label values is created later.
  177. //
  178. // An error is returned if the number of label values is not the same as the
  179. // number of variable labels in Desc (minus any curried labels).
  180. //
  181. // Note that for more than one label value, this method is prone to mistakes
  182. // caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
  183. // an alternative to avoid that type of mistake. For higher label numbers, the
  184. // latter has a much more readable (albeit more verbose) syntax, but it comes
  185. // with a performance overhead (for creating and processing the Labels map).
  186. //
  187. // Note that GetMetricWithLabelValues is usually not called directly but through
  188. // a wrapper around MetricVec, implementing a vector for a specific Metric
  189. // implementation, for example GaugeVec.
  190. func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) {
  191. lvs = constrainLabelValues(m.desc, lvs, m.curry)
  192. h, err := m.hashLabelValues(lvs)
  193. if err != nil {
  194. return nil, err
  195. }
  196. return m.metricMap.getOrCreateMetricWithLabelValues(h, lvs, m.curry), nil
  197. }
  198. // GetMetricWith returns the Metric for the given Labels map (the label names
  199. // must match those of the variable labels in Desc). If that label map is
  200. // accessed for the first time, a new Metric is created. Implications of
  201. // creating a Metric without using it and keeping the Metric for later use
  202. // are the same as for GetMetricWithLabelValues.
  203. //
  204. // An error is returned if the number and names of the Labels are inconsistent
  205. // with those of the variable labels in Desc (minus any curried labels).
  206. //
  207. // This method is used for the same purpose as
  208. // GetMetricWithLabelValues(...string). See there for pros and cons of the two
  209. // methods.
  210. //
  211. // Note that GetMetricWith is usually not called directly but through a wrapper
  212. // around MetricVec, implementing a vector for a specific Metric implementation,
  213. // for example GaugeVec.
  214. func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) {
  215. labels, closer := constrainLabels(m.desc, labels)
  216. defer closer()
  217. h, err := m.hashLabels(labels)
  218. if err != nil {
  219. return nil, err
  220. }
  221. return m.metricMap.getOrCreateMetricWithLabels(h, labels, m.curry), nil
  222. }
  223. func (m *MetricVec) hashLabelValues(vals []string) (uint64, error) {
  224. if err := validateLabelValues(vals, len(m.desc.variableLabels.names)-len(m.curry)); err != nil {
  225. return 0, err
  226. }
  227. var (
  228. h = hashNew()
  229. curry = m.curry
  230. iVals, iCurry int
  231. )
  232. for i := 0; i < len(m.desc.variableLabels.names); i++ {
  233. if iCurry < len(curry) && curry[iCurry].index == i {
  234. h = m.hashAdd(h, curry[iCurry].value)
  235. iCurry++
  236. } else {
  237. h = m.hashAdd(h, vals[iVals])
  238. iVals++
  239. }
  240. h = m.hashAddByte(h, model.SeparatorByte)
  241. }
  242. return h, nil
  243. }
  244. func (m *MetricVec) hashLabels(labels Labels) (uint64, error) {
  245. if err := validateValuesInLabels(labels, len(m.desc.variableLabels.names)-len(m.curry)); err != nil {
  246. return 0, err
  247. }
  248. var (
  249. h = hashNew()
  250. curry = m.curry
  251. iCurry int
  252. )
  253. for i, labelName := range m.desc.variableLabels.names {
  254. val, ok := labels[labelName]
  255. if iCurry < len(curry) && curry[iCurry].index == i {
  256. if ok {
  257. return 0, fmt.Errorf("label name %q is already curried", labelName)
  258. }
  259. h = m.hashAdd(h, curry[iCurry].value)
  260. iCurry++
  261. } else {
  262. if !ok {
  263. return 0, fmt.Errorf("label name %q missing in label map", labelName)
  264. }
  265. h = m.hashAdd(h, val)
  266. }
  267. h = m.hashAddByte(h, model.SeparatorByte)
  268. }
  269. return h, nil
  270. }
  271. // metricWithLabelValues provides the metric and its label values for
  272. // disambiguation on hash collision.
  273. type metricWithLabelValues struct {
  274. values []string
  275. metric Metric
  276. }
  277. // curriedLabelValue sets the curried value for a label at the given index.
  278. type curriedLabelValue struct {
  279. index int
  280. value string
  281. }
  282. // metricMap is a helper for metricVec and shared between differently curried
  283. // metricVecs.
  284. type metricMap struct {
  285. mtx sync.RWMutex // Protects metrics.
  286. metrics map[uint64][]metricWithLabelValues
  287. desc *Desc
  288. newMetric func(labelValues ...string) Metric
  289. }
  290. // Describe implements Collector. It will send exactly one Desc to the provided
  291. // channel.
  292. func (m *metricMap) Describe(ch chan<- *Desc) {
  293. ch <- m.desc
  294. }
  295. // Collect implements Collector.
  296. func (m *metricMap) Collect(ch chan<- Metric) {
  297. m.mtx.RLock()
  298. defer m.mtx.RUnlock()
  299. for _, metrics := range m.metrics {
  300. for _, metric := range metrics {
  301. ch <- metric.metric
  302. }
  303. }
  304. }
  305. // Reset deletes all metrics in this vector.
  306. func (m *metricMap) Reset() {
  307. m.mtx.Lock()
  308. defer m.mtx.Unlock()
  309. for h := range m.metrics {
  310. delete(m.metrics, h)
  311. }
  312. }
  313. // deleteByHashWithLabelValues removes the metric from the hash bucket h. If
  314. // there are multiple matches in the bucket, use lvs to select a metric and
  315. // remove only that metric.
  316. func (m *metricMap) deleteByHashWithLabelValues(
  317. h uint64, lvs []string, curry []curriedLabelValue,
  318. ) bool {
  319. m.mtx.Lock()
  320. defer m.mtx.Unlock()
  321. metrics, ok := m.metrics[h]
  322. if !ok {
  323. return false
  324. }
  325. i := findMetricWithLabelValues(metrics, lvs, curry)
  326. if i >= len(metrics) {
  327. return false
  328. }
  329. if len(metrics) > 1 {
  330. old := metrics
  331. m.metrics[h] = append(metrics[:i], metrics[i+1:]...)
  332. old[len(old)-1] = metricWithLabelValues{}
  333. } else {
  334. delete(m.metrics, h)
  335. }
  336. return true
  337. }
  338. // deleteByHashWithLabels removes the metric from the hash bucket h. If there
  339. // are multiple matches in the bucket, use lvs to select a metric and remove
  340. // only that metric.
  341. func (m *metricMap) deleteByHashWithLabels(
  342. h uint64, labels Labels, curry []curriedLabelValue,
  343. ) bool {
  344. m.mtx.Lock()
  345. defer m.mtx.Unlock()
  346. metrics, ok := m.metrics[h]
  347. if !ok {
  348. return false
  349. }
  350. i := findMetricWithLabels(m.desc, metrics, labels, curry)
  351. if i >= len(metrics) {
  352. return false
  353. }
  354. if len(metrics) > 1 {
  355. old := metrics
  356. m.metrics[h] = append(metrics[:i], metrics[i+1:]...)
  357. old[len(old)-1] = metricWithLabelValues{}
  358. } else {
  359. delete(m.metrics, h)
  360. }
  361. return true
  362. }
  363. // deleteByLabels deletes a metric if the given labels are present in the metric.
  364. func (m *metricMap) deleteByLabels(labels Labels, curry []curriedLabelValue) int {
  365. m.mtx.Lock()
  366. defer m.mtx.Unlock()
  367. var numDeleted int
  368. for h, metrics := range m.metrics {
  369. i := findMetricWithPartialLabels(m.desc, metrics, labels, curry)
  370. if i >= len(metrics) {
  371. // Didn't find matching labels in this metric slice.
  372. continue
  373. }
  374. delete(m.metrics, h)
  375. numDeleted++
  376. }
  377. return numDeleted
  378. }
  379. // findMetricWithPartialLabel returns the index of the matching metric or
  380. // len(metrics) if not found.
  381. func findMetricWithPartialLabels(
  382. desc *Desc, metrics []metricWithLabelValues, labels Labels, curry []curriedLabelValue,
  383. ) int {
  384. for i, metric := range metrics {
  385. if matchPartialLabels(desc, metric.values, labels, curry) {
  386. return i
  387. }
  388. }
  389. return len(metrics)
  390. }
  391. // indexOf searches the given slice of strings for the target string and returns
  392. // the index or len(items) as well as a boolean whether the search succeeded.
  393. func indexOf(target string, items []string) (int, bool) {
  394. for i, l := range items {
  395. if l == target {
  396. return i, true
  397. }
  398. }
  399. return len(items), false
  400. }
  401. // valueMatchesVariableOrCurriedValue determines if a value was previously curried,
  402. // and returns whether it matches either the "base" value or the curried value accordingly.
  403. // It also indicates whether the match is against a curried or uncurried value.
  404. func valueMatchesVariableOrCurriedValue(targetValue string, index int, values []string, curry []curriedLabelValue) (bool, bool) {
  405. for _, curriedValue := range curry {
  406. if curriedValue.index == index {
  407. // This label was curried. See if the curried value matches our target.
  408. return curriedValue.value == targetValue, true
  409. }
  410. }
  411. // This label was not curried. See if the current value matches our target label.
  412. return values[index] == targetValue, false
  413. }
  414. // matchPartialLabels searches the current metric and returns whether all of the target label:value pairs are present.
  415. func matchPartialLabels(desc *Desc, values []string, labels Labels, curry []curriedLabelValue) bool {
  416. for l, v := range labels {
  417. // Check if the target label exists in our metrics and get the index.
  418. varLabelIndex, validLabel := indexOf(l, desc.variableLabels.names)
  419. if validLabel {
  420. // Check the value of that label against the target value.
  421. // We don't consider curried values in partial matches.
  422. matches, curried := valueMatchesVariableOrCurriedValue(v, varLabelIndex, values, curry)
  423. if matches && !curried {
  424. continue
  425. }
  426. }
  427. return false
  428. }
  429. return true
  430. }
  431. // getOrCreateMetricWithLabelValues retrieves the metric by hash and label value
  432. // or creates it and returns the new one.
  433. //
  434. // This function holds the mutex.
  435. func (m *metricMap) getOrCreateMetricWithLabelValues(
  436. hash uint64, lvs []string, curry []curriedLabelValue,
  437. ) Metric {
  438. m.mtx.RLock()
  439. metric, ok := m.getMetricWithHashAndLabelValues(hash, lvs, curry)
  440. m.mtx.RUnlock()
  441. if ok {
  442. return metric
  443. }
  444. m.mtx.Lock()
  445. defer m.mtx.Unlock()
  446. metric, ok = m.getMetricWithHashAndLabelValues(hash, lvs, curry)
  447. if !ok {
  448. inlinedLVs := inlineLabelValues(lvs, curry)
  449. metric = m.newMetric(inlinedLVs...)
  450. m.metrics[hash] = append(m.metrics[hash], metricWithLabelValues{values: inlinedLVs, metric: metric})
  451. }
  452. return metric
  453. }
  454. // getOrCreateMetricWithLabelValues retrieves the metric by hash and label value
  455. // or creates it and returns the new one.
  456. //
  457. // This function holds the mutex.
  458. func (m *metricMap) getOrCreateMetricWithLabels(
  459. hash uint64, labels Labels, curry []curriedLabelValue,
  460. ) Metric {
  461. m.mtx.RLock()
  462. metric, ok := m.getMetricWithHashAndLabels(hash, labels, curry)
  463. m.mtx.RUnlock()
  464. if ok {
  465. return metric
  466. }
  467. m.mtx.Lock()
  468. defer m.mtx.Unlock()
  469. metric, ok = m.getMetricWithHashAndLabels(hash, labels, curry)
  470. if !ok {
  471. lvs := extractLabelValues(m.desc, labels, curry)
  472. metric = m.newMetric(lvs...)
  473. m.metrics[hash] = append(m.metrics[hash], metricWithLabelValues{values: lvs, metric: metric})
  474. }
  475. return metric
  476. }
  477. // getMetricWithHashAndLabelValues gets a metric while handling possible
  478. // collisions in the hash space. Must be called while holding the read mutex.
  479. func (m *metricMap) getMetricWithHashAndLabelValues(
  480. h uint64, lvs []string, curry []curriedLabelValue,
  481. ) (Metric, bool) {
  482. metrics, ok := m.metrics[h]
  483. if ok {
  484. if i := findMetricWithLabelValues(metrics, lvs, curry); i < len(metrics) {
  485. return metrics[i].metric, true
  486. }
  487. }
  488. return nil, false
  489. }
  490. // getMetricWithHashAndLabels gets a metric while handling possible collisions in
  491. // the hash space. Must be called while holding read mutex.
  492. func (m *metricMap) getMetricWithHashAndLabels(
  493. h uint64, labels Labels, curry []curriedLabelValue,
  494. ) (Metric, bool) {
  495. metrics, ok := m.metrics[h]
  496. if ok {
  497. if i := findMetricWithLabels(m.desc, metrics, labels, curry); i < len(metrics) {
  498. return metrics[i].metric, true
  499. }
  500. }
  501. return nil, false
  502. }
  503. // findMetricWithLabelValues returns the index of the matching metric or
  504. // len(metrics) if not found.
  505. func findMetricWithLabelValues(
  506. metrics []metricWithLabelValues, lvs []string, curry []curriedLabelValue,
  507. ) int {
  508. for i, metric := range metrics {
  509. if matchLabelValues(metric.values, lvs, curry) {
  510. return i
  511. }
  512. }
  513. return len(metrics)
  514. }
  515. // findMetricWithLabels returns the index of the matching metric or len(metrics)
  516. // if not found.
  517. func findMetricWithLabels(
  518. desc *Desc, metrics []metricWithLabelValues, labels Labels, curry []curriedLabelValue,
  519. ) int {
  520. for i, metric := range metrics {
  521. if matchLabels(desc, metric.values, labels, curry) {
  522. return i
  523. }
  524. }
  525. return len(metrics)
  526. }
  527. func matchLabelValues(values, lvs []string, curry []curriedLabelValue) bool {
  528. if len(values) != len(lvs)+len(curry) {
  529. return false
  530. }
  531. var iLVs, iCurry int
  532. for i, v := range values {
  533. if iCurry < len(curry) && curry[iCurry].index == i {
  534. if v != curry[iCurry].value {
  535. return false
  536. }
  537. iCurry++
  538. continue
  539. }
  540. if v != lvs[iLVs] {
  541. return false
  542. }
  543. iLVs++
  544. }
  545. return true
  546. }
  547. func matchLabels(desc *Desc, values []string, labels Labels, curry []curriedLabelValue) bool {
  548. if len(values) != len(labels)+len(curry) {
  549. return false
  550. }
  551. iCurry := 0
  552. for i, k := range desc.variableLabels.names {
  553. if iCurry < len(curry) && curry[iCurry].index == i {
  554. if values[i] != curry[iCurry].value {
  555. return false
  556. }
  557. iCurry++
  558. continue
  559. }
  560. if values[i] != labels[k] {
  561. return false
  562. }
  563. }
  564. return true
  565. }
  566. func extractLabelValues(desc *Desc, labels Labels, curry []curriedLabelValue) []string {
  567. labelValues := make([]string, len(labels)+len(curry))
  568. iCurry := 0
  569. for i, k := range desc.variableLabels.names {
  570. if iCurry < len(curry) && curry[iCurry].index == i {
  571. labelValues[i] = curry[iCurry].value
  572. iCurry++
  573. continue
  574. }
  575. labelValues[i] = labels[k]
  576. }
  577. return labelValues
  578. }
  579. func inlineLabelValues(lvs []string, curry []curriedLabelValue) []string {
  580. labelValues := make([]string, len(lvs)+len(curry))
  581. var iCurry, iLVs int
  582. for i := range labelValues {
  583. if iCurry < len(curry) && curry[iCurry].index == i {
  584. labelValues[i] = curry[iCurry].value
  585. iCurry++
  586. continue
  587. }
  588. labelValues[i] = lvs[iLVs]
  589. iLVs++
  590. }
  591. return labelValues
  592. }
  593. var labelsPool = &sync.Pool{
  594. New: func() interface{} {
  595. return make(Labels)
  596. },
  597. }
  598. func constrainLabels(desc *Desc, labels Labels) (Labels, func()) {
  599. if len(desc.variableLabels.labelConstraints) == 0 {
  600. // Fast path when there's no constraints
  601. return labels, func() {}
  602. }
  603. constrainedLabels := labelsPool.Get().(Labels)
  604. for l, v := range labels {
  605. constrainedLabels[l] = desc.variableLabels.constrain(l, v)
  606. }
  607. return constrainedLabels, func() {
  608. for k := range constrainedLabels {
  609. delete(constrainedLabels, k)
  610. }
  611. labelsPool.Put(constrainedLabels)
  612. }
  613. }
  614. func constrainLabelValues(desc *Desc, lvs []string, curry []curriedLabelValue) []string {
  615. if len(desc.variableLabels.labelConstraints) == 0 {
  616. // Fast path when there's no constraints
  617. return lvs
  618. }
  619. constrainedValues := make([]string, len(lvs))
  620. var iCurry, iLVs int
  621. for i := 0; i < len(lvs)+len(curry); i++ {
  622. if iCurry < len(curry) && curry[iCurry].index == i {
  623. iCurry++
  624. continue
  625. }
  626. if i < len(desc.variableLabels.names) {
  627. constrainedValues[iLVs] = desc.variableLabels.constrain(
  628. desc.variableLabels.names[i],
  629. lvs[iLVs],
  630. )
  631. } else {
  632. constrainedValues[iLVs] = lvs[iLVs]
  633. }
  634. iLVs++
  635. }
  636. return constrainedValues
  637. }