diff --git a/storage/remote/codec.go b/storage/remote/codec.go index a3767fddb..630fddda4 100644 --- a/storage/remote/codec.go +++ b/storage/remote/codec.go @@ -363,32 +363,12 @@ func labelProtosToLabels(labelPairs []prompb.Label) labels.Labels { return result } -func labelsetToLabelsProto(ls model.LabelSet) []prompb.Label { - result := make([]prompb.Label, 0, len(ls)) - keys := make([]string, 0, len(ls)) - - for k := range ls { - keys = append(keys, string(k)) - } - sort.Strings(keys) - - for _, k := range keys { - ln := model.LabelName(k) - result = append(result, prompb.Label{ - Name: k, - Value: string(ls[ln]), - }) - } - - return result -} - func labelsToLabelsProto(labels labels.Labels) []prompb.Label { result := make([]prompb.Label, 0, len(labels)) for _, l := range labels { result = append(result, prompb.Label{ - Name: iterner.intern(l.Name), - Value: iterner.intern(l.Value), + Name: interner.intern(l.Name), + Value: interner.intern(l.Value), }) } return result diff --git a/storage/remote/intern.go b/storage/remote/intern.go new file mode 100644 index 000000000..8dfc37fc4 --- /dev/null +++ b/storage/remote/intern.go @@ -0,0 +1,65 @@ +// Copyright 2019 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Inspired / copied / modified from https://gitlab.com/cznic/strutil/blob/master/strutil.go, +// which is MIT licensed, so: +// +// Copyright (c) 2014 The strutil Authors. All rights reserved. + +package remote + +import "sync" + +var interner = newPool() + +type pool struct { + mtx sync.RWMutex + pool map[string]string +} + +func newPool() *pool { + return &pool{ + pool: map[string]string{}, + } +} + +func (p *pool) intern(s string) string { + if s == "" { + return "" + } + + p.mtx.RLock() + interned, ok := p.pool[s] + p.mtx.RUnlock() + if ok { + return interned + } + + p.mtx.Lock() + defer p.mtx.Unlock() + if interned, ok := p.pool[s]; ok { + return interned + } + + s = pack(s) + p.pool[s] = s + return s +} + +// StrPack returns a new instance of s which is tightly packed in memory. +// It is intended for avoiding the situation where having a live reference +// to a string slice over an unreferenced biger underlying string keeps the biger one +// in memory anyway - it can't be GCed. +func pack(s string) string { + return string([]byte(s)) +}