@@ -37,8 +37,8 @@ type MetricVec struct {
37
37
38
38
// newMetricVec returns an initialized MetricVec. The concrete value is
39
39
// 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 {
42
42
children : map [uint64 ][]metricWithLabelValues {},
43
43
desc : desc ,
44
44
newMetric : newMetric ,
@@ -102,7 +102,7 @@ func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) {
102
102
return nil , err
103
103
}
104
104
105
- return m .getOrCreateMetric (h , lvs ), nil
105
+ return m .getOrCreateMetricWithLabelValues (h , lvs ), nil
106
106
}
107
107
108
108
// GetMetricWith returns the Metric for the given Labels map (the label names
@@ -123,7 +123,7 @@ func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) {
123
123
return nil , err
124
124
}
125
125
126
- return m .getOrCreateMetric (h , labels ), nil
126
+ return m .getOrCreateMetricWithLabels (h , labels ), nil
127
127
}
128
128
129
129
// WithLabelValues works as GetMetricWithLabelValues, but panics if an error
@@ -171,7 +171,7 @@ func (m *MetricVec) DeleteLabelValues(lvs ...string) bool {
171
171
if err != nil {
172
172
return false
173
173
}
174
- return m .deleteByHash (h , lvs )
174
+ return m .deleteByHashWithLabelValues (h , lvs )
175
175
}
176
176
177
177
// Delete deletes the metric where the variable labels are the same as those
@@ -193,21 +193,40 @@ func (m *MetricVec) Delete(labels Labels) bool {
193
193
return false
194
194
}
195
195
196
- return m .deleteByHash (h , labels )
196
+ return m .deleteByHashWithLabels (h , labels )
197
197
}
198
198
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 {
205
203
metrics , ok := m .children [h ]
206
204
if ! ok {
207
205
return false
208
206
}
209
207
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 )
211
230
if i >= len (metrics ) {
212
231
return false
213
232
}
@@ -258,119 +277,128 @@ func (m *MetricVec) hashLabels(labels Labels) (uint64, error) {
258
277
return h , nil
259
278
}
260
279
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.
263
282
//
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.
265
307
//
266
308
// 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 {
268
310
m .mtx .RLock ()
269
- metric , ok := m .getMetric (hash , lvs )
311
+ metric , ok := m .getMetricWithLabels (hash , labels )
270
312
m .mtx .RUnlock ()
271
313
if ok {
272
314
return metric
273
315
}
274
316
275
317
m .mtx .Lock ()
276
318
defer m .mtx .Unlock ()
277
- metric , ok = m .getMetric (hash , lvs )
319
+ metric , ok = m .getMetricWithLabels (hash , labels )
278
320
if ! ok {
279
- lvs := m .copyLabelValues ( lvs )
321
+ lvs := m .extractLabelValues ( labels )
280
322
metric = m .newMetric (lvs ... )
281
323
m .children [hash ] = append (m .children [hash ], metricWithLabelValues {values : lvs , metric : metric })
282
324
}
283
325
return metric
284
326
}
285
327
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 ) {
291
331
metrics , ok := m .children [h ]
292
332
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
+ }
294
336
}
295
-
296
337
return nil , false
297
338
}
298
339
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
+ }
304
348
}
305
-
306
349
return nil , false
307
350
}
308
351
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 {
312
355
for i , metric := range metrics {
313
- if m .matchLabels (metric .values , lvs ) {
356
+ if m .matchLabelValues (metric .values , lvs ) {
314
357
return i
315
358
}
316
359
}
317
-
318
360
return len (metrics )
319
361
}
320
362
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
332
369
}
370
+ }
371
+ return len (metrics )
372
+ }
333
373
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 ] {
337
380
return false
338
381
}
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" )
352
382
}
383
+ return true
353
384
}
354
385
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
370
393
}
371
- default :
372
- panic (fmt .Sprintf ("unsupported type for lvs: %#v" , lvs ))
373
394
}
395
+ return true
396
+ }
374
397
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
+ }
375
403
return labelValues
376
404
}
0 commit comments