From 41456d9569b75d32f5e531dc711b0df44ed340fa Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin <valyala@victoriametrics.com> Date: Tue, 23 Jan 2024 14:04:45 +0200 Subject: [PATCH] app/vmselect/netstorage: limit the maximum brsPool size to 32Kb at ProcessSearchQuery() This avoids slow path in Go runtime for allocating objects bigger than 32Kb - see https://github.com/golang/go/blob/704401ffa06c60e059c9e6e4048045b4ff42530a/src/runtime/malloc.go#L11 This also reduces memory usage a bit for vmselect and single-node VictoriaMetrics after the commit 5dd37ad836ef1381ce7d31e9efdd68d7aae984b5 . Updates https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5527 --- app/vmselect/netstorage/netstorage.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/vmselect/netstorage/netstorage.go b/app/vmselect/netstorage/netstorage.go index e0e424ea10..62d1a0db98 100644 --- a/app/vmselect/netstorage/netstorage.go +++ b/app/vmselect/netstorage/netstorage.go @@ -1188,6 +1188,7 @@ func ProcessSearchQuery(qt *querytracer.Tracer, sq *storage.SearchQuery, deadlin return nil, fmt.Errorf("cannot select more than -search.maxSamplesPerQuery=%d samples; possible solutions: to increase the -search.maxSamplesPerQuery; "+ "to reduce time range for the query; to use more specific label filters in order to select lower number of series", *maxSamplesPerQuery) } + buf = br.Marshal(buf[:0]) addr, err := tbf.WriteBlockRefData(buf) if err != nil { @@ -1195,6 +1196,7 @@ func ProcessSearchQuery(qt *querytracer.Tracer, sq *storage.SearchQuery, deadlin putStorageSearch(sr) return nil, fmt.Errorf("cannot write %d bytes to temporary file: %w", len(buf), err) } + metricName := sr.MetricBlockRef.MetricName if metricNamePrev == nil || string(metricName) != string(metricNamePrev) { idx, ok := m[string(metricName)] @@ -1209,8 +1211,14 @@ func ProcessSearchQuery(qt *querytracer.Tracer, sq *storage.SearchQuery, deadlin brsIdx = idx metricNamePrev = append(metricNamePrev[:0], metricName...) } + brs := &brssPool[brsIdx] partRef := br.PartRef() + if uintptr(cap(brsPool)) >= maxFastAllocBlockSize/unsafe.Sizeof(blockRef{}) && len(brsPool) == cap(brsPool) { + // Allocate a new brsPool in order to avoid slow allocation of an object + // bigger than maxFastAllocBlockSize bytes at append() below. + brsPool = make([]blockRef, 0, maxFastAllocBlockSize/unsafe.Sizeof(blockRef{})) + } if brs.brs == nil || haveSameBlockRefTails(brs.brs, brsPool) { // It is safe appending blockRef to brsPool, since there are no other items added there yet. brsPool = append(brsPool, blockRef{ @@ -1334,4 +1342,4 @@ func applyGraphiteRegexpFilter(filter string, ss []string) ([]string, error) { // Go uses fast allocations for block sizes up to 32Kb. // // See https://github.com/golang/go/blob/704401ffa06c60e059c9e6e4048045b4ff42530a/src/runtime/malloc.go#L11 -const maxFastAllocBlockSize = 32*1024 +const maxFastAllocBlockSize = 32 * 1024