compactor: avoid memory blow-up with stringlabels

When compiled with `-tags stringlabels`, the names and values point into
a larger block of memory containing all labels. Garbage-collection
considers the entire block "live" if you point to a part of it, so the
map ends up retaining all labels for (nearly) all series.

Cloning the string value avoids this problem, and we check first if the
value is already in the map. Since the clone is more expensive, only do
it when built with `-tags stringlabels`.

Signed-off-by: Bryan Boreham <bjboreham@gmail.com>
This commit is contained in:
Bryan Boreham 2023-07-29 10:12:09 +01:00
parent c8f5ad1492
commit 4ced6d5f40
3 changed files with 25 additions and 5 deletions

10
tsdb/addsymbol.go Normal file
View file

@ -0,0 +1,10 @@
//go:build !stringlabels
// Split out function which needs to be coded differently for stringlabels case.
package tsdb
func (sw *symbolsBatcher) addSymbol(sym string) error {
sw.buffer[sym] = struct{}{}
return sw.flushSymbols(false)
}

View file

@ -0,0 +1,15 @@
//go:build stringlabels
// Split out function which needs to be coded differently for stringlabels case.
package tsdb
import "strings"
func (sw *symbolsBatcher) addSymbol(sym string) error {
if _, found := sw.buffer[sym]; !found {
sym = strings.Clone(sym) // So we don't retain reference to the entire labels block.
sw.buffer[sym] = struct{}{}
}
return sw.flushSymbols(false)
}

View file

@ -145,11 +145,6 @@ func newSymbolsBatcher(limit int, dir string, flushers *symbolFlushers) *symbols
} }
} }
func (sw *symbolsBatcher) addSymbol(sym string) error {
sw.buffer[sym] = struct{}{}
return sw.flushSymbols(false)
}
func (sw *symbolsBatcher) flushSymbols(force bool) error { func (sw *symbolsBatcher) flushSymbols(force bool) error {
if !force && len(sw.buffer) < sw.limit { if !force && len(sw.buffer) < sw.limit {
return nil return nil