From 87d909df4a061b8b0c10ad2cc0d76b99f09e37e0 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Wed, 8 Sep 2021 10:18:48 +0100 Subject: [PATCH] Remove symbols map from TSDB head (#9301) This saves memory, effort and locking. Since every symbol is also added to postings, `Symbols()` can be implemented there instead. This now has to build a map for deduplication, but `Symbols()` is only called for compaction, and `gc()` used to rebuild the symbols map after every compaction so not an additional cost. Signed-off-by: Bryan Boreham --- tsdb/head.go | 28 ---------------------------- tsdb/head_read.go | 16 +--------------- tsdb/head_test.go | 15 ++++++++------- tsdb/index/postings.go | 23 +++++++++++++++++++++++ 4 files changed, 32 insertions(+), 50 deletions(-) diff --git a/tsdb/head.go b/tsdb/head.go index 4bbf139cc..8d35f99b8 100644 --- a/tsdb/head.go +++ b/tsdb/head.go @@ -79,9 +79,6 @@ type Head struct { // All series addressable by their ID or hash. series *stripeSeries - symMtx sync.RWMutex - symbols map[string]struct{} - deletedMtx sync.Mutex deleted map[uint64]int // Deleted series, and what WAL segment they must be kept until. @@ -223,7 +220,6 @@ func (h *Head) resetInMemoryState() error { h.exemplarMetrics = em h.exemplars = es h.series = newStripeSeries(h.opts.StripeSize, h.opts.SeriesCallback) - h.symbols = map[string]struct{}{} h.postings = index.NewUnorderedMemPostings() h.tombstones = tombstones.NewMemTombstones() h.iso = newIsolation() @@ -1120,22 +1116,6 @@ func (h *Head) gc() int64 { h.deletedMtx.Unlock() } - // Rebuild symbols and label value indices from what is left in the postings terms. - // symMtx ensures that append of symbols and postings is disabled for rebuild time. - h.symMtx.Lock() - defer h.symMtx.Unlock() - - symbols := make(map[string]struct{}, len(h.symbols)) - if err := h.postings.Iter(func(l labels.Label, _ index.Postings) error { - symbols[l.Name] = struct{}{} - symbols[l.Value] = struct{}{} - return nil - }); err != nil { - // This should never happen, as the iteration function only returns nil. - panic(err) - } - h.symbols = symbols - return actualMint } @@ -1234,14 +1214,6 @@ func (h *Head) getOrCreateWithID(id, hash uint64, lset labels.Labels) (*memSerie h.metrics.seriesCreated.Inc() h.numSeries.Inc() - h.symMtx.Lock() - defer h.symMtx.Unlock() - - for _, l := range lset { - h.symbols[l.Name] = struct{}{} - h.symbols[l.Value] = struct{}{} - } - h.postings.Add(id, lset) return s, true, nil } diff --git a/tsdb/head_read.go b/tsdb/head_read.go index 9e43265ba..a337b6948 100644 --- a/tsdb/head_read.go +++ b/tsdb/head_read.go @@ -54,16 +54,7 @@ func (h *headIndexReader) Close() error { } func (h *headIndexReader) Symbols() index.StringIter { - h.head.symMtx.RLock() - res := make([]string, 0, len(h.head.symbols)) - - for s := range h.head.symbols { - res = append(res, s) - } - h.head.symMtx.RUnlock() - - sort.Strings(res) - return index.NewStringListIter(res) + return h.head.postings.Symbols() } // SortedLabelValues returns label values present in the head for the @@ -88,8 +79,6 @@ func (h *headIndexReader) LabelValues(name string, matchers ...*labels.Matcher) } if len(matchers) == 0 { - h.head.symMtx.RLock() - defer h.head.symMtx.RUnlock() return h.head.postings.LabelValues(name), nil } @@ -104,10 +93,7 @@ func (h *headIndexReader) LabelNames(matchers ...*labels.Matcher) ([]string, err } if len(matchers) == 0 { - h.head.symMtx.RLock() labelNames := h.head.postings.LabelNames() - h.head.symMtx.RUnlock() - sort.Strings(labelNames) return labelNames, nil } diff --git a/tsdb/head_test.go b/tsdb/head_test.go index eaaac4166..b3b113185 100644 --- a/tsdb/head_test.go +++ b/tsdb/head_test.go @@ -469,13 +469,14 @@ func TestHead_Truncate(t *testing.T) { require.Nil(t, postingsB2) require.Nil(t, postingsC1) - require.Equal(t, map[string]struct{}{ - "": {}, // from 'all' postings list - "a": {}, - "b": {}, - "1": {}, - "2": {}, - }, h.symbols) + iter := h.postings.Symbols() + symbols := []string{} + for iter.Next() { + symbols = append(symbols, iter.At()) + } + require.Equal(t, + []string{"" /* from 'all' postings list */, "1", "2", "a", "b"}, + symbols) values := map[string]map[string]struct{}{} for _, name := range h.postings.LabelNames() { diff --git a/tsdb/index/postings.go b/tsdb/index/postings.go index a9048e4c6..c63cff713 100644 --- a/tsdb/index/postings.go +++ b/tsdb/index/postings.go @@ -58,6 +58,29 @@ func NewUnorderedMemPostings() *MemPostings { } } +// Symbols returns an iterator over all unique name and value strings, in order. +func (p *MemPostings) Symbols() StringIter { + p.mtx.RLock() + + // Add all the strings to a map to de-duplicate. + symbols := make(map[string]struct{}, 512) + for n, e := range p.m { + symbols[n] = struct{}{} + for v := range e { + symbols[v] = struct{}{} + } + } + p.mtx.RUnlock() + + res := make([]string, 0, len(symbols)) + for k := range symbols { + res = append(res, k) + } + + sort.Strings(res) + return NewStringListIter(res) +} + // SortedKeys returns a list of sorted label keys of the postings. func (p *MemPostings) SortedKeys() []labels.Label { p.mtx.RLock()