Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memory leak in global MeterProvider #5753

Closed
dashpole opened this issue Aug 28, 2024 · 3 comments · Fixed by #5754
Closed

Memory leak in global MeterProvider #5753

dashpole opened this issue Aug 28, 2024 · 3 comments · Fixed by #5754
Assignees
Labels
bug Something isn't working
Milestone

Comments

@dashpole
Copy link
Contributor

Description

Forked from open-telemetry/opentelemetry-go-contrib#5190

I am able to reproduce the issue with these benchmarks:

package bench

import (
	"testing"

	"go.opentelemetry.io/otel"
)

const (
	meterName   = "foo"
	metricName  = "bar"
)

func BenchmarkSimple(b *testing.B) {
	b.ReportAllocs()
	meter := otel.Meter(meterName)
	for i := 0; i < b.N; i++ {
		meter.Int64Counter(metricName)
	}
}

It has 1 allocation. Since we are repeatedly creating the same instrument, it should have 0 allocations. If I replace otel.Meter with a noop meterprovider or the real meter provider, it has 0 allocations. I believe the root cause is that we always append the instrument to the list of instruments for the global meter provider: e.g.

m.instruments = append(m.instruments, i)

We should instead maintain a map of instruments, and ensure that we only keep unique instruments, similar to what we did for the SDK.

Expected behavior

We should not leak memory when a user creates the same instrument repeatedly

@dashpole dashpole added the bug Something isn't working label Aug 28, 2024
@MrAlias MrAlias added this to the v1.30.0 milestone Aug 29, 2024
XSAM added a commit that referenced this issue Aug 29, 2024
Fixes #5753

The added test fails on main, but passes after the fix.

---------

Co-authored-by: Sam Xie <[email protected]>
@Jesse-Bonfire
Copy link
Contributor

Jesse-Bonfire commented Sep 16, 2024

It appears that this change has broken metrics that are registered before otel.SetMeterProvider is called. We are getting into a spot where the delegate on the siUpDownCounter isn't getting set as it looks like with the new map we are creating new ones and throwing away the old. But the old counter has been registered with meter.RegisterCallback
https://github.com/open-telemetry/opentelemetry-go/blob/42fd8fe325c125a40a21796c3f14b599e5e5f586/internal/global/meter.go#L175C8-L175C9

I am pretty new to Otel and Go but this change seems to break the connection pools convention outlined here.
https://github.com/open-telemetry/semantic-conventions/blob/422eeb71f40c73d1b195723cc1cffeb09abf2391/docs/database/database-metrics.md#connection-pools

Currently I am seeing this when instrumenting a Redis Cluster as it creates instrumentation for the connection pool to each node in the cluster. https://github.com/redis/go-redis/blob/233f97accde15bcb2e3d86ee744109b4dce34171/extra/redisotel/metrics.go#L85

This problem is removed if we apply the instrumentation after we call otel.SetMeterProvider because the delegate just creates the counter. Not sure the best way to proceed.

@Jesse-Bonfire
Copy link
Contributor

I am able to reproduce the exception with the following test case

package bench

import (
	"context"
	"testing"

	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/metric"
	sdkmetric "go.opentelemetry.io/otel/sdk/metric"
)

const (
	meterName  = "foo"
	metricName = "bar"
)

func BenchmarkSimple(b *testing.B) {
	b.ReportAllocs()
	meter := otel.Meter(meterName)
	for i := 0; i < 5; i++ {
		m, _ := meter.Int64ObservableUpDownCounter(metricName)
		meter.RegisterCallback(func(ctx context.Context, o metric.Observer) error {
			o.ObserveInt64(m, int64(10))
			return nil
		}, m)
	}
	p1 := sdkmetric.NewMeterProvider()
	otel.SetMeterProvider(p1)
}

@dashpole
Copy link
Contributor Author

@Jesse-Bonfire I opened a new issue to track it: #5827

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants