go-ethereum/metrics/registry_test.go
Martin HS 9045b79bc2
metrics, cmd/geth: change init-process of metrics (#30814)
This PR modifies how the metrics library handles `Enabled`: previously,
the package `init` decided whether to serve real metrics or just
dummy-types.

This has several drawbacks: 
- During pkg init, we need to determine whether metrics are enabled or
not. So we first hacked in a check if certain geth-specific
commandline-flags were enabled. Then we added a similar check for
geth-env-vars. Then we almost added a very elaborate check for
toml-config-file, plus toml parsing.

- Using "real" types and dummy types interchangeably means that
everything is hidden behind interfaces. This has a performance penalty,
and also it just adds a lot of code.

This PR removes the interface stuff, uses concrete types, and allows for
the setting of Enabled to happen later. It is still assumed that
`metrics.Enable()` is invoked early on.

The somewhat 'heavy' operations, such as ticking meters and exp-decay,
now checks the enable-flag to prevent resource leak.

The change may be large, but it's mostly pretty trivial, and from the
last time I gutted the metrics, I ensured that we have fairly good test
coverage.

---------

Co-authored-by: Felix Lange <fjl@twurst.com>
2024-12-10 13:27:29 +01:00

336 lines
6.1 KiB
Go

package metrics
import (
"sync"
"testing"
)
func BenchmarkRegistry(b *testing.B) {
r := NewRegistry()
r.Register("foo", NewCounter())
b.ResetTimer()
for i := 0; i < b.N; i++ {
r.Each(func(string, interface{}) {})
}
}
func BenchmarkRegistryGetOrRegisterParallel_8(b *testing.B) {
benchmarkRegistryGetOrRegisterParallel(b, 8)
}
func BenchmarkRegistryGetOrRegisterParallel_32(b *testing.B) {
benchmarkRegistryGetOrRegisterParallel(b, 32)
}
func benchmarkRegistryGetOrRegisterParallel(b *testing.B, amount int) {
r := NewRegistry()
b.ResetTimer()
var wg sync.WaitGroup
for i := 0; i < amount; i++ {
wg.Add(1)
go func() {
for i := 0; i < b.N; i++ {
r.GetOrRegister("foo", NewMeter)
}
wg.Done()
}()
}
wg.Wait()
}
func TestRegistry(t *testing.T) {
r := NewRegistry()
r.Register("foo", NewCounter())
i := 0
r.Each(func(name string, iface interface{}) {
i++
if name != "foo" {
t.Fatal(name)
}
if _, ok := iface.(*Counter); !ok {
t.Fatal(iface)
}
})
if i != 1 {
t.Fatal(i)
}
r.Unregister("foo")
i = 0
r.Each(func(string, interface{}) { i++ })
if i != 0 {
t.Fatal(i)
}
}
func TestRegistryDuplicate(t *testing.T) {
r := NewRegistry()
if err := r.Register("foo", NewCounter()); nil != err {
t.Fatal(err)
}
if err := r.Register("foo", NewGauge()); nil == err {
t.Fatal(err)
}
i := 0
r.Each(func(name string, iface interface{}) {
i++
if _, ok := iface.(*Counter); !ok {
t.Fatal(iface)
}
})
if i != 1 {
t.Fatal(i)
}
}
func TestRegistryGet(t *testing.T) {
r := NewRegistry()
r.Register("foo", NewCounter())
if count := r.Get("foo").(*Counter).Snapshot().Count(); count != 0 {
t.Fatal(count)
}
r.Get("foo").(*Counter).Inc(1)
if count := r.Get("foo").(*Counter).Snapshot().Count(); count != 1 {
t.Fatal(count)
}
}
func TestRegistryGetOrRegister(t *testing.T) {
r := NewRegistry()
// First metric wins with GetOrRegister
_ = r.GetOrRegister("foo", NewCounter())
m := r.GetOrRegister("foo", NewGauge())
if _, ok := m.(*Counter); !ok {
t.Fatal(m)
}
i := 0
r.Each(func(name string, iface interface{}) {
i++
if name != "foo" {
t.Fatal(name)
}
if _, ok := iface.(*Counter); !ok {
t.Fatal(iface)
}
})
if i != 1 {
t.Fatal(i)
}
}
func TestRegistryGetOrRegisterWithLazyInstantiation(t *testing.T) {
r := NewRegistry()
// First metric wins with GetOrRegister
_ = r.GetOrRegister("foo", NewCounter)
m := r.GetOrRegister("foo", NewGauge)
if _, ok := m.(*Counter); !ok {
t.Fatal(m)
}
i := 0
r.Each(func(name string, iface interface{}) {
i++
if name != "foo" {
t.Fatal(name)
}
if _, ok := iface.(*Counter); !ok {
t.Fatal(iface)
}
})
if i != 1 {
t.Fatal(i)
}
}
func TestRegistryUnregister(t *testing.T) {
l := len(arbiter.meters)
r := NewRegistry()
r.Register("foo", NewCounter())
r.Register("bar", NewMeter())
r.Register("baz", NewTimer())
if len(arbiter.meters) != l+2 {
t.Errorf("arbiter.meters: %d != %d\n", l+2, len(arbiter.meters))
}
r.Unregister("foo")
r.Unregister("bar")
r.Unregister("baz")
if len(arbiter.meters) != l {
t.Errorf("arbiter.meters: %d != %d\n", l+2, len(arbiter.meters))
}
}
func TestPrefixedChildRegistryGetOrRegister(t *testing.T) {
r := NewRegistry()
pr := NewPrefixedChildRegistry(r, "prefix.")
_ = pr.GetOrRegister("foo", NewCounter())
i := 0
r.Each(func(name string, m interface{}) {
i++
if name != "prefix.foo" {
t.Fatal(name)
}
})
if i != 1 {
t.Fatal(i)
}
}
func TestPrefixedRegistryGetOrRegister(t *testing.T) {
r := NewPrefixedRegistry("prefix.")
_ = r.GetOrRegister("foo", NewCounter())
i := 0
r.Each(func(name string, m interface{}) {
i++
if name != "prefix.foo" {
t.Fatal(name)
}
})
if i != 1 {
t.Fatal(i)
}
}
func TestPrefixedRegistryRegister(t *testing.T) {
r := NewPrefixedRegistry("prefix.")
err := r.Register("foo", NewCounter())
c := NewCounter()
Register("bar", c)
if err != nil {
t.Fatal(err.Error())
}
i := 0
r.Each(func(name string, m interface{}) {
i++
if name != "prefix.foo" {
t.Fatal(name)
}
})
if i != 1 {
t.Fatal(i)
}
}
func TestPrefixedRegistryUnregister(t *testing.T) {
r := NewPrefixedRegistry("prefix.")
_ = r.Register("foo", NewCounter())
i := 0
r.Each(func(name string, m interface{}) {
i++
if name != "prefix.foo" {
t.Fatal(name)
}
})
if i != 1 {
t.Fatal(i)
}
r.Unregister("foo")
i = 0
r.Each(func(name string, m interface{}) {
i++
})
if i != 0 {
t.Fatal(i)
}
}
func TestPrefixedRegistryGet(t *testing.T) {
pr := NewPrefixedRegistry("prefix.")
name := "foo"
pr.Register(name, NewCounter())
fooCounter := pr.Get(name)
if fooCounter == nil {
t.Fatal(name)
}
}
func TestPrefixedChildRegistryGet(t *testing.T) {
r := NewRegistry()
pr := NewPrefixedChildRegistry(r, "prefix.")
name := "foo"
pr.Register(name, NewCounter())
fooCounter := pr.Get(name)
if fooCounter == nil {
t.Fatal(name)
}
}
func TestChildPrefixedRegistryRegister(t *testing.T) {
r := NewPrefixedChildRegistry(DefaultRegistry, "prefix.")
err := r.Register("foo", NewCounter())
c := NewCounter()
Register("bar", c)
if err != nil {
t.Fatal(err.Error())
}
i := 0
r.Each(func(name string, m interface{}) {
i++
if name != "prefix.foo" {
t.Fatal(name)
}
})
if i != 1 {
t.Fatal(i)
}
}
func TestChildPrefixedRegistryOfChildRegister(t *testing.T) {
r := NewPrefixedChildRegistry(NewRegistry(), "prefix.")
r2 := NewPrefixedChildRegistry(r, "prefix2.")
err := r.Register("foo2", NewCounter())
if err != nil {
t.Fatal(err.Error())
}
err = r2.Register("baz", NewCounter())
if err != nil {
t.Fatal(err.Error())
}
c := NewCounter()
Register("bars", c)
i := 0
r2.Each(func(name string, m interface{}) {
i++
if name != "prefix.prefix2.baz" {
t.Fatal(name)
}
})
if i != 1 {
t.Fatal(i)
}
}
func TestWalkRegistries(t *testing.T) {
r := NewPrefixedChildRegistry(NewRegistry(), "prefix.")
r2 := NewPrefixedChildRegistry(r, "prefix2.")
err := r.Register("foo2", NewCounter())
if err != nil {
t.Fatal(err.Error())
}
err = r2.Register("baz", NewCounter())
if err != nil {
t.Fatal(err.Error())
}
c := NewCounter()
Register("bars", c)
_, prefix := findPrefix(r2, "")
if prefix != "prefix.prefix2." {
t.Fatal(prefix)
}
}