Skip to content

Commit a1d0466

Browse files
committed
metrics: hardcode lazy metric construction codepath
motivation is to remove the dependency on reflect.Call Throughout the codebase, GetOrRegister() is mostly used with lazy construction while Register() is strictly used with eagerly constructed metric objects. So it makes sense to isolate the lazy construction to GetOrRegister() and make caller decide on which function it makes sense for them.
1 parent 23f07d8 commit a1d0466

12 files changed

+31
-69
lines changed

metrics/counter.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,7 @@ import (
77
// GetOrRegisterCounter returns an existing Counter or constructs and registers
88
// a new Counter.
99
func GetOrRegisterCounter(name string, r Registry) *Counter {
10-
if r == nil {
11-
r = DefaultRegistry
12-
}
13-
return r.GetOrRegister(name, NewCounter).(*Counter)
10+
return GetOrRegister(name, NewCounter, r).(*Counter)
1411
}
1512

1613
// NewCounter constructs a new Counter.

metrics/counter_float64.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@ import (
88
// GetOrRegisterCounterFloat64 returns an existing *CounterFloat64 or constructs and registers
99
// a new CounterFloat64.
1010
func GetOrRegisterCounterFloat64(name string, r Registry) *CounterFloat64 {
11-
if nil == r {
12-
r = DefaultRegistry
13-
}
14-
return r.GetOrRegister(name, NewCounterFloat64).(*CounterFloat64)
11+
return GetOrRegister(name, NewCounterFloat64, r).(*CounterFloat64)
1512
}
1613

1714
// NewCounterFloat64 constructs a new CounterFloat64.

metrics/gauge.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,7 @@ func (g GaugeSnapshot) Value() int64 { return int64(g) }
1111
// GetOrRegisterGauge returns an existing Gauge or constructs and registers a
1212
// new Gauge.
1313
func GetOrRegisterGauge(name string, r Registry) *Gauge {
14-
if r == nil {
15-
r = DefaultRegistry
16-
}
17-
return r.GetOrRegister(name, NewGauge).(*Gauge)
14+
return GetOrRegister(name, NewGauge, r).(*Gauge)
1815
}
1916

2017
// NewGauge constructs a new Gauge.

metrics/gauge_float64.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@ import (
88
// GetOrRegisterGaugeFloat64 returns an existing GaugeFloat64 or constructs and registers a
99
// new GaugeFloat64.
1010
func GetOrRegisterGaugeFloat64(name string, r Registry) *GaugeFloat64 {
11-
if nil == r {
12-
r = DefaultRegistry
13-
}
14-
return r.GetOrRegister(name, NewGaugeFloat64()).(*GaugeFloat64)
11+
return GetOrRegister(name, NewGaugeFloat64, r).(*GaugeFloat64)
1512
}
1613

1714
// GaugeFloat64Snapshot is a read-only copy of a GaugeFloat64.

metrics/gauge_info.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,7 @@ func (val GaugeInfoValue) String() string {
1616
// GetOrRegisterGaugeInfo returns an existing GaugeInfo or constructs and registers a
1717
// new GaugeInfo.
1818
func GetOrRegisterGaugeInfo(name string, r Registry) *GaugeInfo {
19-
if nil == r {
20-
r = DefaultRegistry
21-
}
22-
return r.GetOrRegister(name, NewGaugeInfo()).(*GaugeInfo)
19+
return GetOrRegister(name, NewGaugeInfo, r).(*GaugeInfo)
2320
}
2421

2522
// NewGaugeInfo constructs a new GaugeInfo.

metrics/histogram.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,13 @@ type Histogram interface {
2323
// GetOrRegisterHistogram returns an existing Histogram or constructs and
2424
// registers a new StandardHistogram.
2525
func GetOrRegisterHistogram(name string, r Registry, s Sample) Histogram {
26-
if nil == r {
27-
r = DefaultRegistry
28-
}
29-
return r.GetOrRegister(name, func() Histogram { return NewHistogram(s) }).(Histogram)
26+
return GetOrRegister(name, func() Histogram { return NewHistogram(s) }, r).(Histogram)
3027
}
3128

3229
// GetOrRegisterHistogramLazy returns an existing Histogram or constructs and
3330
// registers a new StandardHistogram.
3431
func GetOrRegisterHistogramLazy(name string, r Registry, s func() Sample) Histogram {
35-
if nil == r {
36-
r = DefaultRegistry
37-
}
38-
return r.GetOrRegister(name, func() Histogram { return NewHistogram(s()) }).(Histogram)
32+
return GetOrRegister(name, func() Histogram { return NewHistogram(s()) }, r).(Histogram)
3933
}
4034

4135
// NewHistogram constructs a new StandardHistogram from a Sample.

metrics/meter.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,7 @@ import (
1212
// Be sure to unregister the meter from the registry once it is of no use to
1313
// allow for garbage collection.
1414
func GetOrRegisterMeter(name string, r Registry) *Meter {
15-
if r == nil {
16-
r = DefaultRegistry
17-
}
18-
return r.GetOrRegister(name, NewMeter).(*Meter)
15+
return GetOrRegister(name, NewMeter, r).(*Meter)
1916
}
2017

2118
// NewMeter constructs a new Meter and launches a goroutine.

metrics/registry.go

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package metrics
33
import (
44
"errors"
55
"fmt"
6-
"reflect"
76
"sort"
87
"strings"
98
"sync"
@@ -30,10 +29,9 @@ type Registry interface {
3029
// GetAll metrics in the Registry.
3130
GetAll() map[string]map[string]interface{}
3231

33-
// GetOrRegister gets an existing metric or registers the given one.
34-
// The interface can be the metric to register if not found in registry,
35-
// or a function returning the metric for lazy instantiation.
36-
GetOrRegister(string, interface{}) interface{}
32+
// GetOrRegister returns an existing metric or registers the one returned
33+
// by the given constructor.
34+
GetOrRegister(string, func() interface{}) interface{}
3735

3836
// Register the given metric under the given name.
3937
Register(string, interface{}) error
@@ -95,18 +93,15 @@ func (r *StandardRegistry) Get(name string) interface{} {
9593
// alternative to calling Get and Register on failure.
9694
// The interface can be the metric to register if not found in registry,
9795
// or a function returning the metric for lazy instantiation.
98-
func (r *StandardRegistry) GetOrRegister(name string, i interface{}) interface{} {
96+
func (r *StandardRegistry) GetOrRegister(name string, ctor func() interface{}) interface{} {
9997
// fast path
10098
cached, ok := r.metrics.Load(name)
10199
if ok {
102100
return cached
103101
}
104-
if v := reflect.ValueOf(i); v.Kind() == reflect.Func {
105-
i = v.Call(nil)[0].Interface()
106-
}
107-
item, _, ok := r.loadOrRegister(name, i)
102+
item, _, ok := r.loadOrRegister(name, ctor())
108103
if !ok {
109-
return i
104+
return item
110105
}
111106
return item
112107
}
@@ -120,9 +115,6 @@ func (r *StandardRegistry) Register(name string, i interface{}) error {
120115
return fmt.Errorf("%w: %v", ErrDuplicateMetric, name)
121116
}
122117

123-
if v := reflect.ValueOf(i); v.Kind() == reflect.Func {
124-
i = v.Call(nil)[0].Interface()
125-
}
126118
_, loaded, _ := r.loadOrRegister(name, i)
127119
if loaded {
128120
return fmt.Errorf("%w: %v", ErrDuplicateMetric, name)
@@ -295,9 +287,9 @@ func (r *PrefixedRegistry) Get(name string) interface{} {
295287
// GetOrRegister gets an existing metric or registers the given one.
296288
// The interface can be the metric to register if not found in registry,
297289
// or a function returning the metric for lazy instantiation.
298-
func (r *PrefixedRegistry) GetOrRegister(name string, metric interface{}) interface{} {
290+
func (r *PrefixedRegistry) GetOrRegister(name string, ctor func() interface{}) interface{} {
299291
realName := r.prefix + name
300-
return r.underlying.GetOrRegister(realName, metric)
292+
return r.underlying.GetOrRegister(realName, ctor)
301293
}
302294

303295
// Register the given metric under the given name. The name will be prefixed.
@@ -338,8 +330,11 @@ func Get(name string) interface{} {
338330

339331
// GetOrRegister gets an existing metric or creates and registers a new one. Threadsafe
340332
// alternative to calling Get and Register on failure.
341-
func GetOrRegister(name string, i interface{}) interface{} {
342-
return DefaultRegistry.GetOrRegister(name, i)
333+
func GetOrRegister[T any](name string, ctor func() T, r Registry) interface{} {
334+
if nil == r {
335+
r = DefaultRegistry
336+
}
337+
return r.GetOrRegister(name, func() any { return ctor() })
343338
}
344339

345340
// Register the given metric under the given name. Returns a ErrDuplicateMetric

metrics/registry_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func benchmarkRegistryGetOrRegisterParallel(b *testing.B, amount int) {
3030
wg.Add(1)
3131
go func() {
3232
for i := 0; i < b.N; i++ {
33-
r.GetOrRegister("foo", NewMeter)
33+
GetOrRegister("foo", NewMeter, r)
3434
}
3535
wg.Done()
3636
}()
@@ -98,8 +98,8 @@ func TestRegistryGetOrRegister(t *testing.T) {
9898
r := NewRegistry()
9999

100100
// First metric wins with GetOrRegister
101-
_ = r.GetOrRegister("foo", NewCounter())
102-
m := r.GetOrRegister("foo", NewGauge())
101+
_ = GetOrRegister("foo", NewCounter, r)
102+
m := GetOrRegister("foo", NewGauge, r)
103103
if _, ok := m.(*Counter); !ok {
104104
t.Fatal(m)
105105
}
@@ -123,8 +123,8 @@ func TestRegistryGetOrRegisterWithLazyInstantiation(t *testing.T) {
123123
r := NewRegistry()
124124

125125
// First metric wins with GetOrRegister
126-
_ = r.GetOrRegister("foo", NewCounter)
127-
m := r.GetOrRegister("foo", NewGauge)
126+
_ = GetOrRegister("foo", NewCounter, r)
127+
m := GetOrRegister("foo", NewGauge, r)
128128
if _, ok := m.(*Counter); !ok {
129129
t.Fatal(m)
130130
}
@@ -165,7 +165,7 @@ func TestPrefixedChildRegistryGetOrRegister(t *testing.T) {
165165
r := NewRegistry()
166166
pr := NewPrefixedChildRegistry(r, "prefix.")
167167

168-
_ = pr.GetOrRegister("foo", NewCounter())
168+
_ = GetOrRegister("foo", NewCounter, pr)
169169

170170
i := 0
171171
r.Each(func(name string, m interface{}) {
@@ -182,7 +182,7 @@ func TestPrefixedChildRegistryGetOrRegister(t *testing.T) {
182182
func TestPrefixedRegistryGetOrRegister(t *testing.T) {
183183
r := NewPrefixedRegistry("prefix.")
184184

185-
_ = r.GetOrRegister("foo", NewCounter())
185+
_ = GetOrRegister("foo", NewCounter, r)
186186

187187
i := 0
188188
r.Each(func(name string, m interface{}) {

metrics/resetting_timer.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@ import (
88
// GetOrRegisterResettingTimer returns an existing ResettingTimer or constructs and registers a
99
// new ResettingTimer.
1010
func GetOrRegisterResettingTimer(name string, r Registry) *ResettingTimer {
11-
if nil == r {
12-
r = DefaultRegistry
13-
}
14-
return r.GetOrRegister(name, NewResettingTimer).(*ResettingTimer)
11+
return GetOrRegister(name, NewResettingTimer, r).(*ResettingTimer)
1512
}
1613

1714
// NewRegisteredResettingTimer constructs and registers a new ResettingTimer.

metrics/runtimehistogram.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,8 @@ import (
88
)
99

1010
func getOrRegisterRuntimeHistogram(name string, scale float64, r Registry) *runtimeHistogram {
11-
if r == nil {
12-
r = DefaultRegistry
13-
}
1411
constructor := func() Histogram { return newRuntimeHistogram(scale) }
15-
return r.GetOrRegister(name, constructor).(*runtimeHistogram)
12+
return GetOrRegister(name, constructor, r).(*runtimeHistogram)
1613
}
1714

1815
// runtimeHistogram wraps a runtime/metrics histogram.

metrics/timer.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,7 @@ import (
1010
// Be sure to unregister the meter from the registry once it is of no use to
1111
// allow for garbage collection.
1212
func GetOrRegisterTimer(name string, r Registry) *Timer {
13-
if nil == r {
14-
r = DefaultRegistry
15-
}
16-
return r.GetOrRegister(name, NewTimer).(*Timer)
13+
return GetOrRegister(name, NewTimer, r).(*Timer)
1714
}
1815

1916
// NewCustomTimer constructs a new Timer from a Histogram and a Meter.

0 commit comments

Comments
 (0)