use new unique package to reduce memory duplication of interned labels

when multiple remote writes are configured

Signed-off-by: Callum Styan <callumstyan@gmail.com>
This commit is contained in:
Callum Styan 2024-08-27 10:51:27 -07:00
parent 75e6497ec1
commit d3d5e2af0e
3 changed files with 24 additions and 23 deletions

2
go.mod
View file

@ -1,6 +1,6 @@
module github.com/prometheus/prometheus module github.com/prometheus/prometheus
go 1.21.0 go 1.23.0
toolchain go1.22.5 toolchain go1.22.5

View file

@ -20,6 +20,7 @@ package remote
import ( import (
"sync" "sync"
"unique"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promauto"
@ -35,22 +36,20 @@ var noReferenceReleases = promauto.NewCounter(prometheus.CounterOpts{
type pool struct { type pool struct {
mtx sync.RWMutex mtx sync.RWMutex
pool map[string]*entry pool map[unique.Handle[string]]*entry
} }
type entry struct { type entry struct {
refs atomic.Int64 refs atomic.Int64
s string
} }
func newEntry(s string) *entry { func newEntry() *entry {
return &entry{s: s} return &entry{}
} }
func newPool() *pool { func newPool() *pool {
return &pool{ return &pool{
pool: map[string]*entry{}, pool: map[unique.Handle[string]]*entry{},
} }
} }
@ -60,27 +59,28 @@ func (p *pool) intern(s string) string {
} }
p.mtx.RLock() p.mtx.RLock()
interned, ok := p.pool[s] h := unique.Make(s)
interned, ok := p.pool[h]
p.mtx.RUnlock() p.mtx.RUnlock()
if ok { if ok {
interned.refs.Inc() interned.refs.Inc()
return interned.s return s
} }
p.mtx.Lock() p.mtx.Lock()
defer p.mtx.Unlock() defer p.mtx.Unlock()
if interned, ok := p.pool[s]; ok { if interned, ok := p.pool[h]; ok {
interned.refs.Inc() interned.refs.Inc()
return interned.s return s
} }
p.pool[h] = newEntry()
p.pool[s] = newEntry(s) p.pool[h].refs.Store(1)
p.pool[s].refs.Store(1)
return s return s
} }
func (p *pool) release(s string) { func (p *pool) release(s string) {
p.mtx.RLock() p.mtx.RLock()
interned, ok := p.pool[s] h := unique.Make(s)
interned, ok := p.pool[h]
p.mtx.RUnlock() p.mtx.RUnlock()
if !ok { if !ok {
@ -98,5 +98,5 @@ func (p *pool) release(s string) {
if interned.refs.Load() != 0 { if interned.refs.Load() != 0 {
return return
} }
delete(p.pool, s) delete(p.pool, h)
} }

View file

@ -22,6 +22,7 @@ import (
"fmt" "fmt"
"testing" "testing"
"time" "time"
"unique"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -30,7 +31,7 @@ func TestIntern(t *testing.T) {
interner := newPool() interner := newPool()
testString := "TestIntern" testString := "TestIntern"
interner.intern(testString) interner.intern(testString)
interned, ok := interner.pool[testString] interned, ok := interner.pool[unique.Make(testString)]
require.True(t, ok) require.True(t, ok)
require.Equal(t, int64(1), interned.refs.Load(), fmt.Sprintf("expected refs to be 1 but it was %d", interned.refs.Load())) require.Equal(t, int64(1), interned.refs.Load(), fmt.Sprintf("expected refs to be 1 but it was %d", interned.refs.Load()))
@ -41,13 +42,13 @@ func TestIntern_MultiRef(t *testing.T) {
testString := "TestIntern_MultiRef" testString := "TestIntern_MultiRef"
interner.intern(testString) interner.intern(testString)
interned, ok := interner.pool[testString] interned, ok := interner.pool[unique.Make(testString)]
require.True(t, ok) require.True(t, ok)
require.Equal(t, int64(1), interned.refs.Load(), fmt.Sprintf("expected refs to be 1 but it was %d", interned.refs.Load())) require.Equal(t, int64(1), interned.refs.Load(), fmt.Sprintf("expected refs to be 1 but it was %d", interned.refs.Load()))
interner.intern(testString) interner.intern(testString)
interned, ok = interner.pool[testString] interned, ok = interner.pool[unique.Make(testString)]
require.True(t, ok) require.True(t, ok)
require.Equal(t, int64(2), interned.refs.Load(), fmt.Sprintf("expected refs to be 2 but it was %d", interned.refs.Load())) require.Equal(t, int64(2), interned.refs.Load(), fmt.Sprintf("expected refs to be 2 but it was %d", interned.refs.Load()))
@ -58,13 +59,13 @@ func TestIntern_DeleteRef(t *testing.T) {
testString := "TestIntern_DeleteRef" testString := "TestIntern_DeleteRef"
interner.intern(testString) interner.intern(testString)
interned, ok := interner.pool[testString] interned, ok := interner.pool[unique.Make(testString)]
require.True(t, ok) require.True(t, ok)
require.Equal(t, int64(1), interned.refs.Load(), fmt.Sprintf("expected refs to be 1 but it was %d", interned.refs.Load())) require.Equal(t, int64(1), interned.refs.Load(), fmt.Sprintf("expected refs to be 1 but it was %d", interned.refs.Load()))
interner.release(testString) interner.release(testString)
_, ok = interner.pool[testString] _, ok = interner.pool[unique.Make(testString)]
require.False(t, ok) require.False(t, ok)
} }
@ -73,7 +74,7 @@ func TestIntern_MultiRef_Concurrent(t *testing.T) {
testString := "TestIntern_MultiRef_Concurrent" testString := "TestIntern_MultiRef_Concurrent"
interner.intern(testString) interner.intern(testString)
interned, ok := interner.pool[testString] interned, ok := interner.pool[unique.Make(testString)]
require.True(t, ok) require.True(t, ok)
require.Equal(t, int64(1), interned.refs.Load(), fmt.Sprintf("expected refs to be 1 but it was %d", interned.refs.Load())) require.Equal(t, int64(1), interned.refs.Load(), fmt.Sprintf("expected refs to be 1 but it was %d", interned.refs.Load()))
@ -84,7 +85,7 @@ func TestIntern_MultiRef_Concurrent(t *testing.T) {
time.Sleep(time.Millisecond) time.Sleep(time.Millisecond)
interner.mtx.RLock() interner.mtx.RLock()
interned, ok = interner.pool[testString] interned, ok = interner.pool[unique.Make(testString)]
interner.mtx.RUnlock() interner.mtx.RUnlock()
require.True(t, ok) require.True(t, ok)
require.Equal(t, int64(1), interned.refs.Load(), fmt.Sprintf("expected refs to be 1 but it was %d", interned.refs.Load())) require.Equal(t, int64(1), interned.refs.Load(), fmt.Sprintf("expected refs to be 1 but it was %d", interned.refs.Load()))