@@ -37,8 +37,8 @@ type MetricVec struct {
3737
3838// newMetricVec returns an initialized MetricVec. The concrete value is
3939// returned for embedding into another struct.
40- func newMetricVec (desc * Desc , newMetric func (lvs ... string ) Metric ) MetricVec {
41- return MetricVec {
40+ func newMetricVec (desc * Desc , newMetric func (lvs ... string ) Metric ) * MetricVec {
41+ return & MetricVec {
4242 children : map [uint64 ][]metricWithLabelValues {},
4343 desc : desc ,
4444 newMetric : newMetric ,
@@ -102,7 +102,7 @@ func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) {
102102 return nil , err
103103 }
104104
105- return m .getOrCreateMetric (h , lvs ), nil
105+ return m .getOrCreateMetricWithLabelValues (h , lvs ), nil
106106}
107107
108108// GetMetricWith returns the Metric for the given Labels map (the label names
@@ -123,7 +123,7 @@ func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) {
123123 return nil , err
124124 }
125125
126- return m .getOrCreateMetric (h , labels ), nil
126+ return m .getOrCreateMetricWithLabels (h , labels ), nil
127127}
128128
129129// WithLabelValues works as GetMetricWithLabelValues, but panics if an error
@@ -171,7 +171,7 @@ func (m *MetricVec) DeleteLabelValues(lvs ...string) bool {
171171 if err != nil {
172172 return false
173173 }
174- return m .deleteByHash (h , lvs )
174+ return m .deleteByHashWithLabelValues (h , lvs )
175175}
176176
177177// Delete deletes the metric where the variable labels are the same as those
@@ -193,21 +193,40 @@ func (m *MetricVec) Delete(labels Labels) bool {
193193 return false
194194 }
195195
196- return m .deleteByHash (h , labels )
196+ return m .deleteByHashWithLabels (h , labels )
197197}
198198
199- // deleteByHash removes the metric from the hash bucket h. If there are
200- // multiple matches in the bucket, use lvs to select a metric and remove only
201- // that metric.
202- //
203- // lvs MUST be of type Labels or []string or this method will panic.
204- func (m * MetricVec ) deleteByHash (h uint64 , lvs interface {}) bool {
199+ // deleteByHashWithLabelValues removes the metric from the hash bucket h. If
200+ // there are multiple matches in the bucket, use lvs to select a metric and
201+ // remove only that metric.
202+ func (m * MetricVec ) deleteByHashWithLabelValues (h uint64 , lvs []string ) bool {
205203 metrics , ok := m .children [h ]
206204 if ! ok {
207205 return false
208206 }
209207
210- i := m .findMetric (metrics , lvs )
208+ i := m .findMetricWithLabelValues (metrics , lvs )
209+ if i >= len (metrics ) {
210+ return false
211+ }
212+
213+ if len (metrics ) > 1 {
214+ m .children [h ] = append (metrics [:i ], metrics [i + 1 :]... )
215+ } else {
216+ delete (m .children , h )
217+ }
218+ return true
219+ }
220+
221+ // deleteByHashWithLabels removes the metric from the hash bucket h. If there
222+ // are multiple matches in the bucket, use lvs to select a metric and remove
223+ // only that metric.
224+ func (m * MetricVec ) deleteByHashWithLabels (h uint64 , labels Labels ) bool {
225+ metrics , ok := m .children [h ]
226+ if ! ok {
227+ return false
228+ }
229+ i := m .findMetricWithLabels (metrics , labels )
211230 if i >= len (metrics ) {
212231 return false
213232 }
@@ -258,119 +277,128 @@ func (m *MetricVec) hashLabels(labels Labels) (uint64, error) {
258277 return h , nil
259278}
260279
261- // getOrCreateMetric retrieves the metric by hash and label value or creates it
262- // and returns the new one.
280+ // getOrCreateMetricWithLabelValues retrieves the metric by hash and label value
281+ // or creates it and returns the new one.
263282//
264- // lvs MUST be of type Labels or []string or this method will panic.
283+ // This function holds the mutex.
284+ func (m * MetricVec ) getOrCreateMetricWithLabelValues (hash uint64 , lvs []string ) Metric {
285+ m .mtx .RLock ()
286+ metric , ok := m .getMetricWithLabelValues (hash , lvs )
287+ m .mtx .RUnlock ()
288+ if ok {
289+ return metric
290+ }
291+
292+ m .mtx .Lock ()
293+ defer m .mtx .Unlock ()
294+ metric , ok = m .getMetricWithLabelValues (hash , lvs )
295+ if ! ok {
296+ // Copy to avoid allocation in case wo don't go down this code path.
297+ copiedLVs := make ([]string , len (lvs ))
298+ copy (copiedLVs , lvs )
299+ metric = m .newMetric (copiedLVs ... )
300+ m .children [hash ] = append (m .children [hash ], metricWithLabelValues {values : copiedLVs , metric : metric })
301+ }
302+ return metric
303+ }
304+
305+ // getOrCreateMetricWithLabelValues retrieves the metric by hash and label value
306+ // or creates it and returns the new one.
265307//
266308// This function holds the mutex.
267- func (m * MetricVec ) getOrCreateMetric (hash uint64 , lvs interface {} ) Metric {
309+ func (m * MetricVec ) getOrCreateMetricWithLabels (hash uint64 , labels Labels ) Metric {
268310 m .mtx .RLock ()
269- metric , ok := m .getMetric (hash , lvs )
311+ metric , ok := m .getMetricWithLabels (hash , labels )
270312 m .mtx .RUnlock ()
271313 if ok {
272314 return metric
273315 }
274316
275317 m .mtx .Lock ()
276318 defer m .mtx .Unlock ()
277- metric , ok = m .getMetric (hash , lvs )
319+ metric , ok = m .getMetricWithLabels (hash , labels )
278320 if ! ok {
279- lvs := m .copyLabelValues ( lvs )
321+ lvs := m .extractLabelValues ( labels )
280322 metric = m .newMetric (lvs ... )
281323 m .children [hash ] = append (m .children [hash ], metricWithLabelValues {values : lvs , metric : metric })
282324 }
283325 return metric
284326}
285327
286- // getMetric while handling possible collisions in the hash space. Must be
287- // called while holding read mutex.
288- //
289- // lvs must be of type Labels or []string.
290- func (m * MetricVec ) getMetric (h uint64 , lvs interface {}) (Metric , bool ) {
328+ // getMetricWithLabelValues gets a metric while handling possible collisions in
329+ // the hash space. Must be called while holding read mutex.
330+ func (m * MetricVec ) getMetricWithLabelValues (h uint64 , lvs []string ) (Metric , bool ) {
291331 metrics , ok := m .children [h ]
292332 if ok {
293- return m .selectMetric (metrics , lvs )
333+ if i := m .findMetricWithLabelValues (metrics , lvs ); i < len (metrics ) {
334+ return metrics [i ].metric , true
335+ }
294336 }
295-
296337 return nil , false
297338}
298339
299- func (m * MetricVec ) selectMetric (metrics []metricWithLabelValues , lvs interface {}) (Metric , bool ) {
300- i := m .findMetric (metrics , lvs )
301-
302- if i < len (metrics ) {
303- return metrics [i ].metric , true
340+ // getMetricWithLabels gets a metric while handling possible collisions in
341+ // the hash space. Must be called while holding read mutex.
342+ func (m * MetricVec ) getMetricWithLabels (h uint64 , labels Labels ) (Metric , bool ) {
343+ metrics , ok := m .children [h ]
344+ if ok {
345+ if i := m .findMetricWithLabels (metrics , labels ); i < len (metrics ) {
346+ return metrics [i ].metric , true
347+ }
304348 }
305-
306349 return nil , false
307350}
308351
309- // findMetric returns the index of the matching metric or len(metrics) if not
310- // found.
311- func (m * MetricVec ) findMetric (metrics []metricWithLabelValues , lvs interface {} ) int {
352+ // findMetricWithLabelValues returns the index of the matching metric or
353+ // len(metrics) if not found.
354+ func (m * MetricVec ) findMetricWithLabelValues (metrics []metricWithLabelValues , lvs [] string ) int {
312355 for i , metric := range metrics {
313- if m .matchLabels (metric .values , lvs ) {
356+ if m .matchLabelValues (metric .values , lvs ) {
314357 return i
315358 }
316359 }
317-
318360 return len (metrics )
319361}
320362
321- func (m * MetricVec ) matchLabels (values []string , lvs interface {}) bool {
322- switch lvs := lvs .(type ) {
323- case []string :
324- if len (values ) != len (lvs ) {
325- return false
326- }
327-
328- for i , v := range values {
329- if v != lvs [i ] {
330- return false
331- }
363+ // findMetricWithLabels returns the index of the matching metric or len(metrics)
364+ // if not found.
365+ func (m * MetricVec ) findMetricWithLabels (metrics []metricWithLabelValues , labels Labels ) int {
366+ for i , metric := range metrics {
367+ if m .matchLabels (metric .values , labels ) {
368+ return i
332369 }
370+ }
371+ return len (metrics )
372+ }
333373
334- return true
335- case Labels :
336- if len (lvs ) != len (values ) {
374+ func (m * MetricVec ) matchLabelValues (values []string , lvs []string ) bool {
375+ if len (values ) != len (lvs ) {
376+ return false
377+ }
378+ for i , v := range values {
379+ if v != lvs [i ] {
337380 return false
338381 }
339-
340- for i , k := range m .desc .variableLabels {
341- if values [i ] != lvs [k ] {
342- return false
343- }
344- }
345-
346- return true
347- default :
348- // If we reach this condition, there is an unexpected type being used
349- // as a labels value. Either add branch here for the new type or fix
350- // the bug causing the type to be passed in.
351- panic ("unsupported type" )
352382 }
383+ return true
353384}
354385
355- // copyLabelValues copies the labels values into common string slice format to
356- // use when allocating the metric and to keep track of hash collision
357- // ambiguity.
358- //
359- // lvs must be of type Labels or []string or this method will panic.
360- func (m * MetricVec ) copyLabelValues (lvs interface {}) []string {
361- var labelValues []string
362- switch lvs := lvs .(type ) {
363- case []string :
364- labelValues = make ([]string , len (lvs ))
365- copy (labelValues , lvs )
366- case Labels :
367- labelValues = make ([]string , len (lvs ))
368- for i , k := range m .desc .variableLabels {
369- labelValues [i ] = lvs [k ]
386+ func (m * MetricVec ) matchLabels (values []string , labels Labels ) bool {
387+ if len (labels ) != len (values ) {
388+ return false
389+ }
390+ for i , k := range m .desc .variableLabels {
391+ if values [i ] != labels [k ] {
392+ return false
370393 }
371- default :
372- panic (fmt .Sprintf ("unsupported type for lvs: %#v" , lvs ))
373394 }
395+ return true
396+ }
374397
398+ func (m * MetricVec ) extractLabelValues (labels Labels ) []string {
399+ labelValues := make ([]string , len (labels ))
400+ for i , k := range m .desc .variableLabels {
401+ labelValues [i ] = labels [k ]
402+ }
375403 return labelValues
376404}
0 commit comments