@@ -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