Skip to content

Commit d4c4940

Browse files
authored
Merge pull request #454 from google/store-expiration-performance
Switch the store RWMutex to a pair of categorical mutices.
2 parents 3538ca0 + cc69d15 commit d4c4940

File tree

7 files changed

+33
-24
lines changed

7 files changed

+33
-24
lines changed

internal/exporter/export.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,8 @@ func formatLabels(name string, m map[string]string, ksep, sep, rep string) strin
156156
type formatter func(string, *metrics.Metric, *metrics.LabelSet, time.Duration) string
157157

158158
func (e *Exporter) writeSocketMetrics(c io.Writer, f formatter, exportTotal *expvar.Int, exportSuccess *expvar.Int) error {
159-
e.store.RLock()
160-
defer e.store.RUnlock()
159+
e.store.SearchMu.RLock()
160+
defer e.store.SearchMu.RUnlock()
161161

162162
for _, ml := range e.store.Metrics {
163163
for _, m := range ml {

internal/exporter/prometheus.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ func (e *Exporter) Describe(c chan<- *prometheus.Desc) {
3030

3131
// Collect implements the prometheus.Collector interface.
3232
func (e *Exporter) Collect(c chan<- prometheus.Metric) {
33-
e.store.RLock()
34-
defer e.store.RUnlock()
33+
e.store.SearchMu.RLock()
34+
defer e.store.SearchMu.RUnlock()
3535

3636
for _, ml := range e.store.Metrics {
3737
lastSource := ""

internal/exporter/varz.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ const varzFormat = "%s{%s} %s\n"
2121

2222
// HandleVarz exports the metrics in Varz format via HTTP.
2323
func (e *Exporter) HandleVarz(w http.ResponseWriter, r *http.Request) {
24-
e.store.RLock()
25-
defer e.store.RUnlock()
24+
e.store.SearchMu.RLock()
25+
defer e.store.SearchMu.RUnlock()
2626

2727
w.Header().Add("Content-type", "text/plain")
2828

internal/metrics/store.go

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ import (
1717

1818
// Store contains Metrics.
1919
type Store struct {
20-
sync.RWMutex
21-
Metrics map[string][]*Metric
20+
SearchMu sync.RWMutex // read for iterate and insert, write for delete
21+
InsertMu sync.Mutex // locked for insert and delete, unlocked for iterate
22+
Metrics map[string][]*Metric
2223
}
2324

2425
// NewStore returns a new metric Store.
@@ -30,13 +31,15 @@ func NewStore() (s *Store) {
3031

3132
// Add is used to add one metric to the Store.
3233
func (s *Store) Add(m *Metric) error {
33-
s.Lock()
34-
defer s.Unlock()
34+
s.InsertMu.Lock()
35+
defer s.InsertMu.Unlock()
36+
s.SearchMu.RLock()
3537
glog.V(1).Infof("Adding a new metric %v", m)
3638
dupeIndex := -1
3739
if len(s.Metrics[m.Name]) > 0 {
3840
t := s.Metrics[m.Name][0].Kind
3941
if m.Kind != t {
42+
s.SearchMu.RUnlock()
4043
return errors.Errorf("Metric %s has different kind %v to existing %v.", m.Name, m.Kind, t)
4144
}
4245

@@ -77,25 +80,31 @@ func (s *Store) Add(m *Metric) error {
7780
}
7881
}
7982
}
83+
s.SearchMu.RUnlock()
8084

85+
// We're in modify mode now so lock out search
86+
s.SearchMu.Lock()
8187
s.Metrics[m.Name] = append(s.Metrics[m.Name], m)
8288
if dupeIndex >= 0 {
8389
s.Metrics[m.Name] = append(s.Metrics[m.Name][0:dupeIndex], s.Metrics[m.Name][dupeIndex+1:]...)
8490
}
91+
s.SearchMu.Unlock()
8592
return nil
8693
}
8794

8895
// ClearMetrics empties the store of all metrics.
8996
func (s *Store) ClearMetrics() {
90-
s.Lock()
91-
defer s.Unlock()
97+
s.InsertMu.Lock()
98+
defer s.InsertMu.Unlock()
99+
s.SearchMu.Lock()
100+
defer s.SearchMu.Unlock()
92101
s.Metrics = make(map[string][]*Metric)
93102
}
94103

95104
// MarshalJSON returns a JSON byte string representing the Store.
96105
func (s *Store) MarshalJSON() (b []byte, err error) {
97-
s.Lock()
98-
defer s.Unlock()
106+
s.SearchMu.RLock()
107+
defer s.SearchMu.RUnlock()
99108
ms := make([]*Metric, 0)
100109
for _, ml := range s.Metrics {
101110
ms = append(ms, ml...)
@@ -107,8 +116,8 @@ func (s *Store) MarshalJSON() (b []byte, err error) {
107116
// for expiry, and removing them if their expiration time has passed.
108117
func (s *Store) Gc() error {
109118
glog.Info("Running Store.Expire()")
110-
s.Lock()
111-
defer s.Unlock()
119+
s.SearchMu.RLock()
120+
defer s.SearchMu.RUnlock()
112121
now := time.Now()
113122
for _, ml := range s.Metrics {
114123
for _, m := range ml {
@@ -154,9 +163,9 @@ func (s *Store) StartGcLoop(ctx context.Context, duration time.Duration) {
154163
// WriteMetrics dumps the current state of the metrics store in JSON format to
155164
// the io.Writer.
156165
func (s *Store) WriteMetrics(w io.Writer) error {
157-
s.RLock()
166+
s.SearchMu.RLock()
158167
b, err := json.MarshalIndent(s.Metrics, "", " ")
159-
s.RUnlock()
168+
s.SearchMu.RUnlock()
160169
if err != nil {
161170
return errors.Wrap(err, "failed to marshal metrics into json")
162171
}

internal/metrics/store_bench_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,10 @@ func BenchmarkStore(b *testing.B) {
7575
addToStore(b, items, *m, s)
7676
},
7777
b: func(b *testing.B, items int, m []*Metric, s *Store) {
78-
s.RLock()
78+
s.SearchMu.RLock()
7979
for _ = range s.Metrics {
8080
}
81-
s.RUnlock()
81+
s.SearchMu.RUnlock()
8282
},
8383
},
8484
}

internal/mtail/examples_integration_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ func TestExamplePrograms(t *testing.T) {
178178
goldenStore := metrics.NewStore()
179179
golden.ReadTestData(g, tc.programfile, goldenStore)
180180

181-
testutil.ExpectNoDiff(t, goldenStore, store, testutil.IgnoreUnexported(sync.RWMutex{}, datum.String{}))
181+
testutil.ExpectNoDiff(t, goldenStore, store, testutil.IgnoreUnexported(sync.RWMutex{}, sync.Mutex{}, datum.String{}))
182182
})
183183
}
184184
}
@@ -301,7 +301,7 @@ func TestFilePipeStreamComparison(t *testing.T) {
301301
cancel()
302302

303303
// Ignore the usual field and the datum.Time field as well, as the results will be unstable otherwise.
304-
testutil.ExpectNoDiff(t, fileStore, pipeStore, testutil.IgnoreUnexported(sync.RWMutex{}, datum.String{}), testutil.IgnoreFields(datum.BaseDatum{}, "Time"))
304+
testutil.ExpectNoDiff(t, fileStore, pipeStore, testutil.IgnoreUnexported(sync.RWMutex{}, sync.Mutex{}, datum.String{}), testutil.IgnoreFields(datum.BaseDatum{}, "Time"))
305305
})
306306
}
307307
}

internal/mtail/golden/reader.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ var varRe = regexp.MustCompile(`^(counter|gauge|timer|text|histogram) ([^ ]+)(?:
2121

2222
// FindMetricOrNil returns a metric in a store, or returns nil if not found.
2323
func FindMetricOrNil(store *metrics.Store, name string) *metrics.Metric {
24-
store.RLock()
25-
defer store.RUnlock()
24+
store.SearchMu.RLock()
25+
defer store.SearchMu.RUnlock()
2626
for n, ml := range store.Metrics {
2727
if n == name {
2828
return ml[0]

0 commit comments

Comments
 (0)